@developer_tribe/react-builder 0.1.29 → 0.1.31
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.
- package/dist/build-components/OnboardButton/OnboardButtonProps.generated.d.ts +0 -1
- package/dist/build-components/OnboardFooter/OnboardFooterProps.generated.d.ts +1 -0
- package/dist/build-components/OnboardSubtitle/OnboardSubtitleProps.generated.d.ts +1 -0
- package/dist/build-components/OnboardTitle/OnboardTitleProps.generated.d.ts +1 -0
- package/dist/build-components/Text/TextProps.generated.d.ts +1 -0
- package/dist/build-components/patterns.generated.d.ts +4 -5
- package/dist/index.cjs.js +4 -4
- package/dist/index.d.ts +4 -0
- package/dist/index.esm.js +4 -4
- package/dist/utils/extractImageStyle.d.ts +3 -0
- package/dist/utils/extractTextStyle.d.ts +3 -0
- package/dist/utils/extractViewStyle.d.ts +3 -0
- package/dist/utils/querySelector.d.ts +2 -0
- package/package.json +1 -1
- package/scripts/prebuild/prebuild.js +1 -1
- package/src/build-components/Image/Image.tsx +2 -13
- package/src/build-components/OnboardButton/OnboardButton.tsx +4 -5
- package/src/build-components/OnboardButton/OnboardButtonProps.generated.ts +0 -1
- package/src/build-components/OnboardButton/pattern.json +0 -1
- package/src/build-components/OnboardFooter/OnboardFooter.tsx +2 -29
- package/src/build-components/OnboardFooter/OnboardFooterProps.generated.ts +1 -0
- package/src/build-components/OnboardProvider/OnboardProvider.tsx +0 -1
- package/src/build-components/OnboardSubtitle/OnboardSubtitleProps.generated.ts +1 -0
- package/src/build-components/OnboardTitle/OnboardTitle.tsx +0 -1
- package/src/build-components/OnboardTitle/OnboardTitleProps.generated.ts +1 -0
- package/src/build-components/Text/Text.tsx +3 -12
- package/src/build-components/Text/TextProps.generated.ts +1 -0
- package/src/build-components/Text/pattern.json +3 -3
- package/src/build-components/View/View.tsx +2 -45
- package/src/build-components/patterns.generated.ts +4 -5
- package/src/build-components/useNode.ts +0 -1
- package/src/index.ts +4 -0
- package/src/utils/extractImageStyle.ts +24 -0
- package/src/utils/extractTextStyle.ts +109 -0
- package/src/utils/extractViewStyle.ts +44 -0
- package/src/utils/novaToJson.ts +5 -2
- package/src/utils/querySelector.ts +43 -0
package/package.json
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import type { ImageComponentProps } from './ImageProps.generated';
|
|
3
3
|
import useNode from '../useNode';
|
|
4
|
+
import { extractImageStyle } from '../../utils/extractImageStyle';
|
|
4
5
|
|
|
5
6
|
function Image({ node }: ImageComponentProps) {
|
|
6
7
|
node = useNode(node);
|
|
@@ -10,19 +11,7 @@ function Image({ node }: ImageComponentProps) {
|
|
|
10
11
|
src={node.attributes?.src}
|
|
11
12
|
width={node.attributes?.width}
|
|
12
13
|
height={node.attributes?.height}
|
|
13
|
-
style={
|
|
14
|
-
width: node.attributes?.width,
|
|
15
|
-
height: node.attributes?.height,
|
|
16
|
-
borderRadius: node.attributes?.borderRadius,
|
|
17
|
-
objectFit:
|
|
18
|
-
node.attributes?.resizeMode === 'cover'
|
|
19
|
-
? 'cover'
|
|
20
|
-
: node.attributes?.resizeMode === 'contain'
|
|
21
|
-
? 'contain'
|
|
22
|
-
: node.attributes?.resizeMode === 'stretch'
|
|
23
|
-
? 'fill'
|
|
24
|
-
: undefined,
|
|
25
|
-
}}
|
|
14
|
+
style={extractImageStyle(node)}
|
|
26
15
|
alt=""
|
|
27
16
|
/>
|
|
28
17
|
);
|
|
@@ -27,15 +27,14 @@ function OnboardButton({ node }: OnboardButtonComponentProps) {
|
|
|
27
27
|
if (e.type === 'Permission') {
|
|
28
28
|
alert(`Permission requested: ${e.permission ?? 'unknown'}`);
|
|
29
29
|
} else if (e.type === 'Navigate') {
|
|
30
|
-
|
|
31
|
-
|
|
30
|
+
const eventTargetIndex = (e as any)?.targetIndex;
|
|
31
|
+
if (typeof eventTargetIndex === 'number') {
|
|
32
|
+
emblaApi?.scrollTo(eventTargetIndex);
|
|
32
33
|
navigateHandled = true;
|
|
33
34
|
}
|
|
34
35
|
}
|
|
35
36
|
}
|
|
36
|
-
|
|
37
|
-
emblaApi?.scrollTo(node.attributes.targetIndex);
|
|
38
|
-
}
|
|
37
|
+
// Fallback: do nothing when there is no Navigate event
|
|
39
38
|
};
|
|
40
39
|
|
|
41
40
|
return (
|
|
@@ -3,6 +3,7 @@ import type { OnboardFooterComponentProps } from './OnboardFooterProps.generated
|
|
|
3
3
|
import useNode from '../useNode';
|
|
4
4
|
import { useRenderStore } from '../../store';
|
|
5
5
|
import { parseSize } from '../../size-matters';
|
|
6
|
+
import { extractTextStyle } from '../../utils/extractTextStyle';
|
|
6
7
|
|
|
7
8
|
type Segment =
|
|
8
9
|
| { type: 'text'; value: string }
|
|
@@ -94,34 +95,7 @@ function OnboardFooter({ node }: OnboardFooterComponentProps) {
|
|
|
94
95
|
key ? (localication?.[defaultLanguage ?? 'en']?.[key] ?? key) : '';
|
|
95
96
|
|
|
96
97
|
const text = t(node?.attributes?.textLocalizationKey);
|
|
97
|
-
const style: React.CSSProperties =
|
|
98
|
-
display: 'flex',
|
|
99
|
-
flexDirection: (node?.attributes?.flexDirection as any) ?? 'column',
|
|
100
|
-
gap: typeof node?.attributes?.gap === 'number' ? node.attributes.gap : 0,
|
|
101
|
-
padding:
|
|
102
|
-
typeof node?.attributes?.padding === 'number'
|
|
103
|
-
? node.attributes.padding
|
|
104
|
-
: undefined,
|
|
105
|
-
margin:
|
|
106
|
-
typeof node?.attributes?.margin === 'number'
|
|
107
|
-
? node.attributes.margin
|
|
108
|
-
: undefined,
|
|
109
|
-
backgroundColor: node?.attributes?.backgroundColor,
|
|
110
|
-
borderRadius:
|
|
111
|
-
typeof node?.attributes?.borderRadius === 'number'
|
|
112
|
-
? node.attributes.borderRadius
|
|
113
|
-
: undefined,
|
|
114
|
-
width:
|
|
115
|
-
typeof node?.attributes?.width === 'number'
|
|
116
|
-
? node.attributes.width
|
|
117
|
-
: undefined,
|
|
118
|
-
height:
|
|
119
|
-
typeof node?.attributes?.height === 'number'
|
|
120
|
-
? node.attributes.height
|
|
121
|
-
: undefined,
|
|
122
|
-
alignItems: node?.attributes?.alignItems as any,
|
|
123
|
-
justifyContent: node?.attributes?.justifyContent as any,
|
|
124
|
-
};
|
|
98
|
+
const style: React.CSSProperties = extractTextStyle(node);
|
|
125
99
|
|
|
126
100
|
const linkStyle = (color?: string): React.CSSProperties => ({
|
|
127
101
|
color,
|
|
@@ -129,7 +103,6 @@ function OnboardFooter({ node }: OnboardFooterComponentProps) {
|
|
|
129
103
|
});
|
|
130
104
|
|
|
131
105
|
const paddingHorizontal = parseSize(node?.attributes?.paddingHorizontal);
|
|
132
|
-
console.log('----', node?.attributes);
|
|
133
106
|
return (
|
|
134
107
|
<div
|
|
135
108
|
style={{
|
|
@@ -19,6 +19,7 @@ export interface OnboardFooterPropsGenerated {
|
|
|
19
19
|
| '700'
|
|
20
20
|
| '800'
|
|
21
21
|
| '900';
|
|
22
|
+
textAlign?: 'left' | 'center' | 'right' | 'justify';
|
|
22
23
|
scrollable?: boolean;
|
|
23
24
|
flexDirection?: 'row' | 'column';
|
|
24
25
|
alignItems?: 'flex-start' | 'center' | 'flex-end' | 'stretch' | 'baseline';
|
|
@@ -15,7 +15,6 @@ import useNode from '../useNode';
|
|
|
15
15
|
export const onboardContext = createContext<any>(undefined);
|
|
16
16
|
function OnboardProvider({ node }: OnboardProviderComponentProps) {
|
|
17
17
|
node = useNode(node);
|
|
18
|
-
console.log('node', node);
|
|
19
18
|
const device = useRenderStore((s) => s.device);
|
|
20
19
|
const [emblaRef, emblaApi] = useEmblaCarousel(node.attributes as any);
|
|
21
20
|
const [selectedIndex, setSelectedIndex] = useState(0);
|
|
@@ -19,6 +19,7 @@ export interface OnboardSubtitlePropsGenerated {
|
|
|
19
19
|
| '700'
|
|
20
20
|
| '800'
|
|
21
21
|
| '900';
|
|
22
|
+
textAlign?: 'left' | 'center' | 'right' | 'justify';
|
|
22
23
|
scrollable?: boolean;
|
|
23
24
|
flexDirection?: 'row' | 'column';
|
|
24
25
|
alignItems?: 'flex-start' | 'center' | 'flex-end' | 'stretch' | 'baseline';
|
|
@@ -19,6 +19,7 @@ export interface OnboardTitlePropsGenerated {
|
|
|
19
19
|
| '700'
|
|
20
20
|
| '800'
|
|
21
21
|
| '900';
|
|
22
|
+
textAlign?: 'left' | 'center' | 'right' | 'justify';
|
|
22
23
|
scrollable?: boolean;
|
|
23
24
|
flexDirection?: 'row' | 'column';
|
|
24
25
|
alignItems?: 'flex-start' | 'center' | 'flex-end' | 'stretch' | 'baseline';
|
|
@@ -2,28 +2,19 @@ import React from 'react';
|
|
|
2
2
|
import type { TextComponentProps } from './TextProps.generated';
|
|
3
3
|
import useNode from '../useNode';
|
|
4
4
|
import { useRenderStore } from '../../store';
|
|
5
|
-
import {
|
|
5
|
+
import { extractTextStyle } from '../../utils/extractTextStyle';
|
|
6
6
|
|
|
7
7
|
function Text({ node }: TextComponentProps) {
|
|
8
8
|
node = useNode(node);
|
|
9
|
-
const { screenStyle, theme } = useRenderStore((s) => ({
|
|
10
|
-
screenStyle: s.screenStyle,
|
|
11
|
-
theme: s.theme,
|
|
12
|
-
}));
|
|
13
|
-
const color =
|
|
14
|
-
theme === 'light' ? screenStyle.light.color : screenStyle.dark.color;
|
|
15
9
|
const { defaultLanguage, localication } = useRenderStore((s) => ({
|
|
16
10
|
defaultLanguage: s.defaultLanguage,
|
|
17
11
|
localication: s.localication,
|
|
18
12
|
}));
|
|
19
13
|
const keyOrText: string = node.children as string;
|
|
20
|
-
const style = (node
|
|
21
|
-
const fontSize = node.attributes?.fontSize
|
|
22
|
-
? parseSize(node.attributes.fontSize)
|
|
23
|
-
: fs(14);
|
|
14
|
+
const style = extractTextStyle(node);
|
|
24
15
|
|
|
25
16
|
return (
|
|
26
|
-
<p style={
|
|
17
|
+
<p style={style}>
|
|
27
18
|
{localication?.[defaultLanguage ?? 'en']?.[keyOrText] ?? keyOrText}
|
|
28
19
|
</p>
|
|
29
20
|
);
|
|
@@ -6,55 +6,12 @@ import type {
|
|
|
6
6
|
import RenderNode from '../RenderNode.generated';
|
|
7
7
|
import { Node } from '../../types/Node';
|
|
8
8
|
import useNode from '../useNode';
|
|
9
|
-
|
|
10
|
-
function mapAttributesToStyle(
|
|
11
|
-
attributes: ViewPropsGenerated['attributes'],
|
|
12
|
-
): React.CSSProperties {
|
|
13
|
-
const scrollable = attributes?.scrollable ?? false;
|
|
14
|
-
const style: React.CSSProperties = {
|
|
15
|
-
display: 'flex',
|
|
16
|
-
flexDirection: 'column',
|
|
17
|
-
};
|
|
18
|
-
if (!attributes) return style;
|
|
19
|
-
if (scrollable) {
|
|
20
|
-
if (attributes.flexDirection === 'row') {
|
|
21
|
-
style.overflowX = 'auto';
|
|
22
|
-
style.overflowY = 'hidden';
|
|
23
|
-
style.maxWidth = '100%';
|
|
24
|
-
style.maxHeight = '100%';
|
|
25
|
-
} else {
|
|
26
|
-
style.overflowY = 'auto';
|
|
27
|
-
style.overflowX = 'hidden';
|
|
28
|
-
style.maxHeight = '100%';
|
|
29
|
-
style.maxWidth = '100%';
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
if (attributes.flexDirection) style.flexDirection = attributes.flexDirection;
|
|
33
|
-
if (attributes.alignItems)
|
|
34
|
-
style.alignItems =
|
|
35
|
-
attributes.alignItems as React.CSSProperties['alignItems'];
|
|
36
|
-
if (attributes.justifyContent)
|
|
37
|
-
style.justifyContent =
|
|
38
|
-
attributes.justifyContent as React.CSSProperties['justifyContent'];
|
|
39
|
-
if (attributes.gap !== undefined) style.gap = attributes.gap;
|
|
40
|
-
if (attributes.padding !== undefined) style.padding = attributes.padding;
|
|
41
|
-
if (attributes.margin !== undefined) style.margin = attributes.margin;
|
|
42
|
-
if (attributes.backgroundColor)
|
|
43
|
-
style.backgroundColor = attributes.backgroundColor;
|
|
44
|
-
if (attributes.borderRadius !== undefined)
|
|
45
|
-
style.borderRadius = attributes.borderRadius;
|
|
46
|
-
if (attributes.width !== undefined) style.width = attributes.width;
|
|
47
|
-
if (attributes.height !== undefined) style.height = attributes.height;
|
|
48
|
-
return style;
|
|
49
|
-
}
|
|
9
|
+
import { extractViewStyle } from '../../utils/extractViewStyle';
|
|
50
10
|
|
|
51
11
|
export function View({ node }: ViewComponentProps) {
|
|
52
12
|
node = useNode(node);
|
|
53
13
|
return (
|
|
54
|
-
<div
|
|
55
|
-
style={mapAttributesToStyle(node.attributes ?? {})}
|
|
56
|
-
className="scroll-container"
|
|
57
|
-
>
|
|
14
|
+
<div style={extractViewStyle(node)} className="scroll-container">
|
|
58
15
|
<RenderNode node={node.children as Node} />
|
|
59
16
|
</div>
|
|
60
17
|
);
|
|
@@ -111,7 +111,6 @@ export const patterns = [
|
|
|
111
111
|
animation_color: 'string',
|
|
112
112
|
button_background_color: 'string',
|
|
113
113
|
flex: 'number',
|
|
114
|
-
targetIndex: 'number',
|
|
115
114
|
events: 'EventObject[]',
|
|
116
115
|
},
|
|
117
116
|
},
|
|
@@ -185,6 +184,7 @@ export const patterns = [
|
|
|
185
184
|
'800',
|
|
186
185
|
'900',
|
|
187
186
|
],
|
|
187
|
+
textAlign: ['left', 'center', 'right', 'justify'],
|
|
188
188
|
scrollable: 'boolean',
|
|
189
189
|
flexDirection: ['row', 'column'],
|
|
190
190
|
alignItems: ['flex-start', 'center', 'flex-end', 'stretch', 'baseline'],
|
|
@@ -223,7 +223,6 @@ export const patterns = [
|
|
|
223
223
|
linkedWordSecondColor: 'string',
|
|
224
224
|
linkedWordSecondPage: 'string',
|
|
225
225
|
},
|
|
226
|
-
textAlign: ['left', 'center', 'right', 'justify'],
|
|
227
226
|
},
|
|
228
227
|
defaults: { paddingHorizontal: '24@s' },
|
|
229
228
|
types: {},
|
|
@@ -307,6 +306,7 @@ export const patterns = [
|
|
|
307
306
|
'800',
|
|
308
307
|
'900',
|
|
309
308
|
],
|
|
309
|
+
textAlign: ['left', 'center', 'right', 'justify'],
|
|
310
310
|
scrollable: 'boolean',
|
|
311
311
|
flexDirection: ['row', 'column'],
|
|
312
312
|
alignItems: ['flex-start', 'center', 'flex-end', 'stretch', 'baseline'],
|
|
@@ -338,7 +338,6 @@ export const patterns = [
|
|
|
338
338
|
width: 'number',
|
|
339
339
|
height: 'number',
|
|
340
340
|
},
|
|
341
|
-
textAlign: ['left', 'center', 'right', 'justify'],
|
|
342
341
|
},
|
|
343
342
|
defaults: { fontSize: '14@fs', fontWeight: '600' },
|
|
344
343
|
types: {},
|
|
@@ -365,6 +364,7 @@ export const patterns = [
|
|
|
365
364
|
'800',
|
|
366
365
|
'900',
|
|
367
366
|
],
|
|
367
|
+
textAlign: ['left', 'center', 'right', 'justify'],
|
|
368
368
|
scrollable: 'boolean',
|
|
369
369
|
flexDirection: ['row', 'column'],
|
|
370
370
|
alignItems: ['flex-start', 'center', 'flex-end', 'stretch', 'baseline'],
|
|
@@ -396,7 +396,6 @@ export const patterns = [
|
|
|
396
396
|
width: 'number',
|
|
397
397
|
height: 'number',
|
|
398
398
|
},
|
|
399
|
-
textAlign: ['left', 'center', 'right', 'justify'],
|
|
400
399
|
},
|
|
401
400
|
defaults: { fontSize: '24@fs', fontWeight: '700', textAlign: 'center' },
|
|
402
401
|
types: {},
|
|
@@ -453,8 +452,8 @@ export const patterns = [
|
|
|
453
452
|
'800',
|
|
454
453
|
'900',
|
|
455
454
|
],
|
|
455
|
+
textAlign: ['left', 'center', 'right', 'justify'],
|
|
456
456
|
},
|
|
457
|
-
textAlign: ['left', 'center', 'right', 'justify'],
|
|
458
457
|
},
|
|
459
458
|
types: {},
|
|
460
459
|
defaults: {},
|
|
@@ -6,7 +6,6 @@ export default function useNode<
|
|
|
6
6
|
>(node: NodeData<T>): NodeData<T> {
|
|
7
7
|
const type = node?.type;
|
|
8
8
|
const defaults = getDefaultsForType(type) as Partial<T> | undefined;
|
|
9
|
-
console.log('defaults', type, defaults);
|
|
10
9
|
if (!defaults) return node;
|
|
11
10
|
const mergedAttributes: T = {
|
|
12
11
|
...(defaults as T),
|
package/src/index.ts
CHANGED
|
@@ -24,3 +24,7 @@ export type { Device } from './types/Device';
|
|
|
24
24
|
export { AttributesEditor };
|
|
25
25
|
export * from './build-components/index';
|
|
26
26
|
export { default as useNode } from './build-components/useNode';
|
|
27
|
+
export { querySelector } from './utils/querySelector';
|
|
28
|
+
export { extractViewStyle } from './utils/extractViewStyle';
|
|
29
|
+
export { extractImageStyle } from './utils/extractImageStyle';
|
|
30
|
+
export { extractTextStyle } from './utils/extractTextStyle';
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { ImagePropsGenerated } from '../build-components/Image/ImageProps.generated';
|
|
2
|
+
import type { NodeData } from '../types/Node';
|
|
3
|
+
|
|
4
|
+
export function extractImageStyle<T extends ImagePropsGenerated['attributes']>(
|
|
5
|
+
node: NodeData<T>,
|
|
6
|
+
) {
|
|
7
|
+
const attributes = node.attributes;
|
|
8
|
+
const style: React.CSSProperties = {};
|
|
9
|
+
|
|
10
|
+
if (!attributes) return style;
|
|
11
|
+
|
|
12
|
+
if (attributes.width !== undefined) style.width = attributes.width;
|
|
13
|
+
if (attributes.height !== undefined) style.height = attributes.height;
|
|
14
|
+
if (attributes.borderRadius !== undefined)
|
|
15
|
+
style.borderRadius = attributes.borderRadius;
|
|
16
|
+
|
|
17
|
+
// Map resizeMode to CSS object-fit
|
|
18
|
+
if (attributes.resizeMode === 'cover') style.objectFit = 'cover';
|
|
19
|
+
else if (attributes.resizeMode === 'contain') style.objectFit = 'contain';
|
|
20
|
+
else if (attributes.resizeMode === 'stretch') style.objectFit = 'fill';
|
|
21
|
+
else if (attributes.resizeMode === 'center') style.objectFit = 'none';
|
|
22
|
+
|
|
23
|
+
return style;
|
|
24
|
+
}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import type { NodeData } from '../types/Node';
|
|
2
|
+
import type { TextPropsGenerated } from '../build-components/Text/TextProps.generated';
|
|
3
|
+
import { fs, parseSize } from '../size-matters';
|
|
4
|
+
import { useRenderStore } from '../store';
|
|
5
|
+
|
|
6
|
+
export function extractTextStyle<T extends TextPropsGenerated['attributes']>(
|
|
7
|
+
node: NodeData<T>,
|
|
8
|
+
) {
|
|
9
|
+
const attributes = node.attributes;
|
|
10
|
+
|
|
11
|
+
const { screenStyle, theme } = useRenderStore.getState();
|
|
12
|
+
const fallbackColor =
|
|
13
|
+
theme === 'light' ? screenStyle.light.color : screenStyle.dark.color;
|
|
14
|
+
|
|
15
|
+
const style: React.CSSProperties = {};
|
|
16
|
+
|
|
17
|
+
if (!attributes) {
|
|
18
|
+
style.fontSize = fs(14);
|
|
19
|
+
style.color = fallbackColor;
|
|
20
|
+
return style;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// Typography
|
|
24
|
+
if (attributes.fontSize !== undefined) {
|
|
25
|
+
const parsed = parseSize(attributes.fontSize);
|
|
26
|
+
style.fontSize = parsed as React.CSSProperties['fontSize'];
|
|
27
|
+
} else {
|
|
28
|
+
style.fontSize = fs(14);
|
|
29
|
+
}
|
|
30
|
+
if (attributes.fontWeight) style.fontWeight = attributes.fontWeight;
|
|
31
|
+
style.color = attributes.color ?? fallbackColor;
|
|
32
|
+
if (attributes.textAlign)
|
|
33
|
+
style.textAlign = attributes.textAlign as React.CSSProperties['textAlign'];
|
|
34
|
+
|
|
35
|
+
// Spacing
|
|
36
|
+
// Shorthand
|
|
37
|
+
if (attributes.padding !== undefined) style.padding = attributes.padding;
|
|
38
|
+
if (attributes.margin !== undefined) style.margin = attributes.margin;
|
|
39
|
+
|
|
40
|
+
// Axis shorthands
|
|
41
|
+
if (attributes.paddingHorizontal !== undefined) {
|
|
42
|
+
const v = parseSize(attributes.paddingHorizontal);
|
|
43
|
+
style.paddingLeft = v as React.CSSProperties['paddingLeft'];
|
|
44
|
+
style.paddingRight = v as React.CSSProperties['paddingRight'];
|
|
45
|
+
}
|
|
46
|
+
if (attributes.paddingVertical !== undefined) {
|
|
47
|
+
const v = parseSize(attributes.paddingVertical);
|
|
48
|
+
style.paddingTop = v as React.CSSProperties['paddingTop'];
|
|
49
|
+
style.paddingBottom = v as React.CSSProperties['paddingBottom'];
|
|
50
|
+
}
|
|
51
|
+
if (attributes.marginHorizontal !== undefined) {
|
|
52
|
+
const v = parseSize(attributes.marginHorizontal);
|
|
53
|
+
style.marginLeft = v as React.CSSProperties['marginLeft'];
|
|
54
|
+
style.marginRight = v as React.CSSProperties['marginRight'];
|
|
55
|
+
}
|
|
56
|
+
if (attributes.marginVertical !== undefined) {
|
|
57
|
+
const v = parseSize(attributes.marginVertical);
|
|
58
|
+
style.marginTop = v as React.CSSProperties['marginTop'];
|
|
59
|
+
style.marginBottom = v as React.CSSProperties['marginBottom'];
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// Edges
|
|
63
|
+
if (attributes.paddingTop !== undefined)
|
|
64
|
+
style.paddingTop = parseSize(
|
|
65
|
+
attributes.paddingTop,
|
|
66
|
+
) as React.CSSProperties['paddingTop'];
|
|
67
|
+
if (attributes.paddingBottom !== undefined)
|
|
68
|
+
style.paddingBottom = parseSize(
|
|
69
|
+
attributes.paddingBottom,
|
|
70
|
+
) as React.CSSProperties['paddingBottom'];
|
|
71
|
+
if (attributes.paddingLeft !== undefined)
|
|
72
|
+
style.paddingLeft = parseSize(
|
|
73
|
+
attributes.paddingLeft,
|
|
74
|
+
) as React.CSSProperties['paddingLeft'];
|
|
75
|
+
if (attributes.paddingRight !== undefined)
|
|
76
|
+
style.paddingRight = parseSize(
|
|
77
|
+
attributes.paddingRight,
|
|
78
|
+
) as React.CSSProperties['paddingRight'];
|
|
79
|
+
|
|
80
|
+
if (attributes.marginTop !== undefined)
|
|
81
|
+
style.marginTop = parseSize(
|
|
82
|
+
attributes.marginTop,
|
|
83
|
+
) as React.CSSProperties['marginTop'];
|
|
84
|
+
if (attributes.marginBottom !== undefined)
|
|
85
|
+
style.marginBottom = parseSize(
|
|
86
|
+
attributes.marginBottom,
|
|
87
|
+
) as React.CSSProperties['marginBottom'];
|
|
88
|
+
if (attributes.marginLeft !== undefined)
|
|
89
|
+
style.marginLeft = parseSize(
|
|
90
|
+
attributes.marginLeft,
|
|
91
|
+
) as React.CSSProperties['marginLeft'];
|
|
92
|
+
if (attributes.marginRight !== undefined)
|
|
93
|
+
style.marginRight = parseSize(
|
|
94
|
+
attributes.marginRight,
|
|
95
|
+
) as React.CSSProperties['marginRight'];
|
|
96
|
+
|
|
97
|
+
// Decor
|
|
98
|
+
if (attributes.backgroundColor)
|
|
99
|
+
style.backgroundColor = attributes.backgroundColor;
|
|
100
|
+
if (attributes.borderRadius !== undefined)
|
|
101
|
+
style.borderRadius =
|
|
102
|
+
attributes.borderRadius as React.CSSProperties['borderRadius'];
|
|
103
|
+
|
|
104
|
+
// Dimensions (rare for text but supported by schema)
|
|
105
|
+
if (attributes.width !== undefined) style.width = attributes.width;
|
|
106
|
+
if (attributes.height !== undefined) style.height = attributes.height;
|
|
107
|
+
|
|
108
|
+
return style;
|
|
109
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { ViewPropsGenerated } from '../build-components/View/ViewProps.generated';
|
|
2
|
+
import type { NodeData } from '../types/Node';
|
|
3
|
+
|
|
4
|
+
export function extractViewStyle<T extends ViewPropsGenerated['attributes']>(
|
|
5
|
+
node: NodeData<T>,
|
|
6
|
+
) {
|
|
7
|
+
const attributes = node.attributes;
|
|
8
|
+
const scrollable = attributes?.scrollable ?? false;
|
|
9
|
+
const style: React.CSSProperties = {
|
|
10
|
+
display: 'flex',
|
|
11
|
+
flexDirection: 'column',
|
|
12
|
+
};
|
|
13
|
+
if (!attributes) return style;
|
|
14
|
+
if (scrollable) {
|
|
15
|
+
if (attributes.flexDirection === 'row') {
|
|
16
|
+
style.overflowX = 'auto';
|
|
17
|
+
style.overflowY = 'hidden';
|
|
18
|
+
style.maxWidth = '100%';
|
|
19
|
+
style.maxHeight = '100%';
|
|
20
|
+
} else {
|
|
21
|
+
style.overflowY = 'auto';
|
|
22
|
+
style.overflowX = 'hidden';
|
|
23
|
+
style.maxHeight = '100%';
|
|
24
|
+
style.maxWidth = '100%';
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
if (attributes.flexDirection) style.flexDirection = attributes.flexDirection;
|
|
28
|
+
if (attributes.alignItems)
|
|
29
|
+
style.alignItems =
|
|
30
|
+
attributes.alignItems as React.CSSProperties['alignItems'];
|
|
31
|
+
if (attributes.justifyContent)
|
|
32
|
+
style.justifyContent =
|
|
33
|
+
attributes.justifyContent as React.CSSProperties['justifyContent'];
|
|
34
|
+
if (attributes.gap !== undefined) style.gap = attributes.gap;
|
|
35
|
+
if (attributes.padding !== undefined) style.padding = attributes.padding;
|
|
36
|
+
if (attributes.margin !== undefined) style.margin = attributes.margin;
|
|
37
|
+
if (attributes.backgroundColor)
|
|
38
|
+
style.backgroundColor = attributes.backgroundColor;
|
|
39
|
+
if (attributes.borderRadius !== undefined)
|
|
40
|
+
style.borderRadius = attributes.borderRadius;
|
|
41
|
+
if (attributes.width !== undefined) style.width = attributes.width;
|
|
42
|
+
if (attributes.height !== undefined) style.height = attributes.height;
|
|
43
|
+
return style;
|
|
44
|
+
}
|
package/src/utils/novaToJson.ts
CHANGED
|
@@ -208,7 +208,7 @@ function buildCarouselItem(
|
|
|
208
208
|
const animationColor = attrs?.animation_color as string | undefined;
|
|
209
209
|
const flex = attrs?.flex ? Number(attrs.flex) : undefined;
|
|
210
210
|
|
|
211
|
-
//
|
|
211
|
+
// Normalize events and compute Navigate target indices when resolvable
|
|
212
212
|
let targetIndex: number | undefined = undefined;
|
|
213
213
|
//@eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
214
214
|
const actions = (attrs?.actions || []) as any[];
|
|
@@ -216,6 +216,7 @@ function buildCarouselItem(
|
|
|
216
216
|
type: 'Permission' | 'Navigate';
|
|
217
217
|
permission?: string;
|
|
218
218
|
navigate_to?: string | null;
|
|
219
|
+
targetIndex?: number;
|
|
219
220
|
}[] = [];
|
|
220
221
|
for (const action of actions) {
|
|
221
222
|
//@eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
@@ -241,6 +242,9 @@ function buildCarouselItem(
|
|
|
241
242
|
normalizedEvents.push({
|
|
242
243
|
type: 'Navigate',
|
|
243
244
|
navigate_to,
|
|
245
|
+
...(hasTarget
|
|
246
|
+
? { targetIndex: pageKeyToIndex.get(nextKey!)! }
|
|
247
|
+
: {}),
|
|
244
248
|
});
|
|
245
249
|
if (hasTarget) {
|
|
246
250
|
targetIndex = pageKeyToIndex.get(nextKey!)!;
|
|
@@ -264,7 +268,6 @@ function buildCarouselItem(
|
|
|
264
268
|
? { button_background_color: buttonBackgroundColor }
|
|
265
269
|
: {}),
|
|
266
270
|
...(typeof flex === 'number' ? { flex } : {}),
|
|
267
|
-
...(typeof targetIndex === 'number' ? { targetIndex } : {}),
|
|
268
271
|
...(normalizedEvents.length ? { events: normalizedEvents } : {}),
|
|
269
272
|
},
|
|
270
273
|
children: undefined as unknown as Node,
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { Node, NodeData } from '../types/Node';
|
|
2
|
+
|
|
3
|
+
function isNodeData(value: Node): value is NodeData<Record<string, unknown>> {
|
|
4
|
+
if (value === null || value === undefined) return false;
|
|
5
|
+
if (Array.isArray(value)) return false;
|
|
6
|
+
if (typeof value === 'string') return false;
|
|
7
|
+
if (typeof value !== 'object') return false;
|
|
8
|
+
return (
|
|
9
|
+
'type' in (value as Record<string, unknown>) &&
|
|
10
|
+
'children' in (value as Record<string, unknown>)
|
|
11
|
+
);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export function querySelector(node: Node, selector: string): NodeData[] {
|
|
15
|
+
if (node === null || node === undefined) return [];
|
|
16
|
+
if (!selector) return [];
|
|
17
|
+
|
|
18
|
+
const isKeySearch = selector.startsWith('#');
|
|
19
|
+
const keyToMatch = isKeySearch ? selector.slice(1) : undefined;
|
|
20
|
+
|
|
21
|
+
const matches = (n: NodeData): boolean => {
|
|
22
|
+
if (isKeySearch) return n.key === keyToMatch;
|
|
23
|
+
return n.type === selector;
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
const result: NodeData[] = [];
|
|
27
|
+
|
|
28
|
+
const visit = (current: Node) => {
|
|
29
|
+
if (current === null || current === undefined) return;
|
|
30
|
+
if (Array.isArray(current)) {
|
|
31
|
+
for (const child of current) visit(child);
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
if (typeof current === 'string') return;
|
|
35
|
+
if (isNodeData(current)) {
|
|
36
|
+
if (matches(current)) result.push(current);
|
|
37
|
+
visit(current.children);
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
visit(node);
|
|
42
|
+
return result;
|
|
43
|
+
}
|