@codeandmoney/soelma 0.0.0-dev.1 → 0.0.0-dev.10

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 (170) hide show
  1. package/.eslintignore +1 -0
  2. package/.eslintrc.js +27 -0
  3. package/.github/workflows/nodejs.yml +33 -0
  4. package/.size-limit.json +6 -0
  5. package/babel.config.js +3 -0
  6. package/biome.jsonc +73 -0
  7. package/docs/api.md +103 -0
  8. package/docs/appearance.md +54 -0
  9. package/docs/dark-mode.md +46 -0
  10. package/docs/dimensions.md +29 -0
  11. package/docs/i18n.md +67 -0
  12. package/docs/logo.png +0 -0
  13. package/docs/media-query.md +274 -0
  14. package/docs/orientation.md +44 -0
  15. package/docs/safe-area.md +62 -0
  16. package/docs/testting.md +51 -0
  17. package/docs/ts.md +127 -0
  18. package/example/AppStyleX/.watchmanconfig +1 -0
  19. package/example/AppStyleX/android/build.gradle +26 -0
  20. package/example/AppStyleX/android/gradle/wrapper/gradle-wrapper.jar +0 -0
  21. package/example/AppStyleX/android/gradle/wrapper/gradle-wrapper.properties +7 -0
  22. package/example/AppStyleX/android/gradle.properties +53 -0
  23. package/example/AppStyleX/android/gradlew +249 -0
  24. package/example/AppStyleX/android/gradlew.bat +92 -0
  25. package/example/AppStyleX/android/settings.gradle +12 -0
  26. package/example/AppStyleX/app.json +16 -0
  27. package/example/AppStyleX/babel.config.js +3 -0
  28. package/example/AppStyleX/index.js +9 -0
  29. package/example/AppStyleX/ios/Podfile +7 -0
  30. package/example/AppStyleX/ios/Podfile.lock +1252 -0
  31. package/example/AppStyleX/metro.config.js +51 -0
  32. package/example/AppStyleX/package.json +43 -0
  33. package/example/AppStyleX/react-native.config.js +23 -0
  34. package/example/AppStyleX/src/App.tsx +22 -0
  35. package/example/AppStyleX/src/BottomNav/index.tsx +26 -0
  36. package/example/AppStyleX/src/BottomNav/styles.ts +42 -0
  37. package/example/AppStyleX/src/Circle/index.tsx +53 -0
  38. package/example/AppStyleX/src/Circle/styles.ts +22 -0
  39. package/example/AppStyleX/src/Root/index.tsx +35 -0
  40. package/example/AppStyleX/src/Root/styles.ts +18 -0
  41. package/example/AppStyleX/src/ToggleButton/index.tsx +52 -0
  42. package/example/AppStyleX/src/ToggleButton/styles.ts +67 -0
  43. package/example/AppStyleX/src/style-system/hooks/useAnimatedBgColor.ts +5 -0
  44. package/example/AppStyleX/src/style-system/hooks/useAnimatedTextColor.ts +5 -0
  45. package/example/AppStyleX/src/style-system/hooks/useIsDark.ts +8 -0
  46. package/example/AppStyleX/src/style-system/palette.ts +11 -0
  47. package/example/AppStyleX/src/style-system/theme.ts +14 -0
  48. package/example/AppStyleX/src/style-system/utils.ts +8 -0
  49. package/example/AppStyleX/src/stylex.d.ts +6 -0
  50. package/example/AppStyleX/tsconfig.json +3 -0
  51. package/example/AppStyleX/yarn.lock +6767 -0
  52. package/jest.config.js +19 -0
  53. package/package.json +38 -3
  54. package/src/appearance/index.ts +30 -0
  55. package/src/appearance/init.ts +12 -0
  56. package/{context.js → src/context.ts} +4 -1
  57. package/src/create-event-emitter.ts +26 -0
  58. package/src/dark-mode/index.ts +26 -0
  59. package/src/dark-mode/init.ts +19 -0
  60. package/{dark-mode/state.js → src/dark-mode/state.ts} +2 -1
  61. package/src/default-theme.ts +4 -0
  62. package/src/dependency-registry.ts +21 -0
  63. package/src/dependency-usage.ts +21 -0
  64. package/src/dimensions/index.ts +20 -0
  65. package/src/dimensions/init.ts +32 -0
  66. package/src/dimensions/utils.ts +9 -0
  67. package/src/i18n.ts +15 -0
  68. package/src/index.ts +7 -0
  69. package/{makeUseStyles/createUseStylesTheme.js → src/make-use-styles/create-use-styles-theme.js} +23 -6
  70. package/src/make-use-styles/create-use-styles-theme.test.js +137 -0
  71. package/{makeUseStyles/createUseStylesWithoutTheme.js → src/make-use-styles/create-use-styles-without-theme.js} +20 -4
  72. package/src/make-use-styles/create-use-styles-without-theme.test.js +63 -0
  73. package/src/make-use-styles/index.d.ts +20 -0
  74. package/src/make-use-styles/index.js +12 -0
  75. package/src/make-use-styles/index.test.js +28 -0
  76. package/src/make-use-styles/types.ts +43 -0
  77. package/{makeUseStyles → src/make-use-styles}/utils.js +5 -10
  78. package/src/media-query/base.ts +32 -0
  79. package/src/media-query/breakpoints.ts +96 -0
  80. package/src/media-query/index.ts +12 -0
  81. package/src/orientation.ts +13 -0
  82. package/{safe-area/eventEmitter.js → src/safe-area/event-emitter.ts} +2 -1
  83. package/src/safe-area/index.tsx +16 -0
  84. package/src/safe-area/init.tsx +6 -0
  85. package/src/safe-area/safe-area-provider.tsx +18 -0
  86. package/{safe-area/state.js → src/safe-area/state.ts} +7 -5
  87. package/src/safe-area/stylex-save-area-consumer.ts +22 -0
  88. package/{safe-area/types.d.ts → src/safe-area/types.ts} +7 -6
  89. package/src/tests/createBreakpoints.test.ts +152 -0
  90. package/src/tests/createBreakpointsMatcher.test.ts +188 -0
  91. package/src/tests/createBreakpointsMatcher.types-test.ts +81 -0
  92. package/src/tests/createEventEmitter.test.ts +37 -0
  93. package/src/tests/dark-mode.test.ts +56 -0
  94. package/src/tests/dependencyRegistry.test.ts +16 -0
  95. package/src/tests/dependencyUsage.test.ts +13 -0
  96. package/src/tests/dimensions.test.ts +33 -0
  97. package/src/tests/makeUseStyles.types-test.ts +69 -0
  98. package/src/tests/media-query.test.ts +196 -0
  99. package/src/tests/orientation.test.ts +61 -0
  100. package/src/tests/useTheme.test.ts +26 -0
  101. package/src/tests/withStyles.types-test.tsx +172 -0
  102. package/src/use-color-transition.ts +50 -0
  103. package/src/use-theme.ts +14 -0
  104. package/src/with-styles.tsx +22 -0
  105. package/tsconfig.build.json +31 -0
  106. package/tsconfig.json +30 -0
  107. package/DefaultTheme.d.ts +0 -2
  108. package/DefaultTheme.js +0 -1
  109. package/appearance/consts.d.ts +0 -1
  110. package/appearance/index.d.ts +0 -8
  111. package/appearance/index.js +0 -18
  112. package/appearance/init.d.ts +0 -1
  113. package/appearance/init.js +0 -7
  114. package/context.d.ts +0 -4
  115. package/createEventEmitter.d.ts +0 -8
  116. package/createEventEmitter.js +0 -11
  117. package/dark-mode/consts.d.ts +0 -1
  118. package/dark-mode/index.d.ts +0 -7
  119. package/dark-mode/index.js +0 -16
  120. package/dark-mode/init.d.ts +0 -1
  121. package/dark-mode/init.js +0 -13
  122. package/dark-mode/state.d.ts +0 -3
  123. package/dependencyRegistry.d.ts +0 -5
  124. package/dependencyRegistry.js +0 -12
  125. package/dependencyUsage.d.ts +0 -7
  126. package/dependencyUsage.js +0 -10
  127. package/dimensions/consts.d.ts +0 -2
  128. package/dimensions/index.d.ts +0 -4
  129. package/dimensions/index.js +0 -13
  130. package/dimensions/init.d.ts +0 -1
  131. package/dimensions/init.js +0 -21
  132. package/dimensions/utils.d.ts +0 -1
  133. package/dimensions/utils.js +0 -7
  134. package/i18n.d.ts +0 -7
  135. package/i18n.js +0 -9
  136. package/index.d.ts +0 -7
  137. package/index.js +0 -5
  138. package/makeUseStyles/index.d.ts +0 -7
  139. package/makeUseStyles/index.js +0 -12
  140. package/media-query/base.d.ts +0 -12
  141. package/media-query/base.js +0 -18
  142. package/media-query/breakpoints.d.ts +0 -18
  143. package/media-query/breakpoints.js +0 -60
  144. package/media-query/index.d.ts +0 -2
  145. package/media-query/index.js +0 -2
  146. package/orientation.d.ts +0 -7
  147. package/orientation.js +0 -7
  148. package/safe-area/SafeAreaProvider.d.ts +0 -3
  149. package/safe-area/SafeAreaProvider.js +0 -9
  150. package/safe-area/StylexSaveAreaConsumer.d.ts +0 -2
  151. package/safe-area/StylexSaveAreaConsumer.js +0 -15
  152. package/safe-area/consts.d.ts +0 -1
  153. package/safe-area/eventEmitter.d.ts +0 -1
  154. package/safe-area/index.d.ts +0 -5
  155. package/safe-area/index.js +0 -10
  156. package/safe-area/init.d.ts +0 -1
  157. package/safe-area/init.js +0 -4
  158. package/safe-area/state.d.ts +0 -8
  159. package/safe-area/types.js +0 -1
  160. package/useColorTransition.d.ts +0 -5
  161. package/useColorTransition.js +0 -38
  162. package/useTheme.d.ts +0 -2
  163. package/useTheme.js +0 -9
  164. package/withStyles.d.ts +0 -7
  165. package/withStyles.js +0 -13
  166. /package/{appearance/consts.js → src/appearance/consts.ts} +0 -0
  167. /package/{dark-mode/consts.js → src/dark-mode/consts.ts} +0 -0
  168. /package/{dimensions/consts.js → src/dimensions/consts.ts} +0 -0
  169. /package/{makeUseStyles → src/make-use-styles}/test-type.js +0 -0
  170. /package/{safe-area/consts.js → src/safe-area/consts.ts} +0 -0
