@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.
Files changed (38) hide show
  1. package/dist/build-components/OnboardDot/OnboardDotProps.generated.d.ts +2 -1
  2. package/dist/build-components/patterns.generated.d.ts +23 -8
  3. package/dist/index.cjs.js +3 -3
  4. package/dist/index.cjs.js.map +1 -1
  5. package/dist/index.esm.js +1 -1
  6. package/dist/index.esm.js.map +1 -1
  7. package/dist/index.web.cjs.js +6 -6
  8. package/dist/index.web.cjs.js.map +1 -1
  9. package/dist/index.web.esm.js +3 -3
  10. package/dist/index.web.esm.js.map +1 -1
  11. package/dist/styles.css +1 -1
  12. package/package.json +1 -1
  13. package/src/assets/meta.json +1 -1
  14. package/src/assets/samples/carousel-sample.json +51 -51
  15. package/src/assets/samples/paywall-1.json +77 -77
  16. package/src/assets/samples/paywall-2.json +76 -76
  17. package/src/assets/samples/simple-1.json +13 -13
  18. package/src/assets/samples/simple-2.json +97 -97
  19. package/src/assets/samples/unmigrated-builder-1.1.1.json +25 -25
  20. package/src/assets/samples/unmigrated-builder1.json +1 -1
  21. package/src/assets/samples/unvalidated-builder1.json +15 -15
  22. package/src/assets/samples/unvalidated-crash1.json +4 -4
  23. package/src/assets/samples/vpn-onboard-1.json +100 -78
  24. package/src/assets/samples/vpn-onboard-2.json +97 -75
  25. package/src/assets/samples/vpn-onboard-3.json +103 -79
  26. package/src/assets/samples/vpn-onboard-4.json +103 -79
  27. package/src/assets/samples/vpn-onboard-5.json +139 -108
  28. package/src/assets/samples/vpn-onboard-6.json +100 -81
  29. package/src/build-components/CarouselDots/CarouselDots.tsx +112 -12
  30. package/src/build-components/OnboardDot/OnboardDot.tsx +74 -40
  31. package/src/build-components/OnboardDot/OnboardDotProps.generated.ts +2 -1
  32. package/src/build-components/OnboardDot/pattern.json +28 -10
  33. package/src/build-components/patterns.generated.ts +23 -8
  34. package/src/build-components/useNode.ts +20 -4
  35. package/src/pages/DebugJsonPage.tsx +80 -1
  36. package/src/styles/utilities/_carousel.scss +0 -32
  37. package/src/utils/analyseNodeByPatterns.ts +16 -6
  38. 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 styleBag = attrs?.style;
24
- const dotType = attrs?.dotType || 'normal_dot';
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 = attrs?.inactive_dot_opacity ?? 0.3;
41
- const activeDotColor = attrs?.active_dot_color;
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 expandingDotWidthRaw = attrs?.expanding_dot_width;
62
- const expandingDotWidthOverride = useMemo(() => {
63
- const parsed = parseSize(expandingDotWidthRaw);
64
- if (parsed === undefined) return undefined;
65
-
66
- // `parseSize` may return number (px) or a string (e.g. "50%").
67
- const cssWidth =
68
- typeof parsed === 'number'
69
- ? `${parsed}px`
70
- : typeof parsed === 'string'
71
- ? parsed
72
- : undefined;
73
-
74
- if (!cssWidth) return undefined;
75
-
76
- return {
77
- // Controls the actual dot diameter (`.embla__dot:after`).
78
- '--embla-dot-size': cssWidth,
79
- } as React.CSSProperties & Record<`--${string}`, string>;
80
- }, [expandingDotWidthRaw]);
81
- const containerStyle = useMergedStyle(style, expandingDotWidthOverride);
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 dotStyles: React.CSSProperties & Record<`--${string}`, string> = {
119
- opacity: isDotSelected ? 1 : inactiveDotOpacity,
120
- };
121
-
122
- if (resolvedActiveDotColor && isDotSelected) {
123
- // Style the actual visual dot (rendered via `.embla__dot:after`) using a CSS variable.
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={`embla__dot ${isDotSelected ? 'embla__dot--selected' : ''}`}
136
- style={dotStyles}
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
- expanding_dot_width?: string;
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
- "expanding_dot_width": "size",
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": 2
56
+ "sort": 3
46
57
  },
47
- "expanding_dot_width": {
48
- "label": "Expanding Dot Width",
49
- "description": "Width used while expanding.",
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": 3
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": 4
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
- expanding_dot_width: 'size',
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: 2,
6173
+ sort: 3,
6173
6174
  },
6174
- expanding_dot_width: {
6175
- label: 'Expanding Dot Width',
6176
- description: 'Width used while expanding.',
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: 3,
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: 4,
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: 'column',
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
- ...(defaultAttributes?.style ?? {}),
22
- ...(nodeAttributes?.style ?? {}),
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
- if (!isPlainObject(maybeStyle)) {
394
- return fail(`style must be an object`, joinPath(path, 'style'));
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
- maybeStyle as Record<string, unknown>,
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, 'style'), styleKey),
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') {
@@ -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 expandingDotWidth = parseNumber(dotAttrs?.expanding_dot_width);
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 (expandingDotWidth !== undefined)
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,