@developer_tribe/react-builder 1.2.19 → 1.2.20
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/OnboardDot/OnboardDotProps.generated.d.ts +2 -1
- package/dist/build-components/patterns.generated.d.ts +23 -8
- package/dist/index.cjs.js +3 -3
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.esm.js +1 -1
- package/dist/index.esm.js.map +1 -1
- package/dist/index.web.cjs.js +6 -6
- package/dist/index.web.cjs.js.map +1 -1
- package/dist/index.web.esm.js +3 -3
- package/dist/index.web.esm.js.map +1 -1
- package/dist/styles.css +1 -1
- package/package.json +1 -1
- package/src/assets/meta.json +1 -1
- package/src/assets/samples/carousel-sample.json +51 -51
- package/src/assets/samples/paywall-1.json +77 -77
- package/src/assets/samples/paywall-2.json +76 -76
- package/src/assets/samples/simple-1.json +13 -13
- package/src/assets/samples/simple-2.json +97 -97
- package/src/assets/samples/unmigrated-builder-1.1.1.json +25 -25
- package/src/assets/samples/unmigrated-builder1.json +1 -1
- package/src/assets/samples/unvalidated-builder1.json +15 -15
- package/src/assets/samples/unvalidated-crash1.json +4 -4
- package/src/assets/samples/vpn-onboard-1.json +100 -78
- package/src/assets/samples/vpn-onboard-2.json +97 -75
- package/src/assets/samples/vpn-onboard-3.json +103 -79
- package/src/assets/samples/vpn-onboard-4.json +103 -79
- package/src/assets/samples/vpn-onboard-5.json +139 -108
- package/src/assets/samples/vpn-onboard-6.json +100 -81
- package/src/build-components/CarouselDots/CarouselDots.tsx +112 -12
- package/src/build-components/OnboardDot/OnboardDot.tsx +74 -40
- package/src/build-components/OnboardDot/OnboardDotProps.generated.ts +2 -1
- package/src/build-components/OnboardDot/pattern.json +28 -10
- package/src/build-components/patterns.generated.ts +23 -8
- package/src/build-components/useNode.ts +20 -4
- package/src/pages/DebugJsonPage.tsx +80 -1
- package/src/styles/utilities/_carousel.scss +0 -32
- package/src/utils/analyseNodeByPatterns.ts +16 -6
- package/src/utils/novaToJson.ts +7 -3
|
@@ -20,8 +20,12 @@ function OnboardDot({ node }: OnboardDotComponentProps) {
|
|
|
20
20
|
const attributeName = node.type ?? 'OnboardDot';
|
|
21
21
|
const attributeKey = node.key ?? generatedId;
|
|
22
22
|
const attrs = node.attributes;
|
|
23
|
-
const
|
|
24
|
-
|
|
23
|
+
const stylesBag =
|
|
24
|
+
((attrs as any)?.styles as Record<string, unknown> | undefined) ??
|
|
25
|
+
((attrs as any)?.style as Record<string, unknown> | undefined) ??
|
|
26
|
+
undefined;
|
|
27
|
+
const dotType =
|
|
28
|
+
(stylesBag?.dotType as any) ?? (attrs as any)?.dotType ?? 'normal_dot';
|
|
25
29
|
const GHOST_DOT_DARK_COLOR = '#E4E5E7';
|
|
26
30
|
const GHOST_DOT_LIGHT_COLOR = '#F7F7F9';
|
|
27
31
|
const {
|
|
@@ -37,12 +41,32 @@ function OnboardDot({ node }: OnboardDotComponentProps) {
|
|
|
37
41
|
const inactiveDotColor = isDark
|
|
38
42
|
? GHOST_DOT_DARK_COLOR
|
|
39
43
|
: GHOST_DOT_LIGHT_COLOR;
|
|
40
|
-
const inactiveDotOpacity =
|
|
41
|
-
|
|
44
|
+
const inactiveDotOpacity =
|
|
45
|
+
(stylesBag?.inactive_dot_opacity as number | undefined) ??
|
|
46
|
+
(attrs as any)?.inactive_dot_opacity ??
|
|
47
|
+
0.3;
|
|
48
|
+
const inactiveDotColorOverride =
|
|
49
|
+
(stylesBag?.inactive_dot_color as string | undefined) ??
|
|
50
|
+
(attrs as any)?.inactive_dot_color;
|
|
51
|
+
const activeDotColor =
|
|
52
|
+
(stylesBag?.active_dot_color as string | undefined) ??
|
|
53
|
+
(attrs as any)?.active_dot_color;
|
|
42
54
|
const resolvedActiveDotColor = useMemo(
|
|
43
55
|
() => parseColor(activeDotColor, { theme: appConfig.theme, projectColors }),
|
|
44
56
|
[activeDotColor, appConfig.theme, projectColors],
|
|
45
57
|
);
|
|
58
|
+
const resolvedInactiveDotColor = useMemo(() => {
|
|
59
|
+
const parsed = parseColor(inactiveDotColorOverride, {
|
|
60
|
+
theme: appConfig.theme,
|
|
61
|
+
projectColors,
|
|
62
|
+
});
|
|
63
|
+
return parsed ?? inactiveDotColor;
|
|
64
|
+
}, [
|
|
65
|
+
inactiveDotColor,
|
|
66
|
+
inactiveDotColorOverride,
|
|
67
|
+
appConfig.theme,
|
|
68
|
+
projectColors,
|
|
69
|
+
]);
|
|
46
70
|
|
|
47
71
|
const extractedStyle = useExtractViewStyle(node);
|
|
48
72
|
const baseStyle = useMemo(() => {
|
|
@@ -58,27 +82,31 @@ function OnboardDot({ node }: OnboardDotComponentProps) {
|
|
|
58
82
|
baseStyle,
|
|
59
83
|
isSelected ? SELECTED_OUTLINE_STYLE : undefined,
|
|
60
84
|
);
|
|
61
|
-
const
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
85
|
+
const dotThicknessRaw =
|
|
86
|
+
(stylesBag?.dot_thickness as any) ?? (attrs as any)?.dot_thickness;
|
|
87
|
+
const dotSizeCss = useMemo((): string => {
|
|
88
|
+
const parsed = parseSize(dotThicknessRaw);
|
|
89
|
+
if (parsed === undefined) return '10px';
|
|
90
|
+
if (typeof parsed === 'number') return `${parsed}px`;
|
|
91
|
+
if (typeof parsed === 'string' && parsed.trim()) return parsed;
|
|
92
|
+
return '10px';
|
|
93
|
+
}, [dotThicknessRaw]);
|
|
94
|
+
const dotGapCss = useMemo((): string => {
|
|
95
|
+
// Prefer px math when possible; otherwise fall back to 10px/3.
|
|
96
|
+
const px =
|
|
97
|
+
typeof dotSizeCss === 'string' && dotSizeCss.trim().endsWith('px')
|
|
98
|
+
? Number.parseFloat(dotSizeCss)
|
|
99
|
+
: Number.NaN;
|
|
100
|
+
const n = Number.isFinite(px) ? px : 10;
|
|
101
|
+
return `${Math.max(0, n / 3)}px`;
|
|
102
|
+
}, [dotSizeCss]);
|
|
103
|
+
const gapValue = (style as any)?.gap ?? dotGapCss;
|
|
104
|
+
const containerStyle = useMergedStyle(style, {
|
|
105
|
+
display: 'flex',
|
|
106
|
+
flexWrap: 'wrap',
|
|
107
|
+
gap: gapValue,
|
|
108
|
+
alignItems: 'center',
|
|
109
|
+
});
|
|
82
110
|
|
|
83
111
|
const onboardApi = useContext(onboardContext);
|
|
84
112
|
const emblaApi = onboardApi?.emblaApi;
|
|
@@ -115,16 +143,12 @@ function OnboardDot({ node }: OnboardDotComponentProps) {
|
|
|
115
143
|
>
|
|
116
144
|
{scrollSnaps.map((snap, index) => {
|
|
117
145
|
const isDotSelected = selectedIndex === index;
|
|
118
|
-
const
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
dotStyles['--embla-dot-color'] = resolvedActiveDotColor;
|
|
125
|
-
} else if (!isDotSelected) {
|
|
126
|
-
dotStyles['--embla-dot-color'] = inactiveDotColor;
|
|
127
|
-
}
|
|
146
|
+
const resolvedColor =
|
|
147
|
+
isDotSelected && resolvedActiveDotColor
|
|
148
|
+
? resolvedActiveDotColor
|
|
149
|
+
: resolvedInactiveDotColor;
|
|
150
|
+
const activeFallback = '#007AFF';
|
|
151
|
+
const dotColor = resolvedColor ?? activeFallback;
|
|
128
152
|
|
|
129
153
|
return (
|
|
130
154
|
<button
|
|
@@ -132,13 +156,23 @@ function OnboardDot({ node }: OnboardDotComponentProps) {
|
|
|
132
156
|
onClick={() => {
|
|
133
157
|
emblaApi?.scrollTo(snap);
|
|
134
158
|
}}
|
|
135
|
-
className=
|
|
136
|
-
style={
|
|
159
|
+
className="embla__dot"
|
|
160
|
+
style={{
|
|
161
|
+
width: dotSizeCss,
|
|
162
|
+
height: dotSizeCss,
|
|
163
|
+
backgroundColor: dotColor,
|
|
164
|
+
opacity: isDotSelected ? 1 : inactiveDotOpacity,
|
|
165
|
+
borderRadius: '9999px',
|
|
166
|
+
border: 0,
|
|
167
|
+
padding: 0,
|
|
168
|
+
margin: 0,
|
|
169
|
+
display: 'inline-block',
|
|
170
|
+
cursor: 'pointer',
|
|
171
|
+
boxSizing: 'border-box',
|
|
172
|
+
}}
|
|
137
173
|
aria-label={`Go to slide ${index + 1}`}
|
|
138
174
|
aria-current={isDotSelected ? 'true' : undefined}
|
|
139
|
-
|
|
140
|
-
{/* Dot visuals are rendered via CSS (`.embla__dot:after`). */}
|
|
141
|
-
</button>
|
|
175
|
+
/>
|
|
142
176
|
);
|
|
143
177
|
})}
|
|
144
178
|
</div>
|
|
@@ -69,8 +69,9 @@ export interface OnboardDotPropsGenerated {
|
|
|
69
69
|
title?: string;
|
|
70
70
|
description?: string;
|
|
71
71
|
dotType?: DotTypeOptionType;
|
|
72
|
+
dot_thickness?: string;
|
|
72
73
|
inactive_dot_opacity?: number;
|
|
73
|
-
|
|
74
|
+
inactive_dot_color?: string;
|
|
74
75
|
active_dot_color?: string;
|
|
75
76
|
flexDirection?: never;
|
|
76
77
|
alignItems?: never;
|
|
@@ -15,18 +15,29 @@
|
|
|
15
15
|
"sliding_dot",
|
|
16
16
|
"liquid_like"
|
|
17
17
|
],
|
|
18
|
+
"dot_thickness": "size",
|
|
18
19
|
"inactive_dot_opacity": "number",
|
|
19
|
-
"
|
|
20
|
+
"inactive_dot_color": "color",
|
|
20
21
|
"active_dot_color": "color",
|
|
21
22
|
"flexDirection": "never",
|
|
22
23
|
"alignItems": "never",
|
|
23
24
|
"justifyContent": "never"
|
|
24
25
|
}
|
|
25
26
|
},
|
|
27
|
+
"defaults": {
|
|
28
|
+
"dotType": "expanding_dot",
|
|
29
|
+
"dot_thickness": 10,
|
|
30
|
+
"inactive_dot_opacity": 0.3,
|
|
31
|
+
"active_dot_color": "#007AFF",
|
|
32
|
+
"style": {
|
|
33
|
+
"flexDirection": "row",
|
|
34
|
+
"alignItems": "center",
|
|
35
|
+
"justifyContent": "center",
|
|
36
|
+
"gap": "12@s"
|
|
37
|
+
}
|
|
38
|
+
},
|
|
26
39
|
"meta": {
|
|
27
|
-
"desiredParent": [
|
|
28
|
-
">OnboardProvider"
|
|
29
|
-
],
|
|
40
|
+
"desiredParent": [">OnboardProvider"],
|
|
30
41
|
"label": "Onboard Dot",
|
|
31
42
|
"description": "Renders onboarding progress dots.",
|
|
32
43
|
"styles": {
|
|
@@ -42,21 +53,28 @@
|
|
|
42
53
|
"description": "Opacity for inactive dots.",
|
|
43
54
|
"category": "style",
|
|
44
55
|
"specialCategory": null,
|
|
45
|
-
"sort":
|
|
56
|
+
"sort": 3
|
|
46
57
|
},
|
|
47
|
-
"
|
|
48
|
-
"label": "
|
|
49
|
-
"description": "
|
|
58
|
+
"inactive_dot_color": {
|
|
59
|
+
"label": "Inactive Dot Color",
|
|
60
|
+
"description": "Color of inactive dots.",
|
|
50
61
|
"category": "style",
|
|
51
62
|
"specialCategory": null,
|
|
52
|
-
"sort":
|
|
63
|
+
"sort": 4
|
|
64
|
+
},
|
|
65
|
+
"dot_thickness": {
|
|
66
|
+
"label": "Dot Thickness",
|
|
67
|
+
"description": "Dot size/diameter.",
|
|
68
|
+
"category": "style",
|
|
69
|
+
"specialCategory": null,
|
|
70
|
+
"sort": 2
|
|
53
71
|
},
|
|
54
72
|
"active_dot_color": {
|
|
55
73
|
"label": "Active Dot Color",
|
|
56
74
|
"description": "Color of the active dot.",
|
|
57
75
|
"category": "style",
|
|
58
76
|
"specialCategory": null,
|
|
59
|
-
"sort":
|
|
77
|
+
"sort": 5
|
|
60
78
|
}
|
|
61
79
|
}
|
|
62
80
|
}
|
|
@@ -6103,8 +6103,9 @@ export const patterns = [
|
|
|
6103
6103
|
'sliding_dot',
|
|
6104
6104
|
'liquid_like',
|
|
6105
6105
|
],
|
|
6106
|
+
dot_thickness: 'size',
|
|
6106
6107
|
inactive_dot_opacity: 'number',
|
|
6107
|
-
|
|
6108
|
+
inactive_dot_color: 'color',
|
|
6108
6109
|
active_dot_color: 'color',
|
|
6109
6110
|
flexDirection: 'never',
|
|
6110
6111
|
alignItems: 'never',
|
|
@@ -6169,21 +6170,28 @@ export const patterns = [
|
|
|
6169
6170
|
description: 'Opacity for inactive dots.',
|
|
6170
6171
|
category: 'style',
|
|
6171
6172
|
specialCategory: null,
|
|
6172
|
-
sort:
|
|
6173
|
+
sort: 3,
|
|
6173
6174
|
},
|
|
6174
|
-
|
|
6175
|
-
label: '
|
|
6176
|
-
description: '
|
|
6175
|
+
inactive_dot_color: {
|
|
6176
|
+
label: 'Inactive Dot Color',
|
|
6177
|
+
description: 'Color of inactive dots.',
|
|
6177
6178
|
category: 'style',
|
|
6178
6179
|
specialCategory: null,
|
|
6179
|
-
sort:
|
|
6180
|
+
sort: 4,
|
|
6181
|
+
},
|
|
6182
|
+
dot_thickness: {
|
|
6183
|
+
label: 'Dot Thickness',
|
|
6184
|
+
description: 'Dot size/diameter.',
|
|
6185
|
+
category: 'style',
|
|
6186
|
+
specialCategory: null,
|
|
6187
|
+
sort: 2,
|
|
6180
6188
|
},
|
|
6181
6189
|
active_dot_color: {
|
|
6182
6190
|
label: 'Active Dot Color',
|
|
6183
6191
|
description: 'Color of the active dot.',
|
|
6184
6192
|
category: 'style',
|
|
6185
6193
|
specialCategory: null,
|
|
6186
|
-
sort:
|
|
6194
|
+
sort: 5,
|
|
6187
6195
|
},
|
|
6188
6196
|
},
|
|
6189
6197
|
attributes: {
|
|
@@ -6440,13 +6448,20 @@ export const patterns = [
|
|
|
6440
6448
|
},
|
|
6441
6449
|
defaults: {
|
|
6442
6450
|
style: {
|
|
6443
|
-
flexDirection: '
|
|
6451
|
+
flexDirection: 'row',
|
|
6444
6452
|
position: 'relative',
|
|
6445
6453
|
zIndex: 1,
|
|
6446
6454
|
alignSelf: 'flex-start',
|
|
6447
6455
|
flexGrow: 0,
|
|
6448
6456
|
flexShrink: 0,
|
|
6457
|
+
alignItems: 'center',
|
|
6458
|
+
justifyContent: 'center',
|
|
6459
|
+
gap: '12@s',
|
|
6449
6460
|
},
|
|
6461
|
+
dotType: 'expanding_dot',
|
|
6462
|
+
dot_thickness: 10,
|
|
6463
|
+
inactive_dot_opacity: 0.3,
|
|
6464
|
+
active_dot_color: '#007AFF',
|
|
6450
6465
|
},
|
|
6451
6466
|
types: {},
|
|
6452
6467
|
},
|
|
@@ -9,18 +9,33 @@ export default function useNode<
|
|
|
9
9
|
if (!defaults) return node;
|
|
10
10
|
const nodeAttributes = ((node.attributes as T) ?? ({} as T)) as T & {
|
|
11
11
|
style?: Record<string, unknown>;
|
|
12
|
+
styles?: Record<string, unknown>;
|
|
12
13
|
};
|
|
13
14
|
const defaultAttributes = defaults as T as T & {
|
|
14
15
|
style?: Record<string, unknown>;
|
|
16
|
+
styles?: Record<string, unknown>;
|
|
17
|
+
};
|
|
18
|
+
// Merge style from both defaults.style and defaults.styles (for schemaVersion=2 compatibility)
|
|
19
|
+
const defaultStyle = {
|
|
20
|
+
...(defaultAttributes?.styles ?? {}),
|
|
21
|
+
...(defaultAttributes?.style ?? {}),
|
|
22
|
+
};
|
|
23
|
+
// Merge node style from both node.attributes.style and node.attributes.styles
|
|
24
|
+
const nodeStyle = {
|
|
25
|
+
...(nodeAttributes?.styles ?? {}),
|
|
26
|
+
...(nodeAttributes?.style ?? {}),
|
|
27
|
+
};
|
|
28
|
+
const mergedStyle = {
|
|
29
|
+
...defaultStyle,
|
|
30
|
+
...nodeStyle,
|
|
15
31
|
};
|
|
16
32
|
const mergedAttributes: T = {
|
|
17
33
|
...(defaultAttributes as T),
|
|
18
34
|
...(nodeAttributes as T),
|
|
19
35
|
// Deep merge `style` so default style values aren't lost when the node provides partial style overrides.
|
|
20
|
-
style
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
},
|
|
36
|
+
// Keep both `style` (for runtime back-compat) and `styles` (for editor schemaVersion=2) in sync.
|
|
37
|
+
style: mergedStyle,
|
|
38
|
+
styles: mergedStyle,
|
|
24
39
|
} as T;
|
|
25
40
|
if (
|
|
26
41
|
mergedAttributes &&
|
|
@@ -29,6 +44,7 @@ export default function useNode<
|
|
|
29
44
|
Object.keys((mergedAttributes as any).style).length === 0
|
|
30
45
|
) {
|
|
31
46
|
delete (mergedAttributes as any).style;
|
|
47
|
+
delete (mergedAttributes as any).styles;
|
|
32
48
|
}
|
|
33
49
|
return { ...node, attributes: mergedAttributes };
|
|
34
50
|
}
|
|
@@ -1,11 +1,17 @@
|
|
|
1
|
-
import React from 'react';
|
|
1
|
+
import React, { useMemo } from 'react';
|
|
2
2
|
import type { Node } from '../types/Node';
|
|
3
|
+
import type { NodeData, NodeDefaultAttribute } from '../types/Node';
|
|
3
4
|
import type { AppConfig } from '../types/PreviewConfig';
|
|
4
5
|
import { Checkbox } from '../components/Checkbox';
|
|
5
6
|
import { JsonTextEditor } from '../components/JsonTextEditor';
|
|
6
7
|
import { analyseAndProccess } from '../utils/analyseNode';
|
|
7
8
|
import { logRenderStore } from '../utils/logRenderStore';
|
|
8
9
|
import { useRenderStore } from '../store';
|
|
10
|
+
import {
|
|
11
|
+
isNodeArray,
|
|
12
|
+
isNodeNullOrUndefined,
|
|
13
|
+
isNodeString,
|
|
14
|
+
} from '../utils/nodeGuards';
|
|
9
15
|
|
|
10
16
|
export type DebugJsonPageProps = {
|
|
11
17
|
data: Node | null | undefined;
|
|
@@ -58,6 +64,68 @@ export function DebugJsonPage({
|
|
|
58
64
|
return value as Node;
|
|
59
65
|
};
|
|
60
66
|
|
|
67
|
+
function isPlainObject(value: unknown): value is Record<string, unknown> {
|
|
68
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function migrateStyleToStyles(node: Node): Node {
|
|
72
|
+
if (isNodeNullOrUndefined(node) || isNodeString(node)) {
|
|
73
|
+
return node;
|
|
74
|
+
}
|
|
75
|
+
if (isNodeArray(node)) {
|
|
76
|
+
const arr = node as Node[];
|
|
77
|
+
return arr.map((n) => migrateStyleToStyles(n));
|
|
78
|
+
}
|
|
79
|
+
if (!isPlainObject(node)) {
|
|
80
|
+
return node;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const record = node as unknown as NodeData<NodeDefaultAttribute>;
|
|
84
|
+
const nextChildren = migrateStyleToStyles(record.children);
|
|
85
|
+
|
|
86
|
+
if (!isPlainObject(record.attributes)) {
|
|
87
|
+
return { ...record, children: nextChildren };
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const attrs = { ...record.attributes };
|
|
91
|
+
if ('style' in attrs && isPlainObject(attrs.style)) {
|
|
92
|
+
attrs.styles = attrs.style;
|
|
93
|
+
delete attrs.style;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
return {
|
|
97
|
+
...record,
|
|
98
|
+
children: nextChildren,
|
|
99
|
+
attributes: attrs,
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
const hasStyleAttribute = useMemo(() => {
|
|
104
|
+
function checkNode(n: Node): boolean {
|
|
105
|
+
if (isNodeNullOrUndefined(n) || isNodeString(n)) return false;
|
|
106
|
+
if (isNodeArray(n)) {
|
|
107
|
+
const arr = n as Node[];
|
|
108
|
+
return arr.some(checkNode);
|
|
109
|
+
}
|
|
110
|
+
if (!isPlainObject(n)) return false;
|
|
111
|
+
const record = n as unknown as NodeData<NodeDefaultAttribute>;
|
|
112
|
+
if (isPlainObject(record.attributes) && 'style' in record.attributes) {
|
|
113
|
+
return true;
|
|
114
|
+
}
|
|
115
|
+
return checkNode(record.children);
|
|
116
|
+
}
|
|
117
|
+
return checkNode(data);
|
|
118
|
+
}, [data]);
|
|
119
|
+
|
|
120
|
+
const handleFixStyleToStyles = () => {
|
|
121
|
+
if (!data) return;
|
|
122
|
+
const migrated = migrateStyleToStyles(data);
|
|
123
|
+
// Don't call analyseAndProccess here - let the user apply changes manually
|
|
124
|
+
// This avoids validation errors for unknown attributes that may exist
|
|
125
|
+
setData(migrated as Node);
|
|
126
|
+
setCurrent(migrated as Node);
|
|
127
|
+
};
|
|
128
|
+
|
|
61
129
|
return (
|
|
62
130
|
<>
|
|
63
131
|
<div className="modal__header localication-modal__header">
|
|
@@ -87,6 +155,17 @@ export function DebugJsonPage({
|
|
|
87
155
|
Log store
|
|
88
156
|
</button>
|
|
89
157
|
|
|
158
|
+
{hasStyleAttribute ? (
|
|
159
|
+
<button
|
|
160
|
+
type="button"
|
|
161
|
+
className="editor-button"
|
|
162
|
+
title="Migrate attributes.style to attributes.styles (schemaVersion=2)"
|
|
163
|
+
onClick={handleFixStyleToStyles}
|
|
164
|
+
>
|
|
165
|
+
Fix style → styles
|
|
166
|
+
</button>
|
|
167
|
+
) : null}
|
|
168
|
+
|
|
90
169
|
{onClose ? (
|
|
91
170
|
<button
|
|
92
171
|
type="button"
|
|
@@ -78,38 +78,6 @@
|
|
|
78
78
|
height: 50px;
|
|
79
79
|
margin: 0 auto;
|
|
80
80
|
}
|
|
81
|
-
.embla__dot {
|
|
82
|
-
-webkit-tap-highlight-color: rgba(var(--text-high-contrast-rgb-value), 0.5);
|
|
83
|
-
-webkit-appearance: none;
|
|
84
|
-
appearance: none;
|
|
85
|
-
background-color: transparent;
|
|
86
|
-
--embla-dot-color: var(--detail-medium-contrast);
|
|
87
|
-
touch-action: manipulation;
|
|
88
|
-
display: inline-flex;
|
|
89
|
-
text-decoration: none;
|
|
90
|
-
cursor: pointer;
|
|
91
|
-
border: 0;
|
|
92
|
-
padding: 0;
|
|
93
|
-
margin: 0;
|
|
94
|
-
/* Keep a reasonable hit-area, but allow bigger dots to grow it. */
|
|
95
|
-
width: max(2.6rem, calc(var(--embla-dot-size, 1.4rem) + 1.2rem));
|
|
96
|
-
height: max(2.6rem, calc(var(--embla-dot-size, 1.4rem) + 1.2rem));
|
|
97
|
-
display: flex;
|
|
98
|
-
align-items: center;
|
|
99
|
-
border-radius: 50%;
|
|
100
|
-
}
|
|
101
|
-
.embla__dot:after {
|
|
102
|
-
background-color: var(--embla-dot-color);
|
|
103
|
-
width: var(--embla-dot-size, 1.4rem);
|
|
104
|
-
height: var(--embla-dot-size, 1.4rem);
|
|
105
|
-
border-radius: 50%;
|
|
106
|
-
display: flex;
|
|
107
|
-
align-items: center;
|
|
108
|
-
content: '';
|
|
109
|
-
}
|
|
110
|
-
.embla__dot--selected {
|
|
111
|
-
--embla-dot-color: var(--text-body);
|
|
112
|
-
}
|
|
113
81
|
.carousel-provider {
|
|
114
82
|
height: 100%;
|
|
115
83
|
}
|
|
@@ -387,14 +387,24 @@ function validateAttributesByPattern(
|
|
|
387
387
|
{}) as AttributeSchema;
|
|
388
388
|
const styleSchema = getStyleSubSchema(schema);
|
|
389
389
|
|
|
390
|
-
// Validate nested `attributes.style` as an object; validate any style keys that also exist in schema.
|
|
391
390
|
const maybeStyle = (attrs as Record<string, unknown>).style;
|
|
391
|
+
const maybeStyles = (attrs as Record<string, unknown>).styles;
|
|
392
|
+
|
|
393
|
+
// schemaVersion=2 requires `attributes.styles` (plural), not `attributes.style` (singular)
|
|
392
394
|
if (maybeStyle != null) {
|
|
393
|
-
|
|
394
|
-
|
|
395
|
+
return fail(
|
|
396
|
+
`Legacy attribute "style" detected. Use "styles" instead (schemaVersion=2). Found at ${joinPath(path, 'style')}`,
|
|
397
|
+
joinPath(path, 'style'),
|
|
398
|
+
);
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
// Validate nested `attributes.styles` (canonical store for AttributesEditor).
|
|
402
|
+
if (maybeStyles != null) {
|
|
403
|
+
if (!isPlainObject(maybeStyles)) {
|
|
404
|
+
return fail(`styles must be an object`, joinPath(path, 'styles'));
|
|
395
405
|
}
|
|
396
406
|
for (const [styleKey, styleValue] of Object.entries(
|
|
397
|
-
|
|
407
|
+
maybeStyles as Record<string, unknown>,
|
|
398
408
|
)) {
|
|
399
409
|
const spec = (styleSchema?.[styleKey] ?? schema?.[styleKey]) as
|
|
400
410
|
| AttributeTypeSpec
|
|
@@ -404,14 +414,14 @@ function validateAttributesByPattern(
|
|
|
404
414
|
pattern.pattern.type,
|
|
405
415
|
styleValue,
|
|
406
416
|
spec,
|
|
407
|
-
joinPath(joinPath(path, '
|
|
417
|
+
joinPath(joinPath(path, 'styles'), styleKey),
|
|
408
418
|
);
|
|
409
419
|
if (!res.valid) return res;
|
|
410
420
|
}
|
|
411
421
|
}
|
|
412
422
|
|
|
413
423
|
for (const [attrName, attrValue] of Object.entries(attrs)) {
|
|
414
|
-
if (attrName === 'style') continue;
|
|
424
|
+
if (attrName === 'style' || attrName === 'styles') continue;
|
|
415
425
|
const attrSpec = schema?.[attrName] as AttributeTypeSpec | undefined;
|
|
416
426
|
if (!attrSpec) {
|
|
417
427
|
if (attrName === 'title' || attrName === 'description') {
|
package/src/utils/novaToJson.ts
CHANGED
|
@@ -474,7 +474,7 @@ function mapDotsFromGeneralComponents(generalComponents: any[]): Node | null {
|
|
|
474
474
|
};
|
|
475
475
|
|
|
476
476
|
const inactiveDotOpacity = parseNumber(dotAttrs?.inactive_dot_opacity);
|
|
477
|
-
const
|
|
477
|
+
const dotThickness = parseNumber(dotAttrs?.dot_thickness);
|
|
478
478
|
const dot_style =
|
|
479
479
|
typeof dotAttrs?.dot_style === 'string'
|
|
480
480
|
? (dotAttrs.dot_style as string)
|
|
@@ -487,16 +487,20 @@ function mapDotsFromGeneralComponents(generalComponents: any[]): Node | null {
|
|
|
487
487
|
typeof dotAttrs?.active_dot_color === 'string'
|
|
488
488
|
? (dotAttrs.active_dot_color as string)
|
|
489
489
|
: undefined;
|
|
490
|
+
const inactive_dot_color =
|
|
491
|
+
typeof dotAttrs?.inactive_dot_color === 'string'
|
|
492
|
+
? (dotAttrs.inactive_dot_color as string)
|
|
493
|
+
: undefined;
|
|
490
494
|
|
|
491
495
|
const attributes: Record<string, unknown> = {};
|
|
492
496
|
if (dotType) attributes.dotType = dotType;
|
|
493
497
|
if (inactiveDotOpacity !== undefined)
|
|
494
498
|
attributes.inactive_dot_opacity = inactiveDotOpacity;
|
|
495
|
-
if (
|
|
496
|
-
attributes.expanding_dot_width = expandingDotWidth;
|
|
499
|
+
if (dotThickness !== undefined) attributes.dot_thickness = dotThickness;
|
|
497
500
|
if (dot_style) attributes.dot_style = dot_style;
|
|
498
501
|
if (container_style) attributes.container_style = container_style;
|
|
499
502
|
if (active_dot_color) attributes.active_dot_color = active_dot_color;
|
|
503
|
+
if (inactive_dot_color) attributes.inactive_dot_color = inactive_dot_color;
|
|
500
504
|
return {
|
|
501
505
|
type: 'OnboardDot',
|
|
502
506
|
attributes: Object.keys(attributes).length ? attributes : undefined,
|