Skip to content

Commit d066ad1

Browse files
add react-native inline-style styling support
1 parent 77576b4 commit d066ad1

13 files changed

Lines changed: 283 additions & 5 deletions

File tree

editor/components/codeui-code-options-control/code-options-control.tsx

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,11 @@ export function CodeOptionsControl(props: CodeOptionsControlProps) {
7272
value: "reactnative_with_styled_components",
7373
description: "with styled-components",
7474
},
75+
{
76+
name: "React Native",
77+
value: "reactnative_with_inline_style",
78+
description: "with inline-style",
79+
},
7580
{
7681
name: "Flutter",
7782
value: "flutter_default",

editor/components/codeui-code-options-control/framework-options.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,11 @@ export const reactnative_presets = {
8585
language: Language.tsx,
8686
styling: "styled-components",
8787
},
88+
reactnative_with_inline_style: <ReactNativeOption>{
89+
framework: Framework.reactnative,
90+
language: Language.tsx,
91+
styling: "inline-style",
92+
},
8893
};
8994

9095
export const flutter_presets = {
@@ -129,6 +134,8 @@ export const all_preset_options_map__prod = {
129134
reactnative_default: reactnative_presets.reactnative_default,
130135
reactnative_with_styled_components:
131136
reactnative_presets.reactnative_with_styled_components,
137+
reactnative_with_inline_style:
138+
reactnative_presets.reactnative_with_inline_style,
132139
vanilla_default: vanilla_presets.vanilla_default,
133140
// react_with_css // NOT ON PRODUCTION
134141
};

editor/query/to-code-options-from-query.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,8 @@ export function get_framework_config(framework: string) {
4747
return reactnative_presets.reactnative_with_style_sheet;
4848
case "react-native-with-styled-components":
4949
return reactnative_presets.reactnative_with_styled_components;
50+
case "react-native-with-inline-style":
51+
return reactnative_presets.reactnative_with_inline_style;
5052
case "flutter":
5153
case "flutter_default":
5254
case "flutter-default":

editor/scaffolds/code/index.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,9 @@ export function CodeSegment() {
146146
"react-native-with-styled-components"
147147
);
148148
break;
149+
case "inline-style":
150+
c = get_framework_config("react-native-with-inline-style");
151+
break;
149152
}
150153
break;
151154
}

packages/builder-config-preset/index.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,16 @@ export const reactnative_presets = {
9999
},
100100
component_declaration_style: _react_component_declaration_style,
101101
},
102+
reactnative_with_inline_style: <config.ReactNativeFrameworkConfig>{
103+
framework: Framework.reactnative,
104+
language: Language.tsx,
105+
svg: null, // TODO:
106+
gradient: null, // TODO:
107+
styling: {
108+
type: "inline-style",
109+
},
110+
component_declaration_style: _react_component_declaration_style,
111+
},
102112
};
103113