@@ -0,0 +1,20 @@
1
+ import { StyleSheet } from "react-native";
2
+ import { DefaultTheme } from "../default-theme";
3
+
4
+ export function makeUseStyles<
5
+ Theme extends DefaultTheme,
6
+ T extends StyleSheet.NamedStyles<T> | StyleSheet.NamedStyles<any>,
7
+ VariantsConfig extends Record<string, StyleSheet.NamedStyles<any>> | undefined = undefined,
8
+ >(config: {
9
+ styles?: (theme: Theme) => T | StyleSheet.NamedStyles<T>;
10
+ variants?: ((theme: Theme) => VariantsConfig) | undefined;
11
+ }): <Variants extends { [Key in keyof VariantsConfig]: keyof VariantsConfig[Key] }>(
12
+ variants?: Partial<Variants>,
13
+ ) => T &
14
+ (VariantsConfig extends undefined
15
+ ? {}
16
+ : {
17
+ variants: {
18
+ [VKey in keyof Variants]: VKey extends keyof VariantsConfig ? VariantsConfig[VKey][Variants[VKey]] : never;
19
+ }[keyof Variants];
20
+ });
@@ -0,0 +1,12 @@
1
+ import { createUseStylesWithoutTheme } from "./create-use-styles-without-theme";
2
+ import { createUseStylesTheme } from "./create-use-styles-theme";
3
+
4
+ export function makeUseStyles({ styles, variants }) {
5
+ const hasThemeDependency = styles?.length === 1;
6
+
7
+ if (!hasThemeDependency) {
8
+ return createUseStylesWithoutTheme(styles, variants);
9
+ }
10
+
11
+ return createUseStylesTheme(styles);
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,43 @@
1
+ // import { StyleSheet } from "react-native";
2
+ // import { DefaultTheme } from "../DefaultTheme";
3
+
4
+ // // // Source - https://stackoverflow.com/a/50375286
5
+ // // // Posted by jcalz, modified by community. See post 'Timeline' for change history
6
+ // // // Retrieved 2026-01-07, License - CC BY-SA 4.0
7
+
8
+ // // type UnionToIntersection<U> = (U extends any ? (x: U) => void : never) extends (
9
+ // // x: infer I,
10
+ // // ) => void
11
+ // // ? I
12
+ // // : never;
13
+
14
+ // export function makeUseStyles<
15
+ // Theme extends DefaultTheme,
16
+ // T extends StyleSheet.NamedStyles<T> | StyleSheet.NamedStyles<any>,
17
+ // V extends Record<string, StyleSheet.NamedStyles<any>>,
18
+ // >(
19
+ // getStyles: (theme: Theme) => T | StyleSheet.NamedStyles<T>,
20
+ // variants: V,
21
+ // ): <Variants extends { [Key in keyof V]: keyof V[Key] }>(
22
+ // variants?: Partial<Variants>,
23
+ // ) => T & {
24
+ // variants: {
25
+ // [VKey in keyof Variants]: VKey extends keyof V
26
+ // ? V[VKey][Variants[VKey]]
27
+ // : never;
28
+ // }[keyof Variants];
29
+ // } {
30
+ // return ((variants: any) => {}) as any;
31
+ // }
32
+
33
+ // const useStyles = makeUseStyles(() => ({ some: { alignContent: "center" } }), {
34
+ // other: {
35
+ // one: { alignContent: "flex-end", fontStyle: "italic", fontFamily: "Inter" },
36
+ // two: { alignContent: "flex-end" },
37
+ // },
38
+ // more: {
39
+ // lol: { color: "red", width: 123 },
40
+ // },
41
+ // });
42
+
43
+ // const res = useStyles({ more: "lol"});
@@ -1,6 +1,6 @@
1
1
  import { optimizeDependencies } from "../dimensions/utils";
2
- import { getUsing } from "../dependencyUsage";
3
- import { getDependency } from "../dependencyRegistry";
2
+ import { getUsing } from "../dependency-usage";
3
+ import { getDependency } from "../dependency-registry";
4
4
 
5
5
  import { useEffect, useRef, useState } from "react";
6
6
 
@@ -17,9 +17,7 @@ export const useForceUpdate = (scope) => {
17
17
  scope.forceUpdate = scope.forceUpdate.concat(forceRerender);
18
18
 
19
19
  unsubscribeRef.current = () => {
20
- scope.forceUpdate = scope.forceUpdate.filter(
21
- (fn) => fn !== forceRerender,
22
- );
20
+ scope.forceUpdate = scope.forceUpdate.filter((fn) => fn !== forceRerender);
23
21
  };
24
22
  }
25
23
 
@@ -32,8 +30,7 @@ export const useForceUpdate = (scope) => {
32
30
  );
33
31
  };
