/../**/__tests__/*universal.ts"
107 | ]
108 | }
109 | ]
110 | }
111 | }
112 |
--------------------------------------------------------------------------------
/rollup.config.js:
--------------------------------------------------------------------------------
1 | import babel from "rollup-plugin-babel";
2 |
3 | const rn = process.env.TARGET === "react-native";
4 |
5 | const file = rn ? "lib/rn.js" : "lib/index.js";
6 |
7 | export default {
8 | external: ["react", "react-native", "lodash"],
9 | input: "src/index.js",
10 | output: { file, format: "cjs", exports: "named" },
11 | plugins: [
12 | babel({
13 | presets: [["es2015", { modules: false }], "react", "stage-0"],
14 | plugins: ["external-helpers"],
15 | comments: false,
16 | babelrc: false,
17 | }),
18 | ],
19 | };
20 |
--------------------------------------------------------------------------------
/src/__mocks__/utils.js:
--------------------------------------------------------------------------------
1 | const actualUtils = jest.requireActual("../utils");
2 |
3 | const utils = { ...actualUtils, getWindowWidth: jest.fn(() => 1024) };
4 |
5 | module.exports = utils;
6 |
--------------------------------------------------------------------------------
/src/__tests__/__snapshots__/processAttrs.spec.web.ts.snap.web:
--------------------------------------------------------------------------------
1 | // Jest Snapshot v1, https://goo.gl/fbAQLP
2 |
3 | exports[`t001 Should not stretch single value 1`] = `
4 | Object {
5 | "numColumns": 3,
6 | }
7 | `;
8 |
9 | exports[`t002 Should stretch array of 1 value 1`] = `
10 | Array [
11 | Object {
12 | "numColumns": 3,
13 | },
14 | Object {
15 | "numColumns": 3,
16 | },
17 | Object {
18 | "numColumns": 3,
19 | },
20 | Object {
21 | "numColumns": 3,
22 | },
23 | Object {
24 | "numColumns": 3,
25 | },
26 | ]
27 | `;
28 |
29 | exports[`t003 Should stretch odd array of 3 values 1`] = `
30 | Array [
31 | Object {
32 | "numColumns": 3,
33 | },
34 | Object {
35 | "numColumns": 3,
36 | },
37 | Object {
38 | "numColumns": 4,
39 | },
40 | Object {
41 | "numColumns": 5,
42 | },
43 | Object {
44 | "numColumns": 5,
45 | },
46 | ]
47 | `;
48 |
49 | exports[`t004 Should stretch odd array of 5 values 1`] = `
50 | Array [
51 | Object {
52 | "numColumns": 2,
53 | },
54 | Object {
55 | "numColumns": 3,
56 | },
57 | Object {
58 | "numColumns": 4,
59 | },
60 | Object {
61 | "numColumns": 5,
62 | },
63 | Object {
64 | "numColumns": 6,
65 | },
66 | ]
67 | `;
68 |
69 | exports[`t005 Should stretch even array of 2 values 1`] = `
70 | Array [
71 | Object {
72 | "numColumns": 3,
73 | },
74 | Object {
75 | "numColumns": 3,
76 | },
77 | Object {
78 | "numColumns": 4,
79 | },
80 | Object {
81 | "numColumns": 4,
82 | },
83 | Object {
84 | "numColumns": 4,
85 | },
86 | ]
87 | `;
88 |
89 | exports[`t006 Should stretch even array of 4 values 1`] = `
90 | Array [
91 | Object {
92 | "numColumns": 2,
93 | },
94 | Object {
95 | "numColumns": 3,
96 | },
97 | Object {
98 | "numColumns": 4,
99 | },
100 | Object {
101 | "numColumns": 5,
102 | },
103 | Object {
104 | "numColumns": 5,
105 | },
106 | ]
107 | `;
108 |
109 | exports[`t007 Should work with array and non-array values 1`] = `
110 | Array [
111 | Object {
112 | "breed": "short hair",
113 | "info": Object {
114 | "prop1": "property 1",
115 | "prop2": "property 2",
116 | },
117 | "specie": "cat",
118 | },
119 | Object {
120 | "breed": "short hair",
121 | "info": Object {
122 | "prop1": "property 1",
123 | "prop2": "property 2",
124 | },
125 | "specie": "cat",
126 | },
127 | Object {
128 | "breed": "callico",
129 | "info": Object {
130 | "prop1": "property 1",
131 | "prop2": "property 2",
132 | },
133 | "specie": "cat",
134 | },
135 | Object {
136 | "breed": "callico",
137 | "info": Object {
138 | "prop1": "property 1",
139 | "prop2": "property 2",
140 | },
141 | "specie": "cat",
142 | },
143 | Object {
144 | "breed": "callico",
145 | "info": Object {
146 | "prop1": "property 1",
147 | "prop2": "property 2",
148 | },
149 | "specie": "cat",
150 | },
151 | ]
152 | `;
153 |
--------------------------------------------------------------------------------
/src/__tests__/__snapshots__/styled.spec.web.tsx.snap.web:
--------------------------------------------------------------------------------
1 | // Jest Snapshot v1, https://goo.gl/fbAQLP
2 |
3 | exports[`t001 single style - no breakpoints, breakpoint: 0, windowWidth: 374 1`] = `
4 |
9 | Hello
10 |
11 | `;
12 |
13 | exports[`t001 single style - no breakpoints, breakpoint: 1, windowWidth: 767 1`] = `
14 |
19 | Hello
20 |
21 | `;
22 |
23 | exports[`t001 single style - no breakpoints, breakpoint: 2, windowWidth: 1023 1`] = `
24 |
29 | Hello
30 |
31 | `;
32 |
33 | exports[`t001 single style - no breakpoints, breakpoint: 3, windowWidth: 1679 1`] = `
34 |
39 | Hello
40 |
41 | `;
42 |
43 | exports[`t001 single style - no breakpoints, breakpoint: 4, windowWidth: 1681 1`] = `
44 |
49 | Hello
50 |
51 | `;
52 |
53 | exports[`t002 single style - with breakpoints, breakpoint: 0, windowWidth: 374 1`] = `
54 |
59 | Hello
60 |
61 | `;
62 |
63 | exports[`t002 single style - with breakpoints, breakpoint: 1, windowWidth: 767 1`] = `
64 |
69 | Hello
70 |
71 | `;
72 |
73 | exports[`t002 single style - with breakpoints, breakpoint: 2, windowWidth: 1023 1`] = `
74 |
79 | Hello
80 |
81 | `;
82 |
83 | exports[`t002 single style - with breakpoints, breakpoint: 3, windowWidth: 1679 1`] = `
84 |
89 | Hello
90 |
91 | `;
92 |
93 | exports[`t002 single style - with breakpoints, breakpoint: 4, windowWidth: 1681 1`] = `
94 |
99 | Hello
100 |
101 | `;
102 |
103 | exports[`t003 single style - should merge nested styled() styles, breakpoint: 0, windowWidth: 374 1`] = `
104 |
108 | `;
109 |
110 | exports[`t003 single style - should merge nested styled() styles, breakpoint: 1, windowWidth: 767 1`] = `
111 |
115 | `;
116 |
117 | exports[`t003 single style - should merge nested styled() styles, breakpoint: 2, windowWidth: 1023 1`] = `
118 |
122 | `;
123 |
124 | exports[`t003 single style - should merge nested styled() styles, breakpoint: 3, windowWidth: 1679 1`] = `
125 |
129 | `;
130 |
131 | exports[`t003 single style - should merge nested styled() styles, breakpoint: 4, windowWidth: 1681 1`] = `
132 |
136 | `;
137 |
138 | exports[`t004 single style - should merge styled() and inline style and attrs 1`] = `
139 |
160 | `;
161 |
162 | exports[`t008 multiple styles - with breakpoints, breakpoint: 0, windowWidth: 374 1`] = `
163 |
172 | `;
173 |
174 | exports[`t008 multiple styles - with breakpoints, breakpoint: 0, windowWidth: 374 2`] = `
175 |
179 | `;
180 |
181 | exports[`t008 multiple styles - with breakpoints, breakpoint: 1, windowWidth: 767 1`] = `
182 |
191 | `;
192 |
193 | exports[`t008 multiple styles - with breakpoints, breakpoint: 1, windowWidth: 767 2`] = `
194 |
198 | `;
199 |
200 | exports[`t008 multiple styles - with breakpoints, breakpoint: 2, windowWidth: 1023 1`] = `
201 |
210 | `;
211 |
212 | exports[`t008 multiple styles - with breakpoints, breakpoint: 2, windowWidth: 1023 2`] = `
213 |
217 | `;
218 |
219 | exports[`t008 multiple styles - with breakpoints, breakpoint: 3, windowWidth: 1679 1`] = `
220 |
229 | `;
230 |
231 | exports[`t008 multiple styles - with breakpoints, breakpoint: 3, windowWidth: 1679 2`] = `
232 |
236 | `;
237 |
238 | exports[`t008 multiple styles - with breakpoints, breakpoint: 4, windowWidth: 1681 1`] = `
239 |
248 | `;
249 |
250 | exports[`t008 multiple styles - with breakpoints, breakpoint: 4, windowWidth: 1681 2`] = `
251 |
255 | `;
256 |
257 | exports[`t009 multiple styles - should merge nested styled() styles - no breakpoints 1`] = `
258 |
282 | `;
283 |
284 | exports[`t010 multiple styles - should merge nested styled() styles - with breakpoints, breakpoint: 0, windowWidth: 374 1`] = `
285 |
309 | `;
310 |
311 | exports[`t010 multiple styles - should merge nested styled() styles - with breakpoints, breakpoint: 1, windowWidth: 767 1`] = `
312 |
336 | `;
337 |
338 | exports[`t010 multiple styles - should merge nested styled() styles - with breakpoints, breakpoint: 2, windowWidth: 1023 1`] = `
339 |
363 | `;
364 |
365 | exports[`t010 multiple styles - should merge nested styled() styles - with breakpoints, breakpoint: 3, windowWidth: 1679 1`] = `
366 |
390 | `;
391 |
392 | exports[`t010 multiple styles - should merge nested styled() styles - with breakpoints, breakpoint: 4, windowWidth: 1681 1`] = `
393 |
417 | `;
418 |
--------------------------------------------------------------------------------
/src/__tests__/__snapshots__/useStyled.spec.web.tsx.snap.web:
--------------------------------------------------------------------------------
1 | // Jest Snapshot v1, https://goo.gl/fbAQLP
2 |
3 | exports[`t001 single style - no breakpoints 1`] = `
4 |
7 |
12 | My color should be red
13 |
14 |
15 | `;
16 |
17 | exports[`t002 single style - with breakpoints, breakpoint: 0, windowWidth: 374 1`] = `
18 |
21 |
26 | My color should be red
27 |
28 |
29 | `;
30 |
31 | exports[`t002 single style - with breakpoints, breakpoint: 1, windowWidth: 767 1`] = `
32 |
35 |
40 | My color should be red
41 |
42 |
43 | `;
44 |
45 | exports[`t002 single style - with breakpoints, breakpoint: 2, windowWidth: 1023 1`] = `
46 |
49 |
54 | My color should be blue
55 |
56 |
57 | `;
58 |
59 | exports[`t002 single style - with breakpoints, breakpoint: 3, windowWidth: 1679 1`] = `
60 |
63 |
68 | My color should be blue
69 |
70 |
71 | `;
72 |
73 | exports[`t002 single style - with breakpoints, breakpoint: 4, windowWidth: 1681 1`] = `
74 |
77 |
82 | My color should be blue
83 |
84 |
85 | `;
86 |
87 | exports[`t003 single style, multiple styles and attrs - with breakpoints, breakpoint: 0, windowWidth: 374 1`] = `
88 |
92 |
97 | My color should be RED
98 |
99 |
100 | `;
101 |
102 | exports[`t003 single style, multiple styles and attrs - with breakpoints, breakpoint: 1, windowWidth: 767 1`] = `
103 |
107 |
112 | My color should be RED
113 |
114 |
115 | `;
116 |
117 | exports[`t003 single style, multiple styles and attrs - with breakpoints, breakpoint: 2, windowWidth: 1023 1`] = `
118 |
122 |
127 | My color should be BLUE
128 |
129 |
130 | `;
131 |
132 | exports[`t003 single style, multiple styles and attrs - with breakpoints, breakpoint: 3, windowWidth: 1679 1`] = `
133 |
137 |
142 | My color should be BLUE
143 |
144 |
145 | `;
146 |
147 | exports[`t003 single style, multiple styles and attrs - with breakpoints, breakpoint: 4, windowWidth: 1681 1`] = `
148 |
152 |
157 | My color should be BLUE
158 |
159 |
160 | `;
161 |
--------------------------------------------------------------------------------
/src/__tests__/os.spec.universal.ts:
--------------------------------------------------------------------------------
1 | import { os } from "../utils";
2 | import { Platform } from "react-native";
3 |
4 | test("os({}) with object argument", () => {
5 | const webOrNative = os({ web: "web", native: "native" });
6 | const webOrIosOrAndroid = os({ web: "web", ios: "ios", android: "android" });
7 |
8 | if (Platform.OS === "web") {
9 | expect(webOrNative).toBe("web");
10 | expect(webOrIosOrAndroid).toBe("web");
11 | } else if (Platform.OS === "ios") {
12 | expect(webOrNative).toBe("native");
13 | expect(webOrIosOrAndroid).toBe("ios");
14 | } else if (Platform.OS === "android") {
15 | expect(webOrNative).toBe("native");
16 | expect(webOrIosOrAndroid).toBe("android");
17 | }
18 | });
19 |
20 | test("os([]) with array argument", () => {
21 | const webOrNative = os(["web", "native"]);
22 | const webOrIosOrAndroid = os(["web", "ios", "android"]);
23 |
24 | if (Platform.OS === "web") {
25 | expect(webOrNative).toBe("web");
26 | expect(webOrIosOrAndroid).toBe("web");
27 | } else if (Platform.OS === "ios") {
28 | expect(webOrNative).toBe("native");
29 | expect(webOrIosOrAndroid).toBe("ios");
30 | } else if (Platform.OS === "android") {
31 | expect(webOrNative).toBe("native");
32 | expect(webOrIosOrAndroid).toBe("android");
33 | }
34 | });
35 |
--------------------------------------------------------------------------------
/src/__tests__/processAttrs.spec.web.ts:
--------------------------------------------------------------------------------
1 | import processAttrs from "../processAttrs";
2 |
3 | const breakpoints = [375, 768, 1024, 1680];
4 |
5 | test("t001 Should not stretch single value", () => {
6 | const attrs = { numColumns: 3 };
7 |
8 | const result = processAttrs(breakpoints, attrs);
9 | expect(result).toMatchSnapshot();
10 | });
11 |
12 | test("t002 Should stretch array of 1 value", () => {
13 | const attrs = { numColumns: [3] };
14 |
15 | const result = processAttrs(breakpoints, attrs);
16 | expect(result).toMatchSnapshot();
17 | });
18 |
19 | test("t003 Should stretch odd array of 3 values", () => {
20 | const attrs = { numColumns: [3, 4, 5] };
21 |
22 | const result = processAttrs(breakpoints, attrs);
23 | expect(result).toMatchSnapshot();
24 | });
25 |
26 | test("t004 Should stretch odd array of 5 values", () => {
27 | const attrs = { numColumns: [2, 3, 4, 5, 6] };
28 |
29 | const result = processAttrs(breakpoints, attrs);
30 | expect(result).toMatchSnapshot();
31 | });
32 |
33 | test("t005 Should stretch even array of 2 values", () => {
34 | const attrs = { numColumns: [3, 4] };
35 |
36 | const result = processAttrs(breakpoints, attrs);
37 | expect(result).toMatchSnapshot();
38 | });
39 |
40 | test("t006 Should stretch even array of 4 values", () => {
41 | const attrs = { numColumns: [2, 3, 4, 5] };
42 |
43 | const result = processAttrs(breakpoints, attrs);
44 | expect(result).toMatchSnapshot();
45 | });
46 |
47 | test("t007 Should work with array and non-array values", () => {
48 | const attrs = {
49 | breed: ["short hair", "callico"],
50 | specie: "cat",
51 | info: {
52 | prop1: "property 1",
53 | prop2: "property 2"
54 | }
55 | };
56 |
57 | const result = processAttrs(breakpoints, attrs);
58 | expect(result).toMatchSnapshot();
59 | });
60 |
--------------------------------------------------------------------------------
/src/__tests__/processStyle.spec.web.ts:
--------------------------------------------------------------------------------
1 | import { processStyle } from "../processStyles";
2 |
3 | const theme = {
4 | breakpoints: [375, 768, 1024, 1680],
5 | space: [0, 3, 6, 12, 24, 48, 96]
6 | };
7 |
8 | test("Should not stretch single values", () => {
9 | const style = {
10 | padding: 2,
11 | margin: 1
12 | };
13 | const stylesExpect = {
14 | padding: 6,
15 | margin: 3
16 | };
17 |
18 | const stylesResult = processStyle(theme, style);
19 |
20 | expect(stylesResult).toEqual(stylesExpect);
21 | });
22 |
23 | test("Should stretch odd array of 3 values", () => {
24 | const style = {
25 | padding: [2, 3, 4], // 6, 12, 24
26 | margin: [1, 2]
27 | };
28 |
29 | const stylesExpect = [
30 | { padding: 6, margin: 3 }, // -- small phone
31 | { padding: 6, margin: 3 }, // -- normal phone
32 | { padding: 12, margin: 6 }, // -- tablet
33 | { padding: 24, margin: 6 }, // -- normal desktop
34 | { padding: 24, margin: 6 } // -- large desktop
35 | ];
36 |
37 | const stylesResult = processStyle(theme, style);
38 |
39 | expect(stylesResult).toEqual(stylesExpect);
40 | });
41 |
--------------------------------------------------------------------------------
/src/__tests__/processStyles.spec.web.ts:
--------------------------------------------------------------------------------
1 | import "@testing-library/jest-dom";
2 | import * as T from "../types";
3 |
4 | import processStyles from "../processStyles";
5 |
6 | const theme: T.Theme = {
7 | breakpoints: [375, 768, 1024, 1680],
8 | borderWidths: [1, 2, 4],
9 | fontSizes: [12, 14, 16, 20, 24, 32, 48, 64],
10 | colors: {},
11 | space: [0, 3, 6, 12, 24, 48, 96],
12 | sizes: [0, 3, 6, 12, 24, 48, 96],
13 | fonts: {
14 | sans: "system-ui, sans-serif",
15 | mono: "Menlo, monospace"
16 | },
17 | fontWeights: {
18 | normal: "normal",
19 | medium: "500",
20 | bold: "bold"
21 | }
22 | };
23 |
24 | test("Should stretch odd array of 1 value", () => {
25 | const styles = {
26 | container: {
27 | padding: [2] // 6
28 | }
29 | };
30 |
31 | const stylesExpect = [
32 | { container: { padding: 6 } }, // -- small phone
33 | { container: { padding: 6 } }, // -- normal phone
34 | { container: { padding: 6 } }, // -- tablet
35 | { container: { padding: 6 } }, // -- normal desktop
36 | { container: { padding: 6 } } // -- large desktop
37 | ];
38 |
39 | const stylesResult = processStyles(theme, styles);
40 |
41 | expect(stylesResult).toEqual(stylesExpect);
42 | });
43 |
44 | test("Should not stretch single values", () => {
45 | const styles = {
46 | container: {
47 | padding: 2
48 | }
49 | };
50 | const stylesExpect = {
51 | container: {
52 | padding: 6
53 | }
54 | };
55 |
56 | const stylesResult = processStyles(theme, styles);
57 |
58 | expect(stylesResult).toEqual(stylesExpect);
59 | });
60 |
61 | test("Should stretch odd array of 3 values", () => {
62 | const styles = {
63 | container: {
64 | padding: [2, 3, 4] // 6, 12, 24
65 | }
66 | };
67 |
68 | const stylesExpect = [
69 | { container: { padding: 6 } }, // -- small phone
70 | { container: { padding: 6 } }, // -- normal phone
71 | { container: { padding: 12 } }, // -- tablet
72 | { container: { padding: 24 } }, // -- normal desktop
73 | { container: { padding: 24 } } // -- large desktop
74 | ];
75 |
76 | const stylesResult = processStyles(theme, styles);
77 | expect(stylesResult).toEqual(stylesExpect);
78 | });
79 |
80 | test("Should stretch odd array of 5 values", () => {
81 | const styles = {
82 | container: {
83 | padding: [2, 3, 4, 5, 6] // 6, 12, 24, 48, 96
84 | }
85 | };
86 |
87 | const stylesExpect = [
88 | { container: { padding: 6 } }, // -- small phone
89 | { container: { padding: 12 } }, // -- normal phone
90 | { container: { padding: 24 } }, // -- tablet
91 | { container: { padding: 48 } }, // -- normal desktop
92 | { container: { padding: 96 } } // -- large desktop
93 | ];
94 |
95 | const stylesResult = processStyles(theme, styles);
96 | expect(stylesResult).toEqual(stylesExpect);
97 | });
98 |
99 | test("Should stretch even array of 2 values", () => {
100 | const styles = {
101 | container: {
102 | padding: [2, 3] // 6, 12
103 | }
104 | };
105 |
106 | const stylesExpect = [
107 | { container: { padding: 6 } }, // -- small phone
108 | { container: { padding: 6 } }, // -- normal phone
109 | { container: { padding: 12 } }, // -- tablet
110 | { container: { padding: 12 } }, // -- normal desktop
111 | { container: { padding: 12 } } // -- large desktop
112 | ];
113 |
114 | const stylesResult = processStyles(theme, styles);
115 | expect(stylesResult).toEqual(stylesExpect);
116 | });
117 |
118 | test("Should stretch even array of 4 values", () => {
119 | const styles = {
120 | container: {
121 | padding: [2, 3, 4, 5] // 6, 12, 24, 48
122 | }
123 | };
124 |
125 | const stylesExpect = [
126 | { container: { padding: 6 } }, // -- small phone
127 | { container: { padding: 12 } }, // -- normal phone
128 | { container: { padding: 24 } }, // -- tablet
129 | { container: { padding: 48 } }, // -- normal desktop
130 | { container: { padding: 48 } } // -- large desktop
131 | ];
132 |
133 | const stylesResult = processStyles(theme, styles);
134 | expect(stylesResult).toEqual(stylesExpect);
135 | });
136 |
--------------------------------------------------------------------------------
/src/__tests__/styled.spec.web.tsx:
--------------------------------------------------------------------------------
1 | jest.mock("../utils");
2 |
3 | import React, { FC } from "react";
4 | import _ from "lodash";
5 | import $ from "jquery";
6 | import { Text, View, ViewProps, StyleProp, ViewStyle } from "react-native";
7 | import "@testing-library/jest-dom";
8 | import styled from "../styled";
9 |
10 | import { renderWithTheme, testBreakpoints } from "../testsUtils";
11 |
12 | testBreakpoints("t001 single style - no breakpoints", () => {
13 | const Hello: FC<{}> = (props) => Hello;
14 |
15 | const StyledHello = styled(Hello, {
16 | fontWeight: "bold",
17 | fontSize: 20,
18 | });
19 |
20 | const { firstChild } = renderWithTheme(StyledHello);
21 |
22 | expect(firstChild).toHaveStyle({
23 | fontWeight: "bold",
24 | fontSize: "20px",
25 | });
26 | expect(firstChild).toMatchSnapshot();
27 | });
28 |
29 | testBreakpoints(
30 | "t002 single style - with breakpoints",
31 | (breakpoint: number) => {
32 | const Hello: FC<{}> = (props) => Hello;
33 | const StyledHello = styled(Hello, {
34 | fontWeight: "bold",
35 | color: ["primary"],
36 | fontSize: [0, 2],
37 | paddingRight: [1, 3, 5],
38 | marginLeft: [1, 2, 3, 4],
39 | marginRight: [1, 1, 2, 3, 4],
40 | });
41 |
42 | const expectStyles = {
43 | fontWeight: "bold",
44 | color: "rgb(255, 0, 0)",
45 | fontSize: ["12px", "12px", "16px", "16px", "16px"][breakpoint],
46 | paddingRight: ["3px", "3px", "12px", "48px", "48px"][breakpoint],
47 | marginLeft: ["3px", "6px", "12px", "24px", "24px"][breakpoint],
48 | marginRight: ["3px", "3px", "6px", "12px", "24px"][breakpoint],
49 | };
50 |
51 | const { firstChild } = renderWithTheme(StyledHello);
52 |
53 | expect(firstChild).toHaveStyle(expectStyles);
54 | expect(firstChild).toMatchSnapshot();
55 | }
56 | );
57 |
58 | testBreakpoints(
59 | "t003 single style - should merge nested styled() styles",
60 | (breakpoint: number) => {
61 | const Comp: FC<{}> = (props) => ;
62 | const StyledComp1 = styled(Comp, { paddingRight: [1, 2] });
63 | const StyledComp2 = styled(StyledComp1, { paddingLeft: [0, 1, 2] });
64 |
65 | const expectStyles = {
66 | paddingRight: ["3px", "3px", "6px", "6px", "6px"][breakpoint],
67 | paddingLeft: ["0px", "0px", "3px", "6px", "6px"][breakpoint],
68 | paddingBottom: "5px",
69 | };
70 |
71 | const { firstChild } = renderWithTheme(StyledComp2, {
72 | style: { paddingBottom: 5 },
73 | });
74 | expect(firstChild).toHaveStyle(expectStyles);
75 | expect(firstChild).toMatchSnapshot();
76 | }
77 | );
78 |
79 | test("t004 single style - should merge styled() and inline style and attrs", () => {
80 | const Comp: FC = ({ ...props }) => {
81 | return ;
82 | };
83 | const StyledComp1 = styled(
84 | Comp,
85 | { paddingRight: "1px" },
86 | { nativeID: "styled-comp-1-native-id" }
87 | );
88 | const StyledComp2 = styled(
89 | StyledComp1,
90 | { paddingLeft: "2px" },
91 | { nativeID: "styled-comp-2-native-id" }
92 | );
93 |
94 | const { firstChild } = renderWithTheme(() => (
95 |
96 |
97 |
101 |
105 |
106 | ));
107 | expect(firstChild).toMatchSnapshot();
108 | });
109 |
110 | testBreakpoints(
111 | "t005 single style - responsive attrs",
112 | (breakpoint: number) => {
113 | const Comp: FC<{ msg: string }> = (props) => (
114 |
115 | {props.msg}
116 |
117 | );
118 |
119 | const StyledComp = styled(Comp, {}, { msg: ["00", "01", "02", "03"] });
120 | const { firstChild } = renderWithTheme(StyledComp);
121 | const textResult = $(firstChild).text();
122 | const textExpect = ["00", "01", "02", "03", "03"][breakpoint];
123 |
124 | expect(textResult).toMatch(textExpect);
125 | }
126 | );
127 |
128 | test("t006 should merge styled attrs with inline attrs", () => {
129 | const Comp: FC<{ msg1: string; msg2: string }> = (props) => (
130 |
131 | {props.msg1}
132 | {props.msg2}
133 |
134 | );
135 |
136 | const StyledComp = styled(
137 | Comp,
138 | {},
139 | { msg1: "styled message 1 ", msg2: "styled message 2" }
140 | );
141 |
142 | // inline attrs should overwrite styled attrs
143 | const { firstChild } = renderWithTheme(StyledComp, {
144 | msg2: "inline message 2",
145 | });
146 |
147 | expect($(firstChild).text()).toMatch("styled message 1 inline message 2");
148 | });
149 |
150 | test("t007 multiple styles - no breakpoints", () => {
151 | const Comp: FC<{
152 | containerStyle?: StyleProp;
153 | innerStyle?: StyleProp;
154 | }> = (props) => (
155 |
156 |
157 |
158 | );
159 |
160 | const StyledComp = styled(Comp, {
161 | containerStyle: { paddingLeft: "5px" },
162 | innerStyle: { paddingLeft: "10px" },
163 | });
164 |
165 | const { firstChild } = renderWithTheme(StyledComp);
166 |
167 | // containerStyle
168 | expect($(firstChild).attr("style")).toMatch("padding-left: 5px;");
169 |
170 | // innerStyle
171 | expect($(firstChild).children().first().attr("style")).toMatch(
172 | "padding-left: 10px;"
173 | );
174 | });
175 |
176 | testBreakpoints(
177 | "t008 multiple styles - with breakpoints",
178 | (breakpoint: number) => {
179 | const Comp: FC<{
180 | containerStyle?: StyleProp;
181 | innerStyle?: StyleProp;
182 | }> = (props) => (
183 |
184 |
185 |
186 | );
187 |
188 | const StyledComp = styled(Comp, {
189 | containerStyle: { paddingLeft: [1, 2] },
190 | innerStyle: { paddingLeft: [3, 2, 1] },
191 | });
192 |
193 | const { debug, firstChild } = renderWithTheme(StyledComp);
194 |
195 | // containerStyle
196 | expect(firstChild).toHaveStyle({
197 | paddingLeft: ["3px", "3px", "6px", "6px", "6px"][breakpoint],
198 | });
199 | expect(firstChild).toMatchSnapshot();
200 |
201 | const grandChild = $(firstChild).children().first()[0];
202 |
203 | // innerStyle
204 | expect(grandChild).toHaveStyle({
205 | paddingLeft: ["12px", "12px", "6px", "3px", "3px"][breakpoint],
206 | });
207 | expect(grandChild).toMatchSnapshot();
208 | }
209 | );
210 |
211 | test("t009 multiple styles - should merge nested styled() styles - no breakpoints", () => {
212 | const Comp: FC<{
213 | containerStyle?: StyleProp;
214 | innerStyle?: StyleProp;
215 | }> = (props) => {
216 | const { containerStyle, innerStyle, ...other } = props;
217 | return (
218 |
219 |
220 |
221 | );
222 | };
223 |
224 | const StyledComp1 = styled(
225 | "StyledComp1",
226 | Comp,
227 | {
228 | containerStyle: { paddingLeft: 0 },
229 | innerStyle: { paddingLeft: 1 },
230 | },
231 | { testID: "styled-comp-1" }
232 | );
233 |
234 | const StyledComp2 = styled(
235 | "StyledComp2",
236 | StyledComp1,
237 | {
238 | containerStyle: { paddingRight: 2 },
239 | innerStyle: { paddingRight: 3 },
240 | },
241 | { testID: "styled-comp-2" }
242 | );
243 |
244 | const { firstChild } = renderWithTheme(() => (
245 |
246 |
247 |
248 |
249 | ));
250 |
251 | expect(firstChild).toMatchSnapshot();
252 | });
253 |
254 | testBreakpoints(
255 | "t010 multiple styles - should merge nested styled() styles - with breakpoints",
256 | () => {
257 | const Comp: FC<{
258 | containerStyle?: StyleProp;
259 | innerStyle?: StyleProp;
260 | }> = (props) => {
261 | const { containerStyle, innerStyle, ...other } = props;
262 | return (
263 |
264 |
265 |
266 | );
267 | };
268 |
269 | const StyledComp1 = styled(
270 | Comp,
271 | {
272 | containerStyle: { paddingLeft: [0, 1] },
273 | innerStyle: { paddingLeft: [1, 2] },
274 | },
275 | { testID: "styled-comp-1" }
276 | );
277 |
278 | const StyledComp2 = styled(
279 | StyledComp1,
280 | {
281 | containerStyle: { paddingRight: [0, 1, 2, 3, 4] },
282 | innerStyle: { paddingRight: ["0px", "1px", "2px", "3px", "4px"] },
283 | },
284 | { testID: "styled-comp-2" }
285 | );
286 |
287 | const { firstChild } = renderWithTheme(() => (
288 |
289 |
290 |
291 |
292 | ));
293 |
294 | expect(firstChild).toMatchSnapshot();
295 | }
296 | );
297 |
--------------------------------------------------------------------------------
/src/__tests__/useStyled.spec.web.tsx:
--------------------------------------------------------------------------------
1 | jest.mock("../utils");
2 |
3 | import React from "react";
4 | import { Text, View } from "react-native";
5 | import "@testing-library/jest-dom";
6 | import useStyled from "../useStyled";
7 | import { renderWithTheme, testBreakpoints } from "../testsUtils";
8 |
9 | test("t001 single style - no breakpoints", () => {
10 | const Comp = () => {
11 | const { style: singleStyle } = useStyled({
12 | style: {
13 | color: "red",
14 | },
15 | });
16 | return (
17 |
18 | My color should be red
19 |
20 | );
21 | };
22 |
23 | const { firstChild } = renderWithTheme(Comp);
24 | expect(firstChild).toMatchSnapshot();
25 | });
26 |
27 | testBreakpoints("t002 single style - with breakpoints", () => {
28 | const Comp = () => {
29 | const { style: singleStyle } = useStyled({
30 | style: {
31 | color: ["red", "blue"],
32 | },
33 | });
34 | return (
35 |
36 | {`My color should be ${singleStyle.color}`}
39 |
40 | );
41 | };
42 |
43 | const { firstChild } = renderWithTheme(Comp);
44 | expect(firstChild).toMatchSnapshot();
45 | });
46 |
47 | testBreakpoints(
48 | "t003 single style, multiple styles and attrs - with breakpoints",
49 | () => {
50 | const Comp = () => {
51 | const { style: singleStyle, styles: style, attrs: attr } = useStyled({
52 | style: {
53 | color: ["red", "blue"],
54 | paddingRight: "7px",
55 | },
56 | styles: {
57 | containerStyle: {
58 | marginLeft: [0, 1, 2],
59 | paddingTop: "11px",
60 | },
61 | },
62 | attrs: {
63 | colorName: ["RED", "BLUE"],
64 | message: "My color should be",
65 | },
66 | });
67 | return (
68 |
69 | {`${attr.message} ${attr.colorName}`}
70 |
71 | );
72 | };
73 |
74 | const { firstChild } = renderWithTheme(Comp);
75 | expect(firstChild).toMatchSnapshot();
76 | }
77 | );
78 |
--------------------------------------------------------------------------------
/src/__type_tests__/index.d.ts:
--------------------------------------------------------------------------------
1 | // TypeScript Version: 4.0
2 |
--------------------------------------------------------------------------------
/src/__type_tests__/misc.typeTest.tsx:
--------------------------------------------------------------------------------
1 | import {
2 | ReflectStyle,
3 | ReflectStyles,
4 | ReactNativeStyles,
5 | ReactNativeStyle,
6 | } from "react-native-reflect";
7 |
8 | const styles: ReactNativeStyles = {
9 | container: {
10 | padding: 10,
11 | },
12 | title: {
13 | color: "red",
14 | },
15 | };
16 |
17 | const style: ReactNativeStyle = {
18 | padding: 10,
19 | backgroundColor: "blue",
20 | };
21 |
22 | const reflectStyle: ReflectStyle = {
23 | padding: [0, 1, 3],
24 | color: "primary",
25 | fontWeight: 0,
26 | };
27 |
28 | const reflectStyles: ReflectStyles = {
29 | container: {
30 | padding: [0, 1, 3],
31 | },
32 | title: {
33 | color: "primary",
34 | },
35 | };
36 |
--------------------------------------------------------------------------------
/src/__type_tests__/styled.typeTest.tsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { View } from "react-native";
3 | import { Button } from "react-native-elements";
4 | import { styled } from "react-native-reflect";
5 |
6 | // $ExpectType typeof Button
7 | const StyledSingle = styled(Button, { fontSize: 4 });
8 |
9 | // $ExpectType typeof Button
10 | const StyledSingleNamed = styled("MyButton", Button, { fontSize: 4 });
11 |
12 | // $ExpectType typeof Button
13 | const StyledSingleTsError = styled(Button, {
14 | fontSize: 4,
15 | // $ExpectError
16 | tsError: "tsError",
17 | });
18 |
19 | // $ExpectType typeof Button
20 | const StyledMultiple = styled(Button, { container: { fontSize: 4 } });
21 |
22 | // $ExpectType typeof Button
23 | const StyledMultipleNamed = styled("MyButton", Button, {
24 | container: { fontSize: 4 },
25 | });
26 |
27 | // $ExpectError
28 | const StyledMultipleTsError = styled(Button, {
29 | container: {
30 | fontSize: 4,
31 | tsError: "tsError",
32 | },
33 | });
34 |
35 | // $ExpectType typeof Button
36 | const StyledAttrs = styled(Button, { padding: [1, 2] }, { a0: "a0" });
37 | const StyledAttrsNamed = styled(
38 | "MyButton",
39 | Button,
40 | { padding: [1, 2] },
41 | { a0: "a0" }
42 | );
43 |
44 | const MyComp = () => (
45 |
46 |
47 |
48 |
55 |
56 |
57 |
58 |
65 |
66 |
67 |
68 |
69 | );
70 |
--------------------------------------------------------------------------------
/src/__type_tests__/theme.typeTest.tsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { View } from "react-native";
3 |
4 | import {
5 | os,
6 | useWindowDimensions,
7 | ThemeProvider,
8 | Theme,
9 | } from "react-native-reflect";
10 |
11 | const testTheme = () => {
12 | const invalidTheme: {
13 | breakpoints: [number, number, number, number];
14 | tsError: string;
15 | } = {
16 | breakpoints: [300, 600, 900, 1200],
17 | tsError: "tsError",
18 | };
19 |
20 | const themeTsError1: Theme = {
21 | breakpoints: [300, 600, 900, 1200],
22 | // $ExpectError
23 | tsError: "tsError",
24 | };
25 |
26 | const themeTsError2: Theme = {
27 | breakpoints: [300, 600, 900, 1200],
28 | // $ExpectError
29 | fontSizes: [10, 20, "tsError"],
30 | };
31 |
32 | const validTheme: Theme = {
33 | breakpoints: [300, 600, 900, 1200],
34 | colors: { primary: "blue", secondary: "white" },
35 | };
36 |
37 | const valid1 = (
38 |
39 |
40 |
41 | );
42 |
43 | // $ExpectError
44 | const tsError1 = ;
45 |
46 | const tsError2 = (
47 |
54 | );
55 | };
56 |
--------------------------------------------------------------------------------
/src/__type_tests__/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "allowSyntheticDefaultImports": true,
4 | "noUnusedParameters": false,
5 | "noUnusedLocals": false,
6 | "jsx": "react-native",
7 | "lib": ["dom", "esnext"],
8 | "moduleResolution": "node",
9 | "noEmit": true,
10 | "skipLibCheck": true,
11 | "resolveJsonModule": true,
12 | "strict": true,
13 | "baseUrl": ".",
14 | "paths": { "react-native-reflect": ["../../src"] }
15 | }
16 | }
17 |
18 | // config taken from:
19 | // https://koerbitz.me/posts/unit-testing-typescript-types-with-dtslint.html
20 | // {
21 | // "compilerOptions": {
22 | // "target": "es5",
23 | // "module": "commonjs",
24 | // /* Strict Type-Checking Options */
25 | // "strict": true,
26 | // // "noImplicitAny": true,
27 | // // "strictNullChecks": true,
28 | // // "strictFunctionTypes": true,
29 | // // "noImplicitThis": true,
30 | // // "alwaysStrict": true,
31 | // /* Additional Checks */
32 | // /* next line commented out because we need unused vars for type tests */
33 | // // "noUnusedLocals": true,
34 | // "noUnusedParameters": true,
35 | // "noImplicitReturns": true,
36 | // "noFallthroughCasesInSwitch": true,
37 | // "lib": ["es5"],
38 | // /* dtslint needs these to operate in this directory */
39 | // "baseUrl": "."
40 | // // "paths": { "home": ["../../src"] }
41 | // }
42 | // }
43 |
--------------------------------------------------------------------------------
/src/__type_tests__/tslint.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "dtslint/dtslint.json",
3 | "rules": {
4 | "no-useless-files": false,
5 | "no-unused-locals": false,
6 | "no-unused-variable": false,
7 | "eofline": false
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/src/__type_tests__/typeTests.tsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { View } from "react-native";
3 |
4 | import { ThemeProvider, Theme } from "react-native-reflect";
5 |
6 | const invalidTheme: {
7 | breakpoints: [number, number, number, number];
8 | tsError: string;
9 | } = {
10 | breakpoints: [300, 600, 900, 1200],
11 | tsError: "tsError",
12 | };
13 |
14 | const themeTsError1: Theme = {
15 | breakpoints: [300, 600, 900, 1200],
16 | // $ExpectError
17 | tsError: "tsError",
18 | };
19 |
20 | const themeTsError2: Theme = {
21 | breakpoints: [300, 600, 900, 1200],
22 | // $ExpectError
23 | fontSizes: [10, 20, "tsError"],
24 | };
25 |
26 | const validTheme: Theme = {
27 | breakpoints: [300, 600, 900, 1200],
28 | colors: { primary: "blue", secondary: "white" },
29 | };
30 |
31 | const valid1 = (
32 |
33 |
34 |
35 | );
36 |
37 | // $ExpectError
38 | const tsError1 = ;
39 |
40 | const tsError2 = (
41 |
48 | );
49 |
--------------------------------------------------------------------------------
/src/__type_tests__/useStyled.typeTest.tsx:
--------------------------------------------------------------------------------
1 | import { useStyled } from "react-native-reflect";
2 |
3 | const { style, styles, attrs } = useStyled({
4 | style: {
5 | fontSize: [4],
6 | flex: 1,
7 | resizeMode: "cover",
8 | // $ExpectError
9 | tsError: "tsError",
10 | },
11 | styles: {
12 | container: {
13 | fontSize: [4],
14 | flex: 1,
15 | resizeMode: "cover",
16 | // $ExpectError
17 | tsError: "tsError",
18 | },
19 | },
20 | attrs: {
21 | a0: "val0",
22 | a1: [0, "1", false],
23 | },
24 | });
25 |
26 | // $ExpectType ReactNativeStyle
27 | style;
28 |
29 | // $ExpectType Record
30 | styles;
31 |
32 | // NOTE: Not sure it is possible to properly set types for attrs. For example,
33 | //
34 | // for a0: "val0", the resulting type would need to be: string
35 | //
36 | // for a1: [0, "1", false], the resulting type would need to be:
37 | // number | string | boolean
38 | //
39 | // $ExpectType { a0: any; a1: any; }
40 | attrs;
41 |
42 | // $ExpectType string | undefined
43 | style.borderBottomColor;
44 |
45 | // $ExpectType number | undefined
46 | style.fontSize;
47 |
48 | // $ExpectType number | undefined
49 | styles.container.fontSize;
50 |
51 | // $ExpectType "cover" | "contain" | "stretch" | "repeat" | "center" | undefined
52 | styles.container.resizeMode;
53 |
54 | // $ExpectError
55 | attrs.tsError;
56 |
--------------------------------------------------------------------------------
/src/__type_tests__/utils.typeTest.tsx:
--------------------------------------------------------------------------------
1 | import { os, useWindowDimensions } from "react-native-reflect";
2 |
3 | // NOTE: for some reason, ExpectType is not working for these lines, might be
4 | // version of TypeScript? Doing checks manually instead
5 | const returnVal0: string | number = os({ web: 0, native: "1" });
6 | const returnVal1: string | number | boolean = os({
7 | web: 0,
8 | ios: "1",
9 | android: false,
10 | });
11 | const returnVal2: string | number = os([0, "1"]);
12 | const returnVal3: string | number | boolean = os([0, "1", false]);
13 |
14 | os({ web: 0 }); // $ExpectError
15 | os({ web: 0, ios: 1 }); // $ExpectError
16 | os({ web: 0, android: 1 }); // $ExpectError
17 | os({ android: 1, ios: 2 }); // $ExpectError
18 | os({ web: 0, ios: 1, native: 2 }); // $ExpectError
19 | os({ web: 0, android: 1, native: 2 }); // $ExpectError
20 | os({ web: 0, ios: 1, android: 2, native: 3 }); // $ExpectError
21 | os(); // $ExpectError
22 | os([0]); // $ExpectError
23 | os([0, 1, 2, 3]); // $ExpectError
24 |
25 | // $ExpectType { width: number; height: number; aspectRatio: number; }
26 | const { width, height, aspectRatio } = useWindowDimensions();
27 |
--------------------------------------------------------------------------------
/src/index.d.ts:
--------------------------------------------------------------------------------
1 | import { os, useWindowDimensions } from "./utils";
2 | import useStyled from "./useStyled";
3 | import styled from "./styled";
4 | import { ThemeProvider, Theme } from "./theme";
5 | import {
6 | ReactNativeStyle,
7 | ReactNativeStyles,
8 | ReflectStyle,
9 | ReflectStyles,
10 | } from "./types";
11 |
12 | export {
13 | os,
14 | useWindowDimensions,
15 | useStyled,
16 | styled,
17 | Theme,
18 | ThemeProvider,
19 | defaultTheme,
20 | ReactNativeStyle,
21 | ReactNativeStyles,
22 | ReflectStyle,
23 | ReflectStyles,
24 | };
25 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | import { os, useWindowDimensions } from "./utils";
2 | import useStyled from "./useStyled";
3 | import styled from "./styled";
4 | import { ThemeContext, ThemeProvider, defaultTheme } from "./theme";
5 |
6 | export {
7 | os,
8 | useWindowDimensions,
9 | useStyled,
10 | styled,
11 | ThemeContext,
12 | ThemeProvider,
13 | defaultTheme,
14 | };
15 |
--------------------------------------------------------------------------------
/src/processAttrs.js:
--------------------------------------------------------------------------------
1 | import _ from "lodash";
2 | import { getStretchIndex } from "./utils";
3 | const processBreakpoint = (breakpoint, attrs) => {
4 | return _.mapValues(attrs, (attr) => {
5 | if (!_.isArray(attr))
6 | return attr;
7 | const stretchIndex = getStretchIndex(breakpoint, _.size(attr));
8 | if (_.isArray(attr))
9 | return attr[stretchIndex];
10 | });
11 | };
12 | /**
13 | * Returns true if at least one attr in attrs is an array
14 | */
15 | const attrsAreArrays = (attrs) => !!_.find(attrs, (attr) => _.isArray(attr));
16 | function processAttrs(breakpoints, attrs) {
17 | if (_.size(breakpoints) !== 4) {
18 | console.error("Theme breakpoints should be of length 4");
19 | return [];
20 | }
21 | // 1) all attrs are non-arrays
22 | if (!attrsAreArrays(attrs))
23 | return attrs;
24 | const processedAttrs = [];
25 | _.times(_.size(breakpoints) + 1, (breakpoint) => {
26 | // 2) some attrs are arrays, some can be non-arrays
27 | processedAttrs[breakpoint] = processBreakpoint(breakpoint, attrs);
28 | });
29 | return processedAttrs;
30 | }
31 | export default processAttrs;
32 |
--------------------------------------------------------------------------------
/src/processStyles.js:
--------------------------------------------------------------------------------
1 | import _ from "lodash";
2 | import { getStretchIndex, THEME_KEYS_BY_PROPS } from "./utils";
3 |
4 | const PROPERTY_CONFIG_NOT_IN_THEME = "PROPERTY_CONFIG_NOT_IN_THEME";
5 | const breakpointsIsValid = (breakpoints) => {
6 | if (_.size(breakpoints) !== 4) {
7 | console.error(
8 | "Theme breakpoints should be of length 4. Given:",
9 | breakpoints
10 | );
11 | return false;
12 | }
13 | return true;
14 | };
15 | /**
16 | * Get single property config from theme depending on the stylePropKey.
17 | * Ex: for "paddding" stylePropKey, it returns theme.space propertyConfig
18 | */
19 | const getThemePropVal = (stylePropKey, theme) => {
20 | const themeKey = THEME_KEYS_BY_PROPS[stylePropKey];
21 | if (!themeKey) return PROPERTY_CONFIG_NOT_IN_THEME;
22 | return theme[themeKey];
23 | };
24 |
25 | /**
26 | * Process single value of property, ex: 0, "100%", "5px", etc.
27 | */
28 | const processSingleStylePropVal = (singleStylePropVal, themePropVal) => {
29 | // non-themed value (ex: flex=1) -> return as it is
30 | if (themePropVal === PROPERTY_CONFIG_NOT_IN_THEME) return singleStylePropVal;
31 |
32 | if (_.isString(singleStylePropVal) && singleStylePropVal.match(/px/)) {
33 | // "px" suffix -> convert to number
34 | return Number(singleStylePropVal.replace("px", ""));
35 | }
36 |
37 | // theme value (ex: colors["primary"])
38 | // or literal value (ex: "100%")
39 | return themePropVal[singleStylePropVal] || singleStylePropVal;
40 | };
41 |
42 | const isValidStylePropValArray = (stylePropValArray, theme) => {
43 | const propertyValSize = _.size(stylePropValArray);
44 | if (propertyValSize > _.size(theme.breakpoints) + 1) {
45 | console.error(
46 | "propertyVal should not have more elements than theme breakpoints",
47 | stylePropValArray
48 | );
49 | return false;
50 | }
51 | return true;
52 | };
53 |
54 | /**
55 | * Process style property, for example: padding
56 | * Returns valid StyleSheet value for property.
57 | */
58 | const processStyleProp = (stylePropKey, stylePropVal, breakpoint, theme) => {
59 | const themePropVal = getThemePropVal(stylePropKey, theme);
60 |
61 | // single non-themed value (ex: flex=1) -> return as it is
62 | if (!_.isArray(stylePropVal) && themePropVal === PROPERTY_CONFIG_NOT_IN_THEME)
63 | return stylePropVal;
64 |
65 | // single themed value (ex: padding=0) -> process
66 | if (!_.isArray(stylePropVal))
67 | return processSingleStylePropVal(stylePropVal, themePropVal);
68 |
69 | // just some validation -> fail if not valid
70 | if (!isValidStylePropValArray(stylePropVal, theme)) return;
71 |
72 | const stretchIndex = getStretchIndex(
73 | breakpoint,
74 | _.size(stylePropVal),
75 | _.size(theme.breakpoints)
76 | );
77 |
78 | // array value -> process value for current breakpoint
79 | return processSingleStylePropVal(stylePropVal[stretchIndex], themePropVal);
80 | };
81 |
82 | /**
83 | * Process single style in style sheet, ex: container: { ... }
84 | */
85 | const processSingleStyle = (style, breakpoint, theme) => {
86 | return _.mapValues(style, (property, stylePropKey) => {
87 | const prop = processStyleProp(stylePropKey, property, breakpoint, theme);
88 | return prop;
89 | });
90 | };
91 |
92 | /**
93 | * Creates a new style sheet for a single breakpoing
94 | */
95 | const processBreakpoint = (oceanoStyles, breakpoint, theme) => {
96 | return _.mapValues(oceanoStyles, (style) => {
97 | return processSingleStyle(style, breakpoint, theme);
98 | });
99 | };
100 | /**
101 | * returns true if at least one property in style is an array
102 | */
103 | const styleAreArrays = (style) => !!_.find(style, (s) => _.isArray(s));
104 | /**
105 | * returns true if at least one style in styles contains an array
106 | */
107 | const stylesAreArray = (styles) => !!_.find(styles, (s) => styleAreArrays(s));
108 | function processStyles(theme, styles) {
109 | if (!breakpointsIsValid(theme.breakpoints)) return;
110 | if (!stylesAreArray(styles)) return processBreakpoint(styles, 0, theme);
111 | return _.map([0, 1, 2, 3, 4], (breakpoint) =>
112 | processBreakpoint(styles, breakpoint, theme)
113 | );
114 | }
115 |
116 | export function processStyle(theme, style) {
117 | if (!breakpointsIsValid(theme.breakpoints)) return;
118 | if (!styleAreArrays(style)) return processSingleStyle(style, 0, theme);
119 |
120 | return _.map([0, 1, 2, 3, 4], (index) =>
121 | processSingleStyle(style, index, theme)
122 | );
123 | }
124 | export default processStyles;
125 |
--------------------------------------------------------------------------------
/src/styled.d.ts:
--------------------------------------------------------------------------------
1 | import { ComponentType } from "react";
2 | import {
3 | ReflectStyle,
4 | ReflectStyles,
5 | ReactNativeStyle,
6 | ReactNativeStyles,
7 | } from "./types";
8 |
9 | /**
10 | * Use **styled()** to create theme-based, responsive components. Full
11 | * explanation with examples: [Reflect Guide /
12 | * styled()](https://sntx.github.io/react-native-reflect/#/?id=styled)
13 | */
14 | export default function styled(
15 | Component: C,
16 | style: ReflectStyle,
17 | attrs?: Record
18 | ): C;
19 | export default function styled(
20 | Component: C,
21 | style: ReflectStyles,
22 | attrs?: Record
23 | ): C;
24 | export default function styled(
25 | name: string,
26 | Component: C,
27 | style: ReflectStyle,
28 | attrs?: Record
29 | ): C;
30 | export default function styled(
31 | name: string,
32 | Component: C,
33 | style: ReflectStyles,
34 | attrs?: Record
35 | ): C;
36 |
--------------------------------------------------------------------------------
/src/styled.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import _ from "lodash";
3 | import useStyled from "./useStyled";
4 |
5 | /*
6 | * Styled for multiple styles.
7 | * For example, singleStyle = { color: "red", padding: 10 }
8 | */
9 | const styledSingle = (name, Component, singleStyle, attrs) => {
10 | const Styled = ({ children, ...props }) => {
11 | const { style: style, attrs: attr } = useStyled({
12 | style: singleStyle,
13 | attrs,
14 | });
15 |
16 | // extend styled() style with prop style, this allows creating embedded
17 | // styled() components
18 | const myStyle = { ...style, ...props.style };
19 |
20 | return (
21 |
22 | {children}
23 |
24 | );
25 | };
26 | Styled.displayName = name || "Styled";
27 | return Styled;
28 | };
29 |
30 | /*
31 | * Styled for multiple styles.
32 | * For example, styles = { containerStyle: { ... }, innerStyle: { ... } }
33 | */
34 | const styledMultiple = (name, Component, styles, attrs) => {
35 | const Styled = ({ children, ...props }) => {
36 | const { styles: style, attrs: attr } = useStyled({ styles, attrs });
37 | const myStyle = {};
38 |
39 | // extend styled() style with prop style, this allows creating embedded
40 | // styled() components
41 | _.each(style, (obj, key) => {
42 | myStyle[key] = { ...obj, ...props[key] };
43 | });
44 |
45 | return (
46 |
47 | {children}
48 |
49 | );
50 | };
51 | Styled.displayName = name || "Styled";
52 | return Styled;
53 | };
54 |
55 | /*
56 | * SINGLE style refers to a single stylesheet
57 | *
58 | * MULTIPLE style refers to multiple stylesheets. For example, in
59 | * react-native-elements, components are styled with multiple style sheets: {
60 | * containerStyle: { ... }, innerStyle: { ... }, ... }
61 | */
62 | const getStyleKind = (s) => {
63 | let kind = "SINGLE";
64 |
65 | for (const prop0 in s) {
66 | if (_.isPlainObject(s[prop0])) kind = "MULTIPLE";
67 | break;
68 | }
69 |
70 | return kind;
71 | };
72 |
73 | /**
74 | * Adds a name to the component for React Dev Tools
75 | */
76 | const styledNamed = (name, C, s, a) => {
77 | if (getStyleKind(s) === "MULTIPLE") return styledMultiple(name, C, s, a);
78 | return styledSingle(name, C, s, a);
79 | };
80 |
81 | /**
82 | * Creates a responsive themed component
83 | */
84 | export default function styled(...args) {
85 | // (1) Special Case: first argument is react-native's View component (which is a string)
86 | if (args[0] === "RCTView") {
87 | return styledNamed("", ...args);
88 | }
89 | // (2) First argument is name of component (used for React dev tools)
90 | if (_.isString(args[0])) {
91 | return styledNamed(...args);
92 | }
93 | // (3) First argument is Component
94 | return styledNamed("", ...args);
95 | }
96 |
--------------------------------------------------------------------------------
/src/testsUtils.d.ts:
--------------------------------------------------------------------------------
1 | export declare const renderWithTheme: (
2 | Comp: any,
3 | props?: any
4 | ) => {
5 | firstChild: HTMLElement;
6 | debug: any;
7 | };
8 |
9 | export declare const testBreakpoints: (title: string, cb: any) => any;
10 |
--------------------------------------------------------------------------------
/src/testsUtils.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import _ from "lodash";
3 | import { render } from "@testing-library/react";
4 | import { ThemeProvider } from "./theme";
5 | import { getWindowWidth } from "./utils";
6 |
7 | export const theme = {
8 | colors: {
9 | primary: "red",
10 | secondary: "pink",
11 | red0: "rgb(128,0,0)",
12 | red1: "rgb(189,0,0)",
13 | red2: "rgb(255,0,0)",
14 | red3: "rgb(255,122,122)",
15 | light: "rgb(255,230,200)",
16 | },
17 | breakpoints: [375, 768, 1024, 1680],
18 | borderWidths: [1, 2, 4, 8, 16],
19 | fontSizes: [12, 14, 16, 20, 24, 32, 48, 64],
20 | space: [0, 3, 6, 12, 24, 48, 96],
21 | sizes: [0, 3, 6, 12, 24, 48, 96],
22 | fonts: {
23 | sans: "system-ui, sans-serif",
24 | mono: "Menlo, monospace",
25 | },
26 | fontWeights: {
27 | normal: "normal",
28 | medium: 500,
29 | bold: "bold",
30 | },
31 | shadows: {
32 | small: "0 0 4px rgba(0, 0, 0, .125)",
33 | large: "0 0 24px rgba(0, 0, 0, .125)",
34 | },
35 | };
36 |
37 | export const renderWithTheme = (Comp, props) => {
38 | const {
39 | container: { firstChild },
40 | debug,
41 | } = render(
42 |
43 |
44 |
45 | );
46 | return { firstChild: firstChild, debug };
47 | };
48 |
49 | const breakpointWidths = [374, 767, 1023, 1679, 1681];
50 |
51 | export const testBreakpoints = (title, cb) =>
52 | _.each(breakpointWidths, (windowWidth, breakpoint) => {
53 | test(`${title}, breakpoint: ${breakpoint}, windowWidth: ${windowWidth}`, () => {
54 | getWindowWidth.mockReturnValue(windowWidth);
55 | cb(breakpoint, windowWidth);
56 | });
57 | });
58 |
--------------------------------------------------------------------------------
/src/theme.d.ts:
--------------------------------------------------------------------------------
1 | import React, { Component, FunctionComponent } from "react";
2 | import { TextStyle, ViewStyle } from "react-native";
3 | import { $PropertyType, $Values } from "utility-types";
4 |
5 | /**
6 | * The theme object is based on the System UI Theme Specification and it is
7 | * used to store design system values and scales. Full explanation with examples: [Reflect Guide / Theme](https://sntx.github.io/react-native-reflect/#/?id=theme)
8 | */
9 | export declare type Theme = {
10 | breakpoints: [number, number, number, number];
11 | colors?: Record;
12 | borderWidths?: number[];
13 | fontSizes?: number[];
14 | space?: number[];
15 | sizes?: number[];
16 | fonts?: Record;
17 | fontWeights?: Record>;
18 | lineHeights?: number[];
19 | letterSpacings?: number[];
20 | borderStyles?: Record>;
21 | radii?: number[];
22 | zIndices?: number[];
23 | };
24 |
25 | /**
26 | * The ThemeProvider component is used to set a global theme. Full explanation with examples: [Reflect Guide / ThemeProvider](https://sntx.github.io/react-native-reflect/#/?id=themeprovider)
27 | */
28 | export declare const ThemeProvider: FunctionComponent<{ value: Theme }>;
29 |
--------------------------------------------------------------------------------
/src/theme.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | export const defaultTheme = {
3 | colors: {
4 | primary: "#007BFF",
5 | secondary: "#6C757D",
6 | success: "#27A744",
7 | error: "#DD3446",
8 | warning: "#FFC106",
9 | red0: "rgb(128,0,0)",
10 | red1: "rgb(189,0,0)",
11 | red2: "rgb(255,0,0)",
12 | red3: "rgb(255,122,122)",
13 | light: "rgb(255,230,200)",
14 | turq0: "#e5fff9",
15 | turq1: "#a3ffea",
16 | turq2: "#a3ffeb",
17 | },
18 | breakpoints: [375, 768, 1024, 1680],
19 | borderWidths: [1, 2, 4, 8],
20 | fontSizes: [12, 14, 16, 20, 24, 32, 48, 64],
21 | space: [0, 3, 6, 12, 24, 48, 96],
22 | sizes: [0, 3, 6, 12, 24, 48, 96],
23 | fonts: {
24 | sans: "system-ui, sans-serif",
25 | mono: "Menlo, monospace",
26 | },
27 | fontWeights: {
28 | normal: "normal",
29 | medium: "500",
30 | bold: "bold",
31 | },
32 | };
33 | export const ThemeContext = React.createContext(defaultTheme);
34 | export const ThemeProvider = ThemeContext.Provider;
35 |
--------------------------------------------------------------------------------
/src/types.d.ts:
--------------------------------------------------------------------------------
1 | import { ViewStyle, TextStyle, ImageStyle } from "react-native";
2 |
3 | /**
4 | * Single react native style sheet, for example:
5 | *
6 | * ```typescript
7 | * const style: ReactNativeStyle = {
8 | * padding: 10,
9 | * backgroundColor: "blue",
10 | * };
11 | * ```
12 | */
13 | export type ReactNativeStyle = ViewStyle & TextStyle & ImageStyle;
14 |
15 | /**
16 | * Multiple React Native style sheets, for example:
17 | *
18 | * ```typescript
19 | * const styles: ReactNativeStyles = {
20 | * container: {
21 | * padding: 10
22 | * },
23 | * title: {
24 | * color: "red"
25 | * }
26 | * }
27 | * ```
28 | */
29 | export type ReactNativeStyles = Record;
30 |
31 | /**
32 | * Single Reflect style sheet.
33 | *
34 | * A Reflect style sheet follows the same structure of a React Native style
35 | * sheet. However, values are extended with arrays, for responsive behaviour
36 | * and with themed values for theme-based behaviour. For example:
37 | *
38 | * ```typescript
39 | * const reflectStyle : ReflectStyle = {
40 | * padding: [0, 1, 3], // responive values
41 | * color: "primary" // themed value
42 | * }
43 | * ```
44 | */
45 | export type ReflectStyle = {
46 | [P in keyof ReactNativeStyle]:
47 | | (string | number | ReactNativeStyle[P])
48 | | (string | number | ReactNativeStyle[P])[];
49 | };
50 |
51 | /**
52 | * Multiple Reflect style sheets.
53 | *
54 | * A Reflect style sheet follows the same structure of a React Native style
55 | * sheet. However, values are extended with arrays, for responsive behaviour
56 | * and with themed values for theme-based behaviour. For example:
57 | *
58 | * ```typescript
59 | * const reflectStyles : ReflectStyles = {
60 | * container : {
61 | * padding: [0, 1, 3], // responive values
62 | * },
63 | * title : {
64 | * color: "primary" // themed value
65 | * }
66 | * }
67 | * ```
68 | */
69 | export type ReflectStyles = Record;
70 |
--------------------------------------------------------------------------------
/src/types.js:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sntx/react-native-reflect/41a6f45ffcfb067ebc104ad0a3c0947abcabea7f/src/types.js
--------------------------------------------------------------------------------
/src/useStyled.d.ts:
--------------------------------------------------------------------------------
1 | import {
2 | ReflectStyle,
3 | ReflectStyles,
4 | ReactNativeStyle,
5 | ReactNativeStyles,
6 | } from "./types";
7 |
8 | /**
9 | * React hook for creating responsive, theme-based style sheets and props. Full
10 | * explanation with examples: [Reflect Guide /
11 | * useStyled()](https://sntx.github.io/react-native-reflect/#/?id=usestyled)
12 | */
13 | export default function useStyled>({
14 | style,
15 | styles,
16 | attrs,
17 | }: {
18 | style?: ReflectStyle;
19 | styles?: ReflectStyles;
20 | attrs?: A;
21 | }): {
22 | style: ReactNativeStyle;
23 | styles: ReactNativeStyles;
24 | attrs: { [P in keyof A]: any };
25 | };
26 |
--------------------------------------------------------------------------------
/src/useStyled.js:
--------------------------------------------------------------------------------
1 | import { useState, useMemo, useContext, useLayoutEffect } from "react";
2 | import { Dimensions } from "react-native";
3 | import _ from "lodash";
4 |
5 | import processAttrs from "./processAttrs";
6 | import processStyles, { processStyle } from "./processStyles";
7 | import { ThemeContext } from "./theme";
8 | import { getWindowWidth } from "./utils";
9 |
10 | const getBreakpoint = (breakpoints) => {
11 | const width = getWindowWidth();
12 |
13 | if (width < breakpoints[0]) return 0;
14 | if (width < breakpoints[1]) return 1;
15 | if (width < breakpoints[2]) return 2;
16 | if (width < breakpoints[3]) return 3;
17 |
18 | return 4;
19 | };
20 |
21 | function arrayOrVal(x, index) {
22 | return _.isArray(x) ? x[index] : x;
23 | }
24 |
25 | /**
26 | * React hook for responsive styles
27 | */
28 | export default ({ styles = {}, style = {}, attrs = {} }) => {
29 | const myTheme = useContext(ThemeContext);
30 | let prevBreakpoint = getBreakpoint(myTheme.breakpoints);
31 |
32 | const objs = useMemo(
33 | () => ({
34 | styles: processStyles(myTheme, styles),
35 | style: processStyle(myTheme, style),
36 | attrs: processAttrs(myTheme.breakpoints, attrs),
37 | }),
38 | [0]
39 | );
40 |
41 | const getReturnProps = (breakpoint) => ({
42 | styles: arrayOrVal(objs.styles, breakpoint),
43 | style: arrayOrVal(objs.style, breakpoint),
44 | attrs: arrayOrVal(objs.attrs, breakpoint),
45 | breakpoint,
46 | theme: myTheme,
47 | });
48 |
49 | const setIfChanged = (breakpoint) => {
50 | if (breakpoint === prevBreakpoint) return;
51 |
52 | prevBreakpoint = breakpoint;
53 | setReturnProps(getReturnProps(breakpoint));
54 | };
55 |
56 | const [returnProps, setReturnProps] = useState(
57 | getReturnProps(prevBreakpoint)
58 | );
59 |
60 | useLayoutEffect(() => {
61 | const handleWindowResize = () => {
62 | const breakpoint = getBreakpoint(myTheme.breakpoints);
63 | setIfChanged(breakpoint);
64 | };
65 |
66 | Dimensions.addEventListener("change", handleWindowResize);
67 |
68 | return () => {
69 | Dimensions.removeEventListener("change", handleWindowResize);
70 | };
71 | }, [0]);
72 |
73 | return returnProps;
74 | };
75 |
--------------------------------------------------------------------------------
/src/utils.d.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * React hook for keeping track of window's dimensions and window resize
3 | * changes.
4 | */
5 | export declare function useWindowDimensions(): {
6 | width: number;
7 | height: number;
8 | aspectRatio: number;
9 | };
10 |
11 | /**
12 | * The os() utility lets you succinctly define platform specific values. Full explanation with examples: [Reflect Guide / os()](https://sntx.github.io/react-native-reflect/#/?id=os)
13 | */
14 | export declare function os({ web, native }: { web: W; native: N }): W | N;
15 | export declare function os({
16 | web,
17 | ios,
18 | android,
19 | }: {
20 | web: W;
21 | ios: I;
22 | android: A;
23 | }): W | I | A;
24 | export declare function os(values: [W, N]): W | N;
25 | export declare function os(values: [W, I, A]): W | I | A;
26 |
--------------------------------------------------------------------------------
/src/utils.js:
--------------------------------------------------------------------------------
1 | import { useState, useEffect } from "react";
2 | import { Dimensions, Platform } from "react-native";
3 | import _ from "lodash";
4 | /**
5 | * Map of theme keys by style property keys
6 | *
7 | * Not available in react native styles:
8 | * - border, borderTop, borderRight, borderLeft
9 | * - boxShadow, textShadow
10 | *
11 | * List of style properties supported by react native:
12 | * https://github.com/vhpoet/react-native-styling-cheat-sheet
13 | */
14 | export const THEME_KEYS_BY_PROPS = {
15 | // space
16 | margin: "space",
17 | marginTop: "space",
18 | marginRight: "space",
19 | marginBottom: "space",
20 | marginLeft: "space",
21 | padding: "space",
22 | paddingRight: "space",
23 | paddingLeft: "space",
24 | paddingTop: "space",
25 | paddingBottom: "space",
26 | // fontSizes
27 | fontSize: "fontSizes",
28 | // colors
29 | color: "colors",
30 | backgroundColor: "colors",
31 | borderColor: "colors",
32 | // fonts
33 | fontFamily: "fonts",
34 | // fontWeights
35 | fontWeight: "fontWeights",
36 | // lineHeights
37 | lineHeight: "lineHeights",
38 | // letterSpacings
39 | letterSpacing: "letterSpacings",
40 | // sizes
41 | width: "sizes",
42 | height: "sizes",
43 | minWidth: "sizes",
44 | maxWidth: "sizes",
45 | minHeight: "sizes",
46 | maxHeight: "sizes",
47 | // borderWidths
48 | borderWidth: "borderWidths",
49 | // borderStyles
50 | borderStyle: "borderStyles",
51 | // radii
52 | borderRadius: "radii",
53 | // zIndices
54 | zIndex: "zIndices",
55 | };
56 |
57 | export const getStretchIndex = (
58 | breakpointIndex,
59 | propertyValSize,
60 | breakpointsSize = 4
61 | ) => {
62 | if (breakpointsSize !== 4) {
63 | return 0;
64 | }
65 |
66 | // breakpointIndex: 0 1 2 3 4
67 | // stretchIndex: 0 1 2 3 4
68 | if (propertyValSize === 5) return breakpointIndex;
69 |
70 | // breakpointIndex: 0
71 | // stretchIndex: 0 0 0 0 0
72 | if (propertyValSize === 1) return 0;
73 |
74 | // breakpointIndex: 0 1 2 3
75 | // stretchIndex: 0 1 2 3 3
76 | if (propertyValSize === 4) {
77 | if (breakpointIndex === 4) return 3;
78 | return breakpointIndex;
79 | }
80 |
81 | // breakpointIndex: 0 1 2
82 | // stretchIndex: 0 0 1 2 2
83 | //
84 | // breakpointIndex: 0 1
85 | // stretchIndex: 0 0 1 1 1
86 | const middle = breakpointsSize / 2;
87 | if (breakpointIndex < middle) return 0;
88 | if (breakpointIndex === middle) return 1;
89 | const lastIndex = propertyValSize - 1;
90 |
91 | return lastIndex;
92 | };
93 |
94 | export const getWindowWidth = () => Dimensions.get("window").width;
95 |
96 | /**
97 | * React hook for keeping track of window's dimensions and window resize changes.
98 | */
99 | export function useWindowDimensions() {
100 | const { width, height } = Dimensions.get("window");
101 |
102 | const [dimensions, setDimensions] = useState({
103 | width,
104 | height,
105 | aspectRatio: width / height,
106 | });
107 |
108 | const onChange = ({ window: { width, height } }) => {
109 | setDimensions({ width, height, aspectRatio: width / height });
110 | };
111 |
112 | useEffect(() => {
113 | Dimensions.addEventListener("change", onChange);
114 | return () => {
115 | Dimensions.removeEventListener("change", onChange);
116 | };
117 | });
118 |
119 | return dimensions;
120 | }
121 |
122 | /**
123 | * Selects value based on platform.
124 | *
125 | * If platform is web, web prop is returned.
126 | * If platform is iOS and ios prop is supplied, ios value is returned back.
127 | * If platform is Android and android prop is supplied, android value is returned back.
128 | * If platform is Android or iOS and android and ios props are not supplied, native value is returned back.
129 | */
130 | function osObjArg({ web, ios, android, native }) {
131 | if (Platform.OS === "web") return web;
132 | if (Platform.OS === "ios" && !_.isNil(ios)) return ios;
133 | if (Platform.OS === "android" && !_.isNil(android)) return android;
134 | return native;
135 | }
136 |
137 | export function os(targets) {
138 | if (_.isPlainObject(targets)) return osObjArg(targets);
139 |
140 | if (!_.isArray(targets)) {
141 | console.error("Invalid call to os() with", targets);
142 | return targets;
143 | }
144 |
145 | if (targets.length === 2)
146 | return osObjArg({ web: targets[0], native: targets[1] });
147 |
148 | if (targets.length === 3)
149 | return osObjArg({ web: targets[0], ios: targets[1], android: targets[2] });
150 |
151 | console.error("Invalid call to os() with", targets);
152 | return targets[0];
153 | }
154 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "allowSyntheticDefaultImports": true,
4 | "jsx": "react-native",
5 | "lib": ["dom", "esnext"],
6 | "moduleResolution": "node",
7 | "noEmit": true,
8 | "skipLibCheck": true,
9 | "resolveJsonModule": true,
10 | "strict": true
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/typedoc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | includes: "./src/",
3 | exclude: [
4 | "**/lib/**/*",
5 | "**/__tests__/**/*",
6 | "**/__type_tests__/**/*",
7 | "**/examples/**/*",
8 | "**/testsUtils.d.ts",
9 | ],
10 | mode: "file",
11 | out: "docs/typedoc",
12 | readme: "none",
13 | includeDeclarations: true,
14 | excludeExternals: true,
15 | excludeNotExported: true,
16 | excludePrivate: true,
17 | };
18 |
--------------------------------------------------------------------------------