104114
export const vanilla_presets = {

packages/builder-config/framework-reactnative/reactnative-config-styling.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
export type ReactNativeStylingStrategy =
2+
| ReactNativeInlineStyleConfig
23
| ReactNativeStyleSheetConfig
34
| ReactNativeStyledComponentsConfig;
45

@@ -13,6 +14,19 @@ export interface ReactNativeStyleSheetConfig {
1314
module: "react-native";
1415
}
1516

17+
/**
18+
* Inline css styling for react-native without using StyleSheet
19+
*
20+
* ```tsx
21+
* // examples
22+
* <View style={{backgroundColor: "black", width: 50, height: 50}}/>
23+
* <View style={box_style}/>
24+
* ```
25+
*/
26+
export interface ReactNativeInlineStyleConfig {
27+
type: "inline-style";
28+
}
29+
1630
export type ReactNativeStyledComponentsConfig =
1731
| ReactStyledComponentsNativeConfig
1832
| ReactEmotionNativeStyledConfig;

packages/builder-react-native/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,6 @@ export * from "./rn-import-specifications";
22
export * from "./rn-widgets";
33

44
// building strategies
5+
export * from "./rn-build-inline-style-widget";
56
export * from "./rn-build-stylesheet-widget";
67
export * from "./rn-build-styled-component-widget";
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import { JsxWidget } from "@web-builder/core";
2+
import {
3+
react as react_config,
4+
reactnative as reactnative_config,
5+
} from "@designto/config";
6+
import { ReactNativeInlineStyleBuilder } from "./rn-inline-style-module-builder";
7+
8+
export function finalizeReactNativeWidget_InlineStyle(
9+
entry: JsxWidget,
10+
{
11+
styling,
12+
exporting,
13+
}: {
14+
styling: reactnative_config.ReactNativeInlineStyleConfig;
15+
exporting: react_config.ReactComponentExportingCofnig;
16+
}
17+
) {
18+
const builder = new ReactNativeInlineStyleBuilder({
19+
entry,
20+
config: styling,
21+
});
22+
return builder.asExportableModule().finalize(exporting);
23+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export * from "./from-static-widget-tree";
Lines changed: 194 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,194 @@
1+
import { ReservedKeywordPlatformPresets } from "@coli.codes/naming/reserved";
2+
import {
3+
react as react_config,
4+
reactnative as reactnative_config,
5+
} from "@designto/config";
6+
import type { JsxWidget } from "@web-builder/core";
7+
import {
8+
react_imports,
9+
ReactWidgetModuleExportable,
10+
makeReactModuleFile,
11+
} from "@web-builder/react-core";
12+
import {
13+
buildJsx,
14+
getWidgetStylesConfigMap,
15+
JSXWithoutStyleElementConfig,
16+
JSXWithStyleElementConfig,
17+
WidgetStyleConfigMap,
18+
} from "@web-builder/core/builders";
19+
import {
20+
BlockStatement,
21+
Identifier,
22+
ImportDeclaration,
23+
JSXAttribute,
24+
ObjectLiteralExpression,
25+
PropertyAssignment,
26+
Return,
27+
ScopedVariableNamer,
28+
TemplateLiteral,
29+
} from "coli";
30+
import { cssToJson } from "@web-builder/styles/_utils";
31+
import { CSSProperties } from "@coli.codes/css";
32+
import { reactnative_imports } from "..";
33+
34+
/**
35+
* CSS In JS Style builder for React Framework
36+
*
37+
*
38+
* css in js is a pattern that allows you to use css as a object in jsx, to property `style`.
39+
*
40+
* ```tsx
41+
* // output be like...
42+
* <div style={{ color: "red" }}/>
43+
* ```
44+
*
45+
*/
46+
export class ReactNativeInlineStyleBuilder {
47+
private readonly entry: JsxWidget;
48+
private readonly widgetName: string;
49+
readonly config: reactnative_config.ReactNativeInlineStyleConfig;
50+
private readonly namer: ScopedVariableNamer;
51+
private readonly stylesConfigWidgetMap: WidgetStyleConfigMap;
52+
53+
constructor({
54+
entry,
55+
config,
56+
}: {
57+
entry: JsxWidget;
58+
config: reactnative_config.ReactNativeInlineStyleConfig;
59+
}) {
60+
this.entry = entry;
61+
this.widgetName = entry.key.name;
62+
this.config = config;
63+
this.namer = new ScopedVariableNamer(
64+
entry.key.id,
65+
ReservedKeywordPlatformPresets.react
66+
);
67+
this.stylesConfigWidgetMap = getWidgetStylesConfigMap(entry, {
68+
namer: this.namer,
69+
rename_tag: false,
70+
});
71+
}
72+
73+
private stylesConfig(
74+
id: string
75+
): JSXWithStyleElementConfig | JSXWithoutStyleElementConfig {
76+
return this.stylesConfigWidgetMap.get(id);
77+
}
78+
79+
private jsxBuilder(widget: JsxWidget) {
80+
return buildJsx(
81+
widget,
82+
{
83+
styledConfig: (id) => {
84+
const cfg = this.stylesConfig(id);
85+
const _default_attr = cfg.attributes;
86+
87+
const existingstyleattr = _default_attr?.find(
88+
// where style refers to react-native's jsx style attribute
89+
(a) => a.name.name === "style"
90+
);
91+
92+
let style: JSXAttribute;
93+
if (existingstyleattr) {
94+
// ignore this case. (element already with style attriibute may be svg element)
95+
// this case is not supported. (should supported if the logic changes)
96+
} else {
97+
//
98+
const styledata: CSSProperties =
99+
(cfg as JSXWithStyleElementConfig).style ?? {};
100+
const reactStyleData = cssToJson(styledata);
101+
const properties: PropertyAssignment[] = Object.keys(
102+
reactStyleData
103+
).map(
104+
(key) =>
105+
new PropertyAssignment({
106+
name: key as unknown as Identifier,
107+
initializer: new TemplateLiteral(reactStyleData[key]),
108+
})
109+
);
110+
111+
style = new JSXAttribute(
112+
"style",
113+
new BlockStatement(
114+
new ObjectLiteralExpression({
115+
properties: properties,
116+
})
117+
)
118+
);
119+
}
120+
121+
const newattributes = [
122+
...(_default_attr ?? []),
123+
//
124+
style,
125+
];
126+
127+
cfg.attributes = newattributes;
128+
129+
return cfg;
130+
},
131+
},
132+
{
133+
self_closing_if_possible: true,
134+
}
135+
);
136+
}
137+
138+
partImports() {
139+
return [
140+
react_imports.import_react_from_react,
141+
reactnative_imports.import_react_prepacked,
142+
];
143+
}
144+
145+
partBody(): BlockStatement {
146+
let jsxTree = this.jsxBuilder(this.entry);
147+
return new BlockStatement(new Return(jsxTree));
148+
}
149+
150+
asExportableModule() {
151+
const body = this.partBody();
152+
const imports = this.partImports();
153+
return new ReactNativeInlineStyleWidgetModuleExportable(this.widgetName, {
154+
body,
155+
imports,
156+
});
157+
}
158+
}
159+
160+
export class ReactNativeInlineStyleWidgetModuleExportable extends ReactWidgetModuleExportable {
161+
constructor(
162+
name,
163+
{
164+
body,
165+
imports,
166+
}: {
167+
body: BlockStatement;
168+
imports: ImportDeclaration[];
169+
}
170+
) {
171+
super({
172+
name,
173+
body,
174+
imports,
175+
});
176+
}
177+
178+
asFile({
179+
exporting,
180+
}: {
181+
exporting: react_config.ReactComponentExportingCofnig;
182+
}) {
183+
return makeReactModuleFile({
184+
name: this.name,
185+
path: "src/components",
186+
imports: this.imports,
187+
declarations: [],
188+
body: this.body,
189+
config: {
190+
exporting: exporting,
191+
},
192+
});
193+
}
194+
}

0 commit comments

Comments
 (0)