34
32
 
35
- export const getDependenciesKeys = () =>
36
- Object.keys(optimizeDependencies(getUsing())).sort();
33
+ export const getDependenciesKeys = () => Object.keys(optimizeDependencies(getUsing())).sort();
37
34
 
38
35
  export const subscribe = (dependenciesKeys, handler) => {
39
36
  if (dependenciesKeys.length === 0) {
@@ -50,9 +47,7 @@ export const subscribe = (dependenciesKeys, handler) => {
50
47
  const dependencyName = dependenciesKeys[index];
51
48
 
52
49
  if (!onChange) {
53
- console.warn(
54
- `[react-native-stylex] Could not find onChange handler for ${dependencyName}!`,
55
- );
50
+ console.warn(`[react-native-stylex] Could not find onChange handler for ${dependencyName}!`);
56
51
  }
57
52
 
58
53
  return !!onChange;
@@ -0,0 +1,32 @@
1
+ import { ScaledSize } from "react-native";
2
+
3
+ import { getWindowDimensions } from "../dimensions";
4
+
5
+ export const createDimensionQueryHelper =
6
+ <Value>(queryFunction: (options: { value: Value; dimensions: ScaledSize }) => boolean) =>
7
+ <T>(value: Value, styles: T): null | T => {
8
+ const isMatched = queryFunction({
9
+ value,
10
+ dimensions: getWindowDimensions(),
11
+ });
12
+
13
+ if (isMatched) {
14
+ return styles;
15
+ }
16
+
17
+ return null;
18
+ };
19
+
20
+ export const maxHeight = createDimensionQueryHelper<number>(({ value, dimensions }) => value >= dimensions.height);
21
+
22
+ export const maxWidth = createDimensionQueryHelper<number>(({ value, dimensions }) => value >= dimensions.width);
23
+
24
+ export const minHeight = createDimensionQueryHelper<number>(({ value, dimensions }) => value <= dimensions.height);
25
+
26
+ export const minWidth = createDimensionQueryHelper<number>(({ value, dimensions }) => value <= dimensions.width);
27
+
28
+ export const minAspectRatio = createDimensionQueryHelper<number>(({ value, dimensions: { width, height } }) => value <= width / height);
29
+
30
+ export const maxAspectRatio = createDimensionQueryHelper<number>(({ value, dimensions: { width, height } }) => value >= width / height);
31
+
32
+ export const aspectRatio = createDimensionQueryHelper<number>(({ value, dimensions: { width, height } }) => value === width / height);
@@ -0,0 +1,96 @@
1
+ import { minAspectRatio, minWidth, minHeight, maxWidth } from "./base";
2
+
3
+ interface BreakpointsMatcher<TBreakpoints> {
4
+ <T>(values: { [mode in keyof TBreakpoints]?: T }): T | undefined;
5
+ <T>(values: { [mode in keyof TBreakpoints]?: T } & { default: T }): T;
6
+ }
7
+
8
+ const orderByMin = [minWidth, minAspectRatio, minHeight];
9
+
10
+ const toOrderedBreakpointNames = <TBreakpoints extends Record<string, number>>(
11
+ values: any,
12
+ breakpoints: any,
13
+ matchFunction: any,
14
+ ): Array<keyof TBreakpoints> => {
15
+ const result = Object.keys(values)
16
+ .filter((e) => e !== "default")
17
+ .sort((a, b) => breakpoints[a] - breakpoints[b]);
18
+
19
+ if (orderByMin.includes(matchFunction)) {
20
+ return result.slice().reverse();
21
+ }
22
+
23
+ return result;
24
+ };
25
+
26
+ /*
27
+ * To debug use analog on CSS: https://codepen.io/retyui/pen/dyOzKzV
28
+ */
29
+ export function createBreakpointsMatcher<TBreakpoints extends Record<string, number>>(
30
+ breakpoints: TBreakpoints,
31
+ matchFunction = minWidth,
32
+ ): BreakpointsMatcher<TBreakpoints> {
33
+ return function breakpointsMatcher(values: any) {
34
+ /* istanbul ignore next */
35
+ // @ts-expect-error
36
+ if (process.env.NODE_ENV !== "production") {
37
+ const invalidKeys = Object.keys(values).filter((key) => {
38
+ return key !== "default" && breakpoints[key] === undefined;
39
+ });
40
+
41
+ if (invalidKeys.length > 0) {
42
+ console.warn(
43
+ `[react-native-stylex]: Invalid values was passed to 'breakpointsMatcher' function
44
+
45
+ allowed keys: ${Object.keys(breakpoints).join(", ")}
46
+ unexpected keys: ${invalidKeys.join(", ")}
47
+ `,
48
+ );
49
+ }
50
+ }
51
+
52
+ const orderedBreakpointNames = toOrderedBreakpointNames<TBreakpoints>(values, breakpoints, matchFunction);
53
+
54
+ const key = orderedBreakpointNames.find((breakpointName) => matchFunction(breakpoints[breakpointName] as number, values[breakpointName])) || "default";
55
+
56
+ return values[key] || null;
57
+ };
58
+ }
59
+
60
+ function getNextByKey<TBreakpoints extends Record<string, number>>(breakpoints: TBreakpoints, key: keyof TBreakpoints) {
61
+ type Keys = keyof TBreakpoints;
62
+
63
+ const breakpointsKeys: Array<Keys> = Object.keys(breakpoints).sort((a: Keys, b: Keys) => breakpoints[a]! - breakpoints[b]!);
64
+ const index = breakpointsKeys.indexOf(key);
65
+ const nextKey: Keys | undefined = breakpointsKeys[index + 1];
66
+
67
+ return nextKey;
68
+ }
69
+
70
+ export function createBreakpoints<TBreakpoints extends Record<string, number>>(breakpoints: TBreakpoints) {
71
+ type Keys = keyof TBreakpoints;
72
+
73
+ function up<T>(key: Keys, value: T): T | null {
74
+ return minWidth<T>(breakpoints[key] as number, value);
75
+ }
76
+
77
+ function down<T>(key: Keys, value: T): T | null {
78
+ return maxWidth<T>(breakpoints[key] as number, value);
79
+ }
80
+
81
+ function only<T>(key: Keys, value: T): T | null {
82
+ const nextKey = getNextByKey<TBreakpoints>(breakpoints, key);
83
+
84
+ if (nextKey !== undefined) {
85
+ return minWidth(breakpoints[key] as number, maxWidth(breakpoints[nextKey]! - 0.05, value));
86
+ }
87
+
88
+ return minWidth(breakpoints[key] as number, value);
89
+ }
90
+
91
+ function between<T>(start: Keys, end: Keys, value: T): T | null {
92
+ return minWidth(breakpoints[start] as number, maxWidth(breakpoints[end]! - 0.05, value));
93
+ }
94
+
95
+ return { up, down, only, between };
96
+ }
@@ -0,0 +1,12 @@
1
+ export {
2
+ minWidth,
3
+ minAspectRatio,
4
+ maxWidth,
5
+ aspectRatio,
6
+ createDimensionQueryHelper,
7
+ maxAspectRatio,
8
+ maxHeight,
9
+ minHeight,
10
+ } from "./base";
11
+
12
+ export { createBreakpointsMatcher, createBreakpoints } from "./breakpoints";
@@ -0,0 +1,13 @@
1
+ import { getScreenDimensions } from "./dimensions";
2
+
3
+ type OrientationType = "portrait" | "landscape";
4
+
5
+ export function orientation<T>(spec: { [orientation in OrientationType]?: T }): T | undefined {
6
+ const { height, width } = getScreenDimensions();
7
+
8
+ return width <= height ? spec.portrait : spec.landscape;
9
+ }
10
+
11
+ export const portraitOrientation = <T>(portraitStyles: T): T | undefined => orientation<T>({ portrait: portraitStyles });
12
+
13
+ export const landscapeOrientation = <T>(landscapeStyles: T): T | undefined => orientation<T>({ landscape: landscapeStyles });
@@ -1,3 +1,4 @@
1
- import { createEventEmitter } from "../createEventEmitter";
1
+ import { createEventEmitter } from "../create-event-emitter";
2
2
  import { SAFE_AREA_DEPENDENCY_KEY } from "./consts";
3
+
3
4
  export const { emit, on } = createEventEmitter(SAFE_AREA_DEPENDENCY_KEY);
@@ -0,0 +1,16 @@
1
+ import "./init";
2
+
3
+ import { onUse } from "../dependency-usage";
4
+
5
+ import { SAFE_AREA_DEPENDENCY_KEY } from "./consts";
6
+ import { EdgeInsets } from "./types";
7
+ import { state } from "./state";
8
+
9
+ export { StylexSaveAreaConsumer } from "./stylex-save-area-consumer";
10
+ export { SafeAreaProvider } from "./safe-area-provider";
11
+
12
+ export function getSafeArea(): EdgeInsets {
13
+ onUse(SAFE_AREA_DEPENDENCY_KEY);
14
+
15
+ return state.insets;
16
+ }
@@ -0,0 +1,6 @@
1
+ import { addDependency } from "../dependency-registry";
2
+
3
+ import { SAFE_AREA_DEPENDENCY_KEY } from "./consts";
4
+ import { on } from "./event-emitter";
5
+
6
+ addDependency(SAFE_AREA_DEPENDENCY_KEY, (handler: () => void) => on(handler));
@@ -0,0 +1,18 @@
1
+ import React from "react";
2
+ import { SafeAreaProvider as CoreSafeAreaProvider, initialWindowMetrics, initialWindowSafeAreaInsets } from "react-native-safe-area-context";
3
+
4
+ import { StylexSaveAreaConsumer } from "./stylex-save-area-consumer";
5
+ import { SafeAreaViewProps } from "./types";
6
+
7
+ export function SafeAreaProvider(props: SafeAreaViewProps): React.JSX.Element | null {
8
+ return (
9
+ <CoreSafeAreaProvider
10
+ initialSafeAreaInsets={props.initialSafeAreaInsets || initialWindowSafeAreaInsets}
11
+ initialMetrics={props.initialMetrics || initialWindowMetrics}
12
+ {...props}
13
+ >
14
+ {props.children}
15
+ <StylexSaveAreaConsumer />
16
+ </CoreSafeAreaProvider>
17
+ );
18
+ }
@@ -1,10 +1,12 @@
1
1
  import { initialWindowMetrics as metrics } from "react-native-safe-area-context";
2
+
2
3
  const defaultInsets = {
3
- top: 0,
4
- left: 0,
5
- bottom: 0,
6
- right: 0,
4
+ top: 0,
5
+ left: 0,
6
+ bottom: 0,
7
+ right: 0,
7
8
  };
9
+
8
10
  export const state = {
9
- insets: metrics?.insets || defaultInsets,
11
+ insets: metrics?.insets || defaultInsets,
10
12
  };
@@ -0,0 +1,22 @@
1
+ import { useSafeAreaInsets } from "react-native-safe-area-context";
2
+ import { EdgeInsets } from "./types";
3
+
4
+ import { emit } from "./event-emitter";
5
+ import { state } from "./state";
6
+ import { useLayoutEffect } from "react";
7
+ import type React from "react";
8
+
9
+ export function StylexSaveAreaConsumer(): React.JSX.Element | null {
10
+ const insets: EdgeInsets = useSafeAreaInsets();
11
+
12
+ useLayoutEffect(() => {
13
+ const isChanged = Object.entries(insets).some(([key, value]) => state.insets[key as keyof EdgeInsets] !== value);
14
+
15
+ if (isChanged) {
16
+ state.insets = insets;
17
+ emit();
18
+ }
19
+ }, [insets]);
20
+
21
+ return null;
22
+ }
@@ -1,9 +1,10 @@
1
1
  import { SafeAreaProviderProps } from "react-native-safe-area-context";
2
+
2
3
  export interface EdgeInsets {
3
- top: number;
4
- right: number;
5
- bottom: number;
6
- left: number;
7
- }
8
- export interface SafeAreaViewProps extends SafeAreaProviderProps {
4
+ top: number;
5
+ right: number;
6
+ bottom: number;
7
+ left: number;
9
8
  }
9
+
10
+ export interface SafeAreaViewProps extends SafeAreaProviderProps {}
@@ -0,0 +1,152 @@
1
+ import { createBreakpoints } from "../media-query/breakpoints";
2
+ import { getWindowDimensions } from "../dimensions";
3
+
4
+ jest.mock("../dimensions", () => ({
5
+ getWindowDimensions: jest.fn(),
6
+ }));
7
+
8
+ const mockWindowWidth = (width: number) => {
9
+ const mockGetWindowDimensions = getWindowDimensions as jest.Mock;
10
+ mockGetWindowDimensions.mockReturnValue({ width });
11
+ };
12
+
13
+ describe("createBreakpoints", () => {
14
+ const config = {
15
+ xs: 360,
16
+ sm: 600,
17
+ md: 960,
18
+ lg: 1280,
19
+ xl: 1920,
20
+ };
21
+ const breakpoints = createBreakpoints(config);
22
+
23
+ describe("down", () => {
24
+ const getStyles = () => ({
25
+ fontSize: 16,
26
+ ...breakpoints.down("lg", { fontSize: 18 }),
27
+ ...breakpoints.down("sm", { fontSize: 20 }),
28
+ });
29
+
30
+ it("should return small variant", () => {
31
+ mockWindowWidth(0);
32
+ expect(getStyles()).toEqual({ fontSize: 20 });
33
+ mockWindowWidth(600);
34
+ expect(getStyles()).toEqual({ fontSize: 20 });
35
+ });
36
+
37
+ it("should return large variant", () => {
38
+ mockWindowWidth(601);
39
+ expect(getStyles()).toEqual({ fontSize: 18 });
40
+ mockWindowWidth(1280);
41
+ expect(getStyles()).toEqual({ fontSize: 18 });
42
+ });
43
+
44
+ it("should return default variant", () => {
45
+ mockWindowWidth(1281);
46
+ expect(getStyles()).toEqual({ fontSize: 16 });
47
+ mockWindowWidth(1920);
48
+ expect(getStyles()).toEqual({ fontSize: 16 });
49
+ mockWindowWidth(1921);
50
+ expect(getStyles()).toEqual({ fontSize: 16 });
51
+ });
52
+ });
53
+
54
+ describe("up", () => {
55
+ const getStyles = () => ({
56
+ fontSize: 20,
57
+ ...breakpoints.up("sm", { fontSize: 18 }),
58
+ ...breakpoints.up("lg", { fontSize: 16 }),
59
+ });
60
+
61
+ it("should return small variant", () => {
62
+ mockWindowWidth(0);
63
+ expect(getStyles()).toEqual({ fontSize: 20 });
64
+ mockWindowWidth(599);
65
+ expect(getStyles()).toEqual({ fontSize: 20 });
66
+ });
67
+
68
+ it("should return large variant", () => {
69
+ mockWindowWidth(600);
70
+ expect(getStyles()).toEqual({ fontSize: 18 });
71
+ mockWindowWidth(1279);
72
+ expect(getStyles()).toEqual({ fontSize: 18 });
73
+ });
74
+
75
+ it("should return default variant", () => {
76
+ mockWindowWidth(1280);
77
+ expect(getStyles()).toEqual({ fontSize: 16 });
78
+ mockWindowWidth(1920);
79
+ expect(getStyles()).toEqual({ fontSize: 16 });
80
+ mockWindowWidth(1921);
81
+ expect(getStyles()).toEqual({ fontSize: 16 });
82
+ });
83
+ });
84
+
85
+ describe("only", () => {
86
+ it("should work properly", () => {
87
+ const getStyles = () => ({
88
+ fontSize: 16,
89
+ ...breakpoints.only("md", { fontSize: 20 }),
90
+ });
91
+ // window width : 0...xs...sm...md...lg...xl...∞
92
+ // fontSize : .............[20...20]...........
93
+
94
+ mockWindowWidth(0);
95
+ expect(getStyles()).toEqual({ fontSize: 16 });
96
+ mockWindowWidth(config.md - 0.5);
97
+ expect(getStyles()).toEqual({ fontSize: 16 });
98
+
99
+ mockWindowWidth(config.md);
100
+ expect(getStyles()).toEqual({ fontSize: 20 });
101
+ mockWindowWidth(config.lg - 0.5);
102
+ expect(getStyles()).toEqual({ fontSize: 20 });
103
+
104
+ mockWindowWidth(config.lg);
105
+ expect(getStyles()).toEqual({ fontSize: 16 });
106
+ });
107
+
108
+ it("should call up on last breakpoint", () => {
109
+ const getStyles = () => ({
110
+ fontSize: 16,
111
+ ...breakpoints.only("xl", { fontSize: 20 }),
112
+ });
113
+ // window width : 0...xs...sm...md...lg...xl...∞
114
+ // fontSize : .......................[20.......
115
+
116
+ mockWindowWidth(config.xl - 0.5);
117
+ expect(getStyles()).toEqual({ fontSize: 16 });
118
+
119
+ mockWindowWidth(config.xl);
120
+ expect(getStyles()).toEqual({ fontSize: 20 });
121
+ mockWindowWidth(config.xl + 0.5);
122
+ expect(getStyles()).toEqual({ fontSize: 20 });
123
+ });
124
+ });
125
+
126
+ describe("between", () => {
127
+ const getStyles = () => ({
128
+ fontSize: 16,
129
+ ...breakpoints.between("sm", "lg", { fontSize: 20 }),
130
+ });
131
+
132
+ it("should work properly", () => {
133
+ // window width : 0...xs...sm...md...lg...xl...∞
134
+ // fontSize : ........[20........20]...........
135
+
136
+ mockWindowWidth(0);
137
+ expect(getStyles()).toEqual({ fontSize: 16 });
138
+ mockWindowWidth(config.sm - 0.5);
139
+ expect(getStyles()).toEqual({ fontSize: 16 });
140
+
141
+ mockWindowWidth(config.sm);
142
+ expect(getStyles()).toEqual({ fontSize: 20 });
143
+ mockWindowWidth(config.md);
144
+ expect(getStyles()).toEqual({ fontSize: 20 });
145
+ mockWindowWidth(config.lg - 0.5);
146
+ expect(getStyles()).toEqual({ fontSize: 20 });
147
+
148
+ mockWindowWidth(config.lg);
149
+ expect(getStyles()).toEqual({ fontSize: 16 });
150
+ });
151
+ });
152
+ });