@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,21 @@
1
+ type UnSubscribeFn = () => void;
2
+ type SubscribeFn = (handler: () => void) => UnSubscribeFn;
3
+
4
+ interface Registry {
5
+ [dependencyName: string]: SubscribeFn;
6
+ }
7
+
8
+ const registry: Registry = {};
9
+
10
+ export function addDependency(name: string, onChange: SubscribeFn): void {
11
+ if (!registry[name]) {
12
+ Object.defineProperty(registry, name, {
13
+ value: onChange,
14
+ writable: false,
15
+ });
16
+ }
17
+ }
18
+
19
+ export function getDependency(name: string): SubscribeFn | undefined {
20
+ return registry[name];
21
+ }
@@ -0,0 +1,31 @@
1
+ interface UsingSpec {
2
+ [dependencyName: string]: boolean;
3
+ }
4
+
5
+ interface Ref {
6
+ current: UsingSpec;
7
+ }
8
+
9
+ const ref: Ref = { current: {} };
10
+
11
+ export function resetUsing(): void {
12
+ ref.current = {};
13
+ }
14
+
15
+ export function getUsing(): UsingSpec {
16
+ return { ...ref.current };
17
+ }
18
+
19
+ export function onUse(name: string): void {
20
+ ref.current[name] = true;
21
+ }
22
+
23
+ interface Button {
24
+ variant: "primary" | "danger"
25
+ onAction: () => void
26
+ }
27
+
28
+ // onAction -> onClick
29
+ // onAction -> onPress
30
+
31
+
@@ -0,0 +1,2 @@
1
+ export const WINDOW_DEPENDENCY_KEY = "Dimensions.window";
2
+ export const SCREEN_DEPENDENCY_KEY = "Dimensions.screen";
@@ -0,0 +1,20 @@
1
+ import "./init";
2
+
3
+ import { Dimensions, ScaledSize } from "react-native";
4
+
5
+ import { onUse } from "../dependencyUsage";
6
+ import { WINDOW_DEPENDENCY_KEY, SCREEN_DEPENDENCY_KEY } from "./consts";
7
+
8
+ const get = (dim: "window" | "screen") => Dimensions.get(dim);
9
+
10
+ export function getWindowDimensions(): ScaledSize {
11
+ onUse(WINDOW_DEPENDENCY_KEY);
12
+
13
+ return get("window");
14
+ }
15
+
16
+ export function getScreenDimensions(): ScaledSize {
17
+ onUse(SCREEN_DEPENDENCY_KEY);
18
+
19
+ return get("screen");
20
+ }
@@ -0,0 +1,37 @@
1
+ import { Dimensions, ScaledSize } from "react-native";
2
+
3
+ import { createEventEmitter } from "../createEventEmitter";
4
+ import { addDependency } from "../dependencyRegistry";
5
+ import { SCREEN_DEPENDENCY_KEY, WINDOW_DEPENDENCY_KEY } from "./consts";
6
+
7
+ const { get, addEventListener } = Dimensions;
8
+
9
+ const state = { window: get("window"), screen: get("screen") };
10
+
11
+ const windowEventEmitter = createEventEmitter(WINDOW_DEPENDENCY_KEY);
12
+ const screenEventEmitter = createEventEmitter(SCREEN_DEPENDENCY_KEY);
13
+
14
+ const isNotEqual = (a: ScaledSize, b: ScaledSize) =>
15
+ a.width !== b.width || a.height !== b.height;
16
+
17
+ addDependency(WINDOW_DEPENDENCY_KEY, (handler: () => void) =>
18
+ windowEventEmitter.on(handler)
19
+ );
20
+
21
+ addDependency(SCREEN_DEPENDENCY_KEY, (handler: () => void) =>
22
+ screenEventEmitter.on(handler)
23
+ );
24
+
25
+ addEventListener("change", ({ window, screen }) => {
26
+ if (isNotEqual(screen, state.screen)) {
27
+ state.screen = screen;
28
+
29
+ screenEventEmitter.emit();
30
+ }
31
+
32
+ if (isNotEqual(window, state.window)) {
33
+ state.window = window;
34
+
35
+ windowEventEmitter.emit();
36
+ }
37
+ });
@@ -0,0 +1,11 @@
1
+ import { SCREEN_DEPENDENCY_KEY, WINDOW_DEPENDENCY_KEY } from "./consts";
2
+
3
+ export const optimizeDependencies = (
4
+ keys: Record<string, boolean>
5
+ ): Record<string, boolean> => {
6
+ if (keys[WINDOW_DEPENDENCY_KEY] && keys[SCREEN_DEPENDENCY_KEY]) {
7
+ delete keys[SCREEN_DEPENDENCY_KEY];
8
+ }
9
+
10
+ return keys;
11
+ };
package/src/i18n.ts ADDED
@@ -0,0 +1,18 @@
1
+ import { I18nManager } from "react-native";
2
+
3
+ type LayoutDirectionType = "rtl" | "ltr";
4
+
5
+ export function i18n<T>({
6
+ rtl,
7
+ ltr,
8
+ }: { [direction in LayoutDirectionType]?: T }): T | undefined {
9
+ if (I18nManager.isRTL) {
10
+ return rtl;
11
+ }
12
+
13
+ return ltr;
14
+ }
15
+
16
+ export const rtl = <T>(styles: T): T | undefined => i18n<T>({ rtl: styles });
17
+
18
+ export const ltr = <T>(styles: T): T | undefined => i18n<T>({ ltr: styles });
package/src/index.ts ADDED
@@ -0,0 +1,7 @@
1
+ export { useTheme } from "./useTheme";
2
+ export { withStyles } from "./withStyles";
3
+ export { ThemeProvider, ThemeConsumer } from "./context";
4
+ export { useColorTransition } from "./useColorTransition";
5
+ export { makeUseStyles } from "./makeUseStyles";
6
+ export type { DefaultTheme } from "./DefaultTheme";
7
+ export type { InferInjectedStyledProps } from "./withStyles";
@@ -0,0 +1,42 @@
1
+ import { StyleSheet } from "react-native";
2
+ import { useTheme } from "../useTheme";
3
+ import { resetUsing } from "../dependencyUsage";
4
+ import { getDependenciesKeys, useForceUpdate, subscribe } from "./utils";
5
+
6
+ export function createUseStylesTheme(getStyles) {
7
+ const scope = {
8
+ styles: new WeakMap(),
9
+ forceUpdate: [],
10
+ unsubscribe: () => {},
11
+ };
12
+
13
+ function onUpdate() {
14
+ // Clear all styles to recreate them after dependencies update
15
+ scope.styles = new WeakMap();
16
+ scope.forceUpdate.forEach((fn) => fn());
17
+ }
18
+
19
+ function initStyle(theme) {
20
+ scope.unsubscribe();
21
+ resetUsing();
22
+
23
+ const style = StyleSheet.create(getStyles(theme));
24
+ const keys = getDependenciesKeys();
25
+
26
+ scope.unsubscribe = subscribe(keys, onUpdate);
27
+
28
+ return style;
29
+ }
30
+
31
+ return function useStyles() {
32
+ const theme = useTheme();
33
+
34
+ useForceUpdate(scope);
35
+
36
+ if (!scope.styles.has(theme)) {
37
+ scope.styles.set(theme, initStyle(theme));
38
+ }
39
+
40
+ return scope.styles.get(theme);
41
+ };
42
+ }
@@ -0,0 +1,137 @@
1
+ import { renderHook, act } from "@testing-library/react-hooks";
2
+ import { createUseStylesTheme } from "./createUseStylesTheme";
3
+ import { ThemeProvider } from "../context";
4
+ import { createEventEmitter } from "../createEventEmitter";
5
+ import { addDependency } from "../dependencyRegistry";
6
+ import { onUse } from "../dependencyUsage";
7
+
8
+ it("should create styles using a theme", () => {
9
+ const mockGetStyles = jest.fn(({ colors }) => ({
10
+ root: { color: colors.red },
11
+ }));
12
+
13
+ const useStyles = createUseStylesTheme(mockGetStyles);
14
+ const { result } = renderHook(() => useStyles(), {
15
+ wrapper: ThemeProvider,
16
+ initialProps: { value: { colors: { red: "red" } } },
17
+ });
18
+
19
+ expect(result.current).toEqual({
20
+ root: { color: "red" },
21
+ });
22
+ });
23
+
24
+ it("should create styles once", () => {
25
+ const theme = { colors: { red: "red" } };
26
+ const mockGetStyles = jest.fn(({ colors }) => ({
27
+ root: { color: colors.red },
28
+ }));
29
+
30
+ const useStyles = createUseStylesTheme(mockGetStyles);
31
+
32
+ renderHook(
33
+ () => {
34
+ useStyles();
35
+ useStyles();
36
+ useStyles();
37
+ },
38
+ {
39
+ wrapper: ThemeProvider,
40
+ initialProps: { value: theme },
41
+ }
42
+ );
43
+
44
+ expect(mockGetStyles).toHaveBeenCalledTimes(1);
45
+ });
46
+
47
+ it("should not update styles when component rerender", () => {
48
+ const theme = { colors: { red: "red" } };
49
+ const mockGetStyles = jest.fn(({ colors }) => ({
50
+ root: { color: colors.red },
51
+ }));
52
+
53
+ const useStyles = createUseStylesTheme(mockGetStyles);
54
+ const { rerender } = renderHook(() => useStyles(), {
55
+ wrapper: ThemeProvider,
56
+ initialProps: { value: theme },
57
+ });
58
+
59
+ rerender({ value: theme, a: 1 });
60
+ rerender({ value: theme, a: 2 });
61
+ rerender({ value: theme, a: 3 });
62
+
63
+ expect(mockGetStyles).toHaveBeenCalledTimes(1);
64
+ });
65
+
66
+ it("should use memoized styles when component rerender", () => {
67
+ const theme = { colors: { red: "red" } };
68
+ const mockGetStyles = jest.fn(({ colors }) => ({
69
+ root: { color: colors.red },
70
+ }));
71
+
72
+ const useStyles = createUseStylesTheme(mockGetStyles);
73
+ const { rerender, result } = renderHook(() => useStyles(), {
74
+ wrapper: ThemeProvider,
75
+ initialProps: { value: theme },
76
+ });
77
+
78
+ const initialStyles = result.current;
79
+
80
+ rerender({ value: theme, a: 1 });
81
+ rerender({ value: theme, a: 2 });
82
+ rerender({ value: theme, a: 3 });
83
+
84
+ const afterUpdateStyles = result.current;
85
+
86
+ expect(afterUpdateStyles).toBe(initialStyles);
87
+ });
88
+
89
+ it("should update styles when theme was changed", () => {
90
+ const initialTheme = { colors: { red: "red" } };
91
+ const newTheme = { colors: { red: "white" } };
92
+
93
+ const mockGetStyles = jest.fn(({ colors }) => ({
94
+ root: { color: colors.red },
95
+ }));
96
+
97
+ const useStyles = createUseStylesTheme(mockGetStyles);
98
+ const { rerender } = renderHook(() => useStyles(), {
99
+ wrapper: ThemeProvider,
100
+ initialProps: { value: initialTheme },
101
+ });
102
+
103
+ act(() => {
104
+ rerender({ value: newTheme });
105
+ });
106
+
107
+ expect(mockGetStyles).toHaveBeenCalledTimes(2);
108
+ });
109
+
110
+ it("should update styles when dependency changed", () => {
111
+ const theme = { colors: { red: "red" } };
112
+ const TEST_DEPENDENCY_KEY = `test_${Math.random()}`;
113
+ const { on, emit } = createEventEmitter(TEST_DEPENDENCY_KEY);
114
+ const mockGetStyles = jest.fn(({ colors }) => {
115
+ // simulate using a dependency
116
+ onUse(TEST_DEPENDENCY_KEY);
117
+
118
+ return {
119
+ root: { color: colors.red },
120
+ };
121
+ });
122
+
123
+ addDependency(TEST_DEPENDENCY_KEY, (handler) => on(handler));
124
+
125
+ const useStyles = createUseStylesTheme(mockGetStyles);
126
+
127
+ renderHook(() => useStyles(), {
128
+ wrapper: ThemeProvider,
129
+ initialProps: { value: theme },
130
+ });
131
+
132
+ act(() => {
133
+ emit();
134
+ });
135
+
136
+ expect(mockGetStyles).toHaveBeenCalledTimes(2);
137
+ });
@@ -0,0 +1,38 @@
1
+ import { StyleSheet } from "react-native";
2
+ import { resetUsing } from "../dependencyUsage";
3
+ import { getDependenciesKeys, subscribe, useForceUpdate } from "./utils";
4
+
5
+ export function createUseStylesWithoutTheme(getStyles) {
6
+ const scope = {
7
+ style: null,
8
+ forceUpdate: [],
9
+ unsubscribe: () => {},
10
+ };
11
+
12
+ function onUpdate() {
13
+ // Recreate style after dependencies update
14
+ initStyle();
15
+ scope.forceUpdate.forEach((fn) => fn());
16
+ }
17
+
18
+ function initStyle() {
19
+ scope.unsubscribe();
20
+ resetUsing();
21
+
22
+ scope.style = StyleSheet.create(getStyles());
23
+
24
+ const keys = getDependenciesKeys();
25
+
26
+ scope.unsubscribe = subscribe(keys, onUpdate);
27
+ }
28
+
29
+ initStyle();
30
+
31
+ function useStyles() {
32
+ useForceUpdate(scope);
33
+
34
+ return scope.style;
35
+ }
36
+
37
+ return useStyles;
38
+ }
@@ -0,0 +1,63 @@
1
+ import { renderHook, act } from "@testing-library/react-hooks";
2
+ import { createUseStylesWithoutTheme } from "./createUseStylesWithoutTheme";
3
+
4
+ import { addDependency } from "../dependencyRegistry";
5
+ import { onUse } from "../dependencyUsage";
6
+ import { createEventEmitter } from "../createEventEmitter";
7
+
8
+ const TEST_DEPENDENCY_KEY = `test_${Math.random()}`;
9
+
10
+ const { on, emit } = createEventEmitter(TEST_DEPENDENCY_KEY);
11
+
12
+ addDependency(TEST_DEPENDENCY_KEY, (handler) => on(handler));
13
+
14
+ const useStyles = createUseStylesWithoutTheme(() => {
15
+ // simulate using a dependency
16
+ onUse(TEST_DEPENDENCY_KEY);
17
+
18
+ return {
19
+ root: { color: "red" },
20
+ };
21
+ });
22
+
23
+ it("should use the same styles object to reduce memory usage", () => {
24
+ const { result } = renderHook(() => ({
25
+ stl1: useStyles(),
26
+ stl2: useStyles(),
27
+ }));
28
+
29
+ expect(result.current.stl1).toBe(result.current.stl2);
30
+ });
31
+
32
+ it("should update styles when dependency changed", () => {
33
+ const { result } = renderHook(() => ({
34
+ stl1: useStyles(),
35
+ stl2: useStyles(),
36
+ }));
37
+
38
+ const initialResult = { ...result.current };
39
+
40
+ act(() => {
41
+ emit();
42
+ });
43
+
44
+ const updatedResult = { ...result.current };
45
+
46
+ expect(initialResult.stl1).not.toBe(updatedResult.stl1);
47
+ expect(initialResult.stl2).not.toBe(updatedResult.stl2);
48
+ });
49
+
50
+ it("should return the same style when component rerender", () => {
51
+ const mockUseStyles = jest.fn(useStyles);
52
+
53
+ const { result, rerender } = renderHook(() => mockUseStyles());
54
+
55
+ const initialResult = result.current;
56
+
57
+ rerender({});
58
+
59
+ const updatedResult = result.current;
60
+
61
+ expect(mockUseStyles).toHaveBeenCalledTimes(2);
62
+ expect(initialResult).toBe(updatedResult);
63
+ });
@@ -0,0 +1,7 @@
1
+ import { StyleSheet } from "react-native";
2
+ import { DefaultTheme } from "../DefaultTheme";
3
+
4
+ export function makeUseStyles<
5
+ Theme extends DefaultTheme,
6
+ T extends StyleSheet.NamedStyles<T> | StyleSheet.NamedStyles<any>
7
+ >(getStyles: (theme: Theme) => T | StyleSheet.NamedStyles<T>): () => T;
@@ -0,0 +1,12 @@
1
+ import { createUseStylesWithoutTheme } from "./createUseStylesWithoutTheme";
2
+ import { createUseStylesTheme } from "./createUseStylesTheme";
3
+
4
+ export function makeUseStyles(getStyles) {
5
+ const hasThemeDependency = getStyles.length === 1;
6
+
7
+ if (!hasThemeDependency) {
8
+ return createUseStylesWithoutTheme(getStyles);
9
+ }
10
+
11
+ return createUseStylesTheme(getStyles);
12
+ }
@@ -0,0 +1,28 @@
1
+ import { createUseStylesWithoutTheme } from "./createUseStylesWithoutTheme";
2
+ import { createUseStylesTheme } from "./createUseStylesTheme";
3
+ import { makeUseStyles } from "./index";
4
+
5
+ jest.mock("./createUseStylesWithoutTheme");
6
+ jest.mock("./createUseStylesTheme");
7
+
8
+ afterEach(() => {
9
+ jest.clearAllMocks();
10
+ });
11
+
12
+ it("should invoke 'createUseStylesWithoutTheme' when pass function without first argument", () => {
13
+ const withoutThemeDeps = () => ({});
14
+
15
+ makeUseStyles(withoutThemeDeps);
16
+
17
+ expect(createUseStylesWithoutTheme).toHaveBeenCalledTimes(1);
18
+ expect(createUseStylesTheme).toHaveBeenCalledTimes(0);
19
+ });
20
+
21
+ it("should invoke 'createUseStylesTheme' when pass function with first argument", () => {
22
+ const withThemeDeps = (theme) => ({});
23
+
24
+ makeUseStyles(withThemeDeps);
25
+
26
+ expect(createUseStylesWithoutTheme).toHaveBeenCalledTimes(0);
27
+ expect(createUseStylesTheme).toHaveBeenCalledTimes(1);
28
+ });
@@ -0,0 +1,28 @@
1
+ import { makeUseStyles } from "./index";
2
+ {
3
+ const useStyles = makeUseStyles(() => ({
4
+ root: {
5
+ // @ts-expect-error: invalid color value
6
+ color: [],
7
+ },
8
+ }));
9
+ const {
10
+ root,
11
+ // @ts-expect-error: 'otherProps' not exist
12
+ otherProp,
13
+ } = useStyles();
14
+ }
15
+ {
16
+ const useStyles = makeUseStyles((theme) => ({
17
+ root: {
18
+ color: theme.color.red,
19
+ // @ts-expect-error: 'blue' not exist in passed theme
20
+ borderColor: theme.color.blue,
21
+ },
22
+ }));
23
+ const {
24
+ root,
25
+ // @ts-expect-error: 'otherProps' not exist
26
+ otherProp,
27
+ } = useStyles();
28
+ }
@@ -0,0 +1,67 @@
1
+ import { optimizeDependencies } from "../dimensions/utils";
2
+ import { getUsing } from "../dependencyUsage";
3
+ import { getDependency } from "../dependencyRegistry";
4
+
5
+ import { useEffect, useRef, useState } from "react";
6
+
7
+ /* istanbul ignore next */
8
+ const noop = () => {};
9
+
10
+ export const useForceUpdate = (scope) => {
11
+ const unsubscribeRef = useRef(noop);
12
+ const setState = useState(false)[1];
13
+
14
+ if (unsubscribeRef.current === noop) {
15
+ const forceRerender = () => setState((flag) => !flag);
16
+
17
+ scope.forceUpdate = scope.forceUpdate.concat(forceRerender);
18
+
19
+ unsubscribeRef.current = () => {
20
+ scope.forceUpdate = scope.forceUpdate.filter(
21
+ (fn) => fn !== forceRerender
22
+ );
23
+ };
24
+ }
25
+
26
+ useEffect(
27
+ () => () => {
28
+ unsubscribeRef.current();
29
+ unsubscribeRef.current = noop;
30
+ },
31
+ []
32
+ );
33
+ };
34
+
35
+ export const getDependenciesKeys = () =>
36
+ Object.keys(optimizeDependencies(getUsing())).sort();
37
+
38
+ export const subscribe = (dependenciesKeys, handler) => {
39
+ if (dependenciesKeys.length === 0) {
40
+ /* istanbul ignore next */
41
+ return () => {};
42
+ }
43
+
44
+ const unsubscribeFns = dependenciesKeys
45
+ .map(getDependency)
46
+ .filter(
47
+ /* istanbul ignore next */
48
+ process.env.NODE_ENV !== "production"
49
+ ? (onChange, index) => {
50
+ const dependencyName = dependenciesKeys[index];
51
+
52
+ if (!onChange) {
53
+ console.warn(
54
+ `[react-native-stylex] Could not find onChange handler for ${dependencyName}!`
55
+ );
56
+ }
57
+
58
+ return !!onChange;
59
+ }
60
+ : Boolean
61
+ )
62
+ .map((onChange) => onChange(handler));
63
+
64
+ return () => {
65
+ unsubscribeFns.forEach((unsubscribe) => unsubscribe());
66
+ };
67
+ };
@@ -0,0 +1,43 @@
1
+ import { ScaledSize } from "react-native";
2
+
3
+ import { getWindowDimensions } from "../dimensions";
4
+
5
+ export const createDimensionQueryHelper = <Value>(
6
+ queryFunction: (options: { value: Value; dimensions: ScaledSize }) => boolean
7
+ ) => <T>(value: Value, styles: T): null | T => {
8
+ const isMatched = queryFunction({ value, dimensions: getWindowDimensions() });
9
+
10
+ if (isMatched) {
11
+ return styles;
12
+ }
13
+
14
+ return null;
15
+ };
16
+
17
+ export const maxHeight = createDimensionQueryHelper<number>(
18
+ ({ value, dimensions }) => value >= dimensions.height
19
+ );
20
+
21
+ export const maxWidth = createDimensionQueryHelper<number>(
22
+ ({ value, dimensions }) => value >= dimensions.width
23
+ );
24
+
25
+ export const minHeight = createDimensionQueryHelper<number>(
26
+ ({ value, dimensions }) => value <= dimensions.height
27
+ );
28
+
29
+ export const minWidth = createDimensionQueryHelper<number>(
30
+ ({ value, dimensions }) => value <= dimensions.width
31
+ );
32
+
33
+ export const minAspectRatio = createDimensionQueryHelper<number>(
34
+ ({ value, dimensions: { width, height } }) => value <= width / height
35
+ );
36
+
37
+ export const maxAspectRatio = createDimensionQueryHelper<number>(
38
+ ({ value, dimensions: { width, height } }) => value >= width / height
39
+ );
40
+
41
+ export const aspectRatio = createDimensionQueryHelper<number>(
42
+ ({ value, dimensions: { width, height } }) => value === width / height
43
+ );