@developer_tribe/react-builder 1.0.3 → 1.0.4

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 (102) hide show
  1. package/dist/android.svg +43 -0
  2. package/dist/apple.svg +16 -0
  3. package/dist/attributes-editor/Field.d.ts +2 -1
  4. package/dist/attributes-editor/SizeField.d.ts +9 -0
  5. package/dist/build-components/BackgroundImage/BackgroundImageProps.generated.d.ts +1 -0
  6. package/dist/build-components/Button/ButtonProps.generated.d.ts +1 -0
  7. package/dist/build-components/Carousel/CarouselProps.generated.d.ts +1 -0
  8. package/dist/build-components/CarouselButtons/CarouselButtonsProps.generated.d.ts +1 -0
  9. package/dist/build-components/CarouselDots/CarouselDotsProps.generated.d.ts +1 -0
  10. package/dist/build-components/CarouselItem/CarouselItemProps.generated.d.ts +1 -0
  11. package/dist/build-components/CarouselProvider/CarouselProviderProps.generated.d.ts +1 -0
  12. package/dist/build-components/Image/ImageProps.generated.d.ts +1 -0
  13. package/dist/build-components/Onboard/OnboardProps.generated.d.ts +1 -0
  14. package/dist/build-components/OnboardButton/OnboardButtonProps.generated.d.ts +1 -1
  15. package/dist/build-components/OnboardButtons/OnboardButtonsProps.generated.d.ts +1 -0
  16. package/dist/build-components/OnboardDot/OnboardDotProps.generated.d.ts +2 -3
  17. package/dist/build-components/OnboardFooter/OnboardFooterProps.generated.d.ts +1 -0
  18. package/dist/build-components/OnboardImage/OnboardImageProps.generated.d.ts +2 -1
  19. package/dist/build-components/OnboardItem/OnboardItemProps.generated.d.ts +1 -0
  20. package/dist/build-components/OnboardProvider/OnboardProviderProps.generated.d.ts +1 -0
  21. package/dist/build-components/OnboardSubtitle/OnboardSubtitleProps.generated.d.ts +1 -0
  22. package/dist/build-components/OnboardTitle/OnboardTitleProps.generated.d.ts +1 -0
  23. package/dist/build-components/Text/TextProps.generated.d.ts +1 -0
  24. package/dist/build-components/View/ViewProps.generated.d.ts +1 -0
  25. package/dist/build-components/patterns.generated.d.ts +194 -57
  26. package/dist/components/JsonTextEditor.d.ts +9 -0
  27. package/dist/index.cjs.js +5 -5
  28. package/dist/index.cjs.js.map +1 -1
  29. package/dist/index.esm.js +5 -5
  30. package/dist/index.esm.js.map +1 -1
  31. package/dist/pages/tabs/SideTool.d.ts +2 -1
  32. package/dist/store.d.ts +2 -0
  33. package/dist/styles.css +1 -1
  34. package/dist/utils/extractImageStyle.d.ts +2 -1
  35. package/dist/utils/extractViewStyle.d.ts +1 -2
  36. package/dist/utils/selection.d.ts +7 -0
  37. package/dist/utils/useMergedStyle.d.ts +2 -0
  38. package/package.json +2 -5
  39. package/src/.DS_Store +0 -0
  40. package/src/AttributesEditor.tsx +7 -2
  41. package/src/RenderPage.tsx +10 -6
  42. package/src/attributes-editor/Field.tsx +48 -160
  43. package/src/attributes-editor/SizeField.tsx +184 -0
  44. package/src/attributes-editor/SpecialCategorySection.tsx +10 -3
  45. package/src/build-components/BackgroundImage/BackgroundImage.tsx +7 -17
  46. package/src/build-components/BackgroundImage/BackgroundImageProps.generated.ts +1 -0
  47. package/src/build-components/Button/Button.tsx +7 -9
  48. package/src/build-components/Button/ButtonProps.generated.ts +1 -0
  49. package/src/build-components/Carousel/Carousel.tsx +7 -9
  50. package/src/build-components/Carousel/CarouselProps.generated.ts +1 -0
  51. package/src/build-components/CarouselButtons/CarouselButtonsProps.generated.ts +1 -0
  52. package/src/build-components/CarouselDots/CarouselDotsProps.generated.ts +1 -0
  53. package/src/build-components/CarouselItem/CarouselItemProps.generated.ts +1 -0
  54. package/src/build-components/CarouselProvider/CarouselProviderProps.generated.ts +1 -0
  55. package/src/build-components/Image/Image.tsx +11 -18
  56. package/src/build-components/Image/ImageProps.generated.ts +1 -0
  57. package/src/build-components/Image/pattern.json +1 -9
  58. package/src/build-components/Onboard/OnboardProps.generated.ts +1 -0
  59. package/src/build-components/OnboardButton/OnboardButton.tsx +0 -3
  60. package/src/build-components/OnboardButton/OnboardButtonProps.generated.ts +1 -1
  61. package/src/build-components/OnboardButtons/OnboardButtonsProps.generated.ts +1 -0
  62. package/src/build-components/OnboardDot/OnboardDot.tsx +59 -39
  63. package/src/build-components/OnboardDot/OnboardDotProps.generated.ts +2 -3
  64. package/src/build-components/OnboardDot/pattern.json +2 -18
  65. package/src/build-components/OnboardFooter/OnboardFooter.tsx +28 -15
  66. package/src/build-components/OnboardFooter/OnboardFooterProps.generated.ts +1 -0
  67. package/src/build-components/OnboardImage/OnboardImageProps.generated.ts +2 -1
  68. package/src/build-components/OnboardItem/OnboardItem.tsx +1 -11
  69. package/src/build-components/OnboardItem/OnboardItemProps.generated.ts +1 -0
  70. package/src/build-components/OnboardProvider/OnboardProvider.tsx +1 -8
  71. package/src/build-components/OnboardProvider/OnboardProviderProps.generated.ts +1 -0
  72. package/src/build-components/OnboardSubtitle/OnboardSubtitleProps.generated.ts +1 -0
  73. package/src/build-components/OnboardTitle/OnboardTitleProps.generated.ts +1 -0
  74. package/src/build-components/Text/Text.tsx +9 -15
  75. package/src/build-components/Text/TextProps.generated.ts +1 -0
  76. package/src/build-components/View/View.tsx +7 -9
  77. package/src/build-components/View/ViewProps.generated.ts +1 -0
  78. package/src/build-components/View/pattern.json +9 -1
  79. package/src/build-components/patterns.generated.ts +194 -57
  80. package/src/components/Builder.tsx +61 -17
  81. package/src/components/DeviceNavigationBar.tsx +0 -1
  82. package/src/components/EditorHeader.tsx +11 -1
  83. package/src/components/JsonTextEditor.tsx +185 -0
  84. package/src/mockOS/components/MockOSRouter.tsx +6 -0
  85. package/src/mockOS/context/MockOSContext.tsx +0 -5
  86. package/src/mockOS/managers/mockPermissionManager.ts +0 -4
  87. package/src/mockOS/managers/navigationManager.ts +1 -6
  88. package/src/modals/ColorModal.tsx +103 -25
  89. package/src/modals/LocalicationModal.tsx +4 -5
  90. package/src/modals/Modal.tsx +8 -1
  91. package/src/pages/ProjectPage.tsx +7 -1
  92. package/src/pages/tabs/SideTool.tsx +10 -9
  93. package/src/store.ts +5 -0
  94. package/src/styles/base/_global.scss +5 -0
  95. package/src/styles/components/_editor-shell.scss +4 -2
  96. package/src/styles/modals/_color-modal.scss +30 -1
  97. package/src/styles/utilities/_carousel.scss +9 -8
  98. package/src/utils/extractImageStyle.ts +3 -6
  99. package/src/utils/extractTextStyle.ts +2 -81
  100. package/src/utils/extractViewStyle.ts +20 -15
  101. package/src/utils/selection.ts +24 -0
  102. package/src/utils/useMergedStyle.ts +16 -0
@@ -0,0 +1,184 @@
1
+ import React, { useCallback, useEffect, useMemo, useState } from 'react';
2
+
3
+ type SizeUnit = '' | 'vs' | 's' | 'f' | '%';
4
+ type SizeSelectUnit = SizeUnit | 'auto';
5
+
6
+ export type PreferredScale = 'vs' | 's' | 'f' | '%';
7
+
8
+ export function toPreferredScale(value: unknown): PreferredScale | undefined {
9
+ if (value === 'vs' || value === 's' || value === 'f' || value === '%') {
10
+ return value;
11
+ }
12
+ if (typeof value !== 'string') return undefined;
13
+ const normalized = value.trim().toLowerCase();
14
+ return normalized === 'vs' ||
15
+ normalized === 's' ||
16
+ normalized === 'f' ||
17
+ normalized === '%'
18
+ ? (normalized as PreferredScale)
19
+ : undefined;
20
+ }
21
+
22
+ export type SizeFieldProps = {
23
+ value: unknown;
24
+ onChange: (val: unknown) => void;
25
+ preferredScale?: PreferredScale;
26
+ fieldName: string;
27
+ };
28
+
29
+ function parseSizeValue(value: unknown): { amount: string; unit: SizeUnit } {
30
+ const empty = { amount: '', unit: '' as SizeUnit };
31
+ if (typeof value === 'number' && Number.isFinite(value)) {
32
+ return { amount: String(value), unit: '' };
33
+ }
34
+ if (typeof value !== 'string') return empty;
35
+ const trimmed = value.trim();
36
+ if (!trimmed) return empty;
37
+ if (trimmed.endsWith('%')) {
38
+ return { amount: trimmed.slice(0, -1), unit: '%' };
39
+ }
40
+ const lower = trimmed.toLowerCase();
41
+ if (lower.endsWith('@vs'))
42
+ return { amount: trimmed.slice(0, -3), unit: 'vs' };
43
+ if (lower.endsWith('vs')) return { amount: trimmed.slice(0, -2), unit: 'vs' };
44
+ if (lower.endsWith('@fs')) return { amount: trimmed.slice(0, -3), unit: 'f' };
45
+ if (lower.endsWith('@f')) return { amount: trimmed.slice(0, -2), unit: 'f' };
46
+ if (lower.endsWith('fs')) return { amount: trimmed.slice(0, -2), unit: 'f' };
47
+ if (lower.endsWith('f')) return { amount: trimmed.slice(0, -1), unit: 'f' };
48
+ if (lower.endsWith('@s')) return { amount: trimmed.slice(0, -2), unit: 's' };
49
+ if (lower.endsWith('s')) return { amount: trimmed.slice(0, -1), unit: 's' };
50
+ if (lower.endsWith('px')) return { amount: trimmed.slice(0, -2), unit: '' };
51
+ return { amount: trimmed, unit: '' };
52
+ }
53
+
54
+ function composeSizeValue(amount: string, unit: SizeUnit): string | number {
55
+ const trimmed = amount.trim();
56
+ if (unit === '%') {
57
+ return `${trimmed}%`;
58
+ }
59
+ if (unit === '') {
60
+ const numeric = Number(trimmed);
61
+ return Number.isFinite(numeric) ? numeric : trimmed;
62
+ }
63
+ if (unit === 'f') {
64
+ return `${trimmed}@fs`;
65
+ }
66
+ return `${trimmed}@${unit}`;
67
+ }
68
+
69
+ function normalizePreferredScale(
70
+ preferredScale: PreferredScale | undefined,
71
+ fieldName: string,
72
+ ): SizeUnit {
73
+ const fallbackName = fieldName.trim().toLowerCase();
74
+ const fallback: SizeUnit =
75
+ fallbackName.includes('height') ||
76
+ fallbackName.includes('top') ||
77
+ fallbackName.includes('vertical')
78
+ ? 'vs'
79
+ : 's';
80
+ return preferredScale ?? fallback;
81
+ }
82
+
83
+ export function SizeField({
84
+ value,
85
+ onChange,
86
+ preferredScale,
87
+ fieldName,
88
+ }: SizeFieldProps) {
89
+ const parsed = useMemo(() => parseSizeValue(value), [value]);
90
+ const normalizedPreferred = useMemo(
91
+ () => normalizePreferredScale(preferredScale, fieldName),
92
+ [preferredScale, fieldName],
93
+ );
94
+
95
+ const [amount, setAmount] = useState(parsed.amount);
96
+ const [unit, setUnit] = useState<SizeSelectUnit>(() => {
97
+ // Default to "auto" when the stored unit is already the preferred one (or empty)
98
+ // so that "auto" means "use preferredScale" without changing behavior.
99
+ return parsed.unit && parsed.unit !== normalizedPreferred
100
+ ? parsed.unit
101
+ : 'auto';
102
+ });
103
+
104
+ useEffect(() => {
105
+ setAmount(parsed.amount);
106
+
107
+ // Keep "auto" selected whenever value is preferred (or unscaled).
108
+ const nextUnit: SizeSelectUnit =
109
+ parsed.unit && parsed.unit !== normalizedPreferred ? parsed.unit : 'auto';
110
+ setUnit(nextUnit);
111
+ }, [parsed.amount, parsed.unit, normalizedPreferred]);
112
+
113
+ const resolveUnit = useCallback(
114
+ (nextUnit: SizeSelectUnit): SizeUnit =>
115
+ nextUnit === 'auto' ? normalizedPreferred : nextUnit,
116
+ [normalizedPreferred],
117
+ );
118
+
119
+ const emitValue = useCallback(
120
+ (nextAmount: string, nextUnit: SizeSelectUnit) => {
121
+ const trimmed = nextAmount.trim();
122
+ if (!trimmed) {
123
+ onChange(undefined);
124
+ return;
125
+ }
126
+ // If "auto" is selected, persist the preferredScale.
127
+ onChange(composeSizeValue(trimmed, resolveUnit(nextUnit)));
128
+ },
129
+ [onChange, resolveUnit],
130
+ );
131
+
132
+ const handleAmountChange = (e: React.ChangeEvent<HTMLInputElement>) => {
133
+ const nextAmount = e.target.value;
134
+ setAmount(nextAmount);
135
+ if (!nextAmount.trim()) {
136
+ onChange(undefined);
137
+ return;
138
+ }
139
+ emitValue(nextAmount, unit);
140
+ };
141
+
142
+ const handleUnitChange = (e: React.ChangeEvent<HTMLSelectElement>) => {
143
+ const nextUnit = e.target.value as SizeSelectUnit;
144
+ setUnit(nextUnit);
145
+ if (!amount.trim()) return;
146
+ emitValue(amount, nextUnit);
147
+ };
148
+
149
+ const unitPriority: SizeUnit[] = ['vs', 's', 'f', '%'];
150
+ const orderedUnits = [
151
+ normalizedPreferred,
152
+ ...unitPriority.filter((u) => u !== normalizedPreferred),
153
+ ];
154
+
155
+ const unitOptions: Array<{ value: SizeSelectUnit; label: string }> = [
156
+ { value: 'auto', label: 'auto' },
157
+ ...orderedUnits.map((u) => ({
158
+ value: u,
159
+ label: u === normalizedPreferred ? `${u} (preferred)` : u,
160
+ })),
161
+ ];
162
+
163
+ return (
164
+ <div className="attributes-editor__size-field">
165
+ <input
166
+ type="number"
167
+ className="input attributes-editor__size-field-input"
168
+ value={amount}
169
+ onChange={handleAmountChange}
170
+ />
171
+ <select
172
+ className="input attributes-editor__size-field-select"
173
+ value={unit}
174
+ onChange={handleUnitChange}
175
+ >
176
+ {unitOptions.map((opt) => (
177
+ <option key={opt.value} value={opt.value}>
178
+ {opt.label}
179
+ </option>
180
+ ))}
181
+ </select>
182
+ </div>
183
+ );
184
+ }
@@ -3,6 +3,7 @@ import { NodeDefaultAttribute } from '../types/Node';
3
3
  import type { ProjectColors } from '../types/Project';
4
4
  import { Field } from './Field';
5
5
  import { FieldInfoTooltip } from './FieldInfoTooltip';
6
+ import { toPreferredScale } from './SizeField';
6
7
  import {
7
8
  AttributeMetaMap,
8
9
  LayoutContext,
@@ -92,7 +93,9 @@ export function SpecialCategorySection({
92
93
  {entries.map(({ name, type }) => {
93
94
  const label = attributeMeta?.[name]?.label ?? name;
94
95
  const description = attributeMeta?.[name]?.description;
95
- const preferredScale = attributeMeta?.[name]?.preferedScale;
96
+ const preferredScale = toPreferredScale(
97
+ attributeMeta?.[name]?.preferedScale,
98
+ );
96
99
  const currentValue = (attributes as Record<string, unknown>)[name];
97
100
  const isBoolean = isBooleanFieldType(type);
98
101
  const fieldSlot = detectFieldSlot(name);
@@ -169,7 +172,9 @@ export function SpecialCategorySection({
169
172
  {boxEntries.map(({ name, type }) => {
170
173
  const label = attributeMeta?.[name]?.label ?? name;
171
174
  const description = attributeMeta?.[name]?.description;
172
- const preferredScale = attributeMeta?.[name]?.preferedScale;
175
+ const preferredScale = toPreferredScale(
176
+ attributeMeta?.[name]?.preferedScale,
177
+ );
173
178
  const currentValue = (attributes as Record<string, unknown>)[name];
174
179
  const isBoolean = isBooleanFieldType(type);
175
180
  const fieldSlot = detectFieldSlot(name);
@@ -212,7 +217,9 @@ export function SpecialCategorySection({
212
217
  {baseEntries.map(({ name, type }) => {
213
218
  const label = attributeMeta?.[name]?.label ?? name;
214
219
  const description = attributeMeta?.[name]?.description;
215
- const preferredScale = attributeMeta?.[name]?.preferedScale;
220
+ const preferredScale = toPreferredScale(
221
+ attributeMeta?.[name]?.preferedScale,
222
+ );
216
223
  const currentValue = (attributes as Record<string, unknown>)[name];
217
224
  const isBoolean = isBooleanFieldType(type);
218
225
  const wrapperClassNames = [
@@ -4,6 +4,8 @@ import useNode from '../useNode';
4
4
  import { useRenderStore } from '../../store';
5
5
  import { extractViewStyle } from '../../utils/extractViewStyle';
6
6
  import { useLogRender } from '../../utils/useLogRender';
7
+ import { isNodeSelected, SELECTED_OUTLINE_STYLE } from '../../utils/selection';
8
+ import { useMergedStyle } from '../../utils/useMergedStyle';
7
9
 
8
10
  function BackgroundImage({ node }: BackgroundImageComponentProps) {
9
11
  useLogRender('BackgroundImage');
@@ -56,25 +58,13 @@ function BackgroundImage({ node }: BackgroundImageComponentProps) {
56
58
 
57
59
  return style;
58
60
  }, [node]);
59
- const isSelected =
60
- previewMode &&
61
- !!current &&
62
- (current as any)?.key &&
63
- (node as any)?.key &&
64
- (current as any).key === (node as any).key;
65
-
66
- const mergedStyle = useMemo(
67
- () => ({
68
- ...baseStyle,
69
- ...backgroundStyle,
70
- }),
71
- [baseStyle, backgroundStyle],
61
+ const isSelected = isNodeSelected({ previewMode, current, node });
62
+ const mergedStyle = useMergedStyle(baseStyle, backgroundStyle);
63
+ const style = useMergedStyle(
64
+ mergedStyle,
65
+ isSelected ? SELECTED_OUTLINE_STYLE : undefined,
72
66
  );
73
67
 
74
- const style = isSelected
75
- ? { ...mergedStyle, outline: '2px solid #2684FF' }
76
- : mergedStyle;
77
-
78
68
  return (
79
69
  <div
80
70
  attribute-name={attributeName}
@@ -44,6 +44,7 @@ export interface BackgroundImagePropsGenerated {
44
44
  borderRadius?: string;
45
45
  width?: string;
46
46
  height?: string;
47
+ flex?: number;
47
48
  position?: PositionOptionType;
48
49
  top?: string;
49
50
  bottom?: string;
@@ -4,6 +4,8 @@ import useNode from '../useNode';
4
4
  import { useRenderStore } from '../../store';
5
5
  import { useLogRender } from '../../utils/useLogRender';
6
6
  import { extractViewStyle } from '../../utils/extractViewStyle';
7
+ import { isNodeSelected, SELECTED_OUTLINE_STYLE } from '../../utils/selection';
8
+ import { useMergedStyle } from '../../utils/useMergedStyle';
7
9
 
8
10
  function Button({ node }: ButtonComponentProps) {
9
11
  useLogRender('Button');
@@ -23,15 +25,11 @@ function Button({ node }: ButtonComponentProps) {
23
25
  () => extractViewStyle(node, { appConfig, projectColors }),
24
26
  [node, appConfig, projectColors],
25
27
  );
26
- const isSelected =
27
- previewMode &&
28
- !!current &&
29
- (current as any)?.key &&
30
- (node as any)?.key &&
31
- (current as any).key === (node as any).key;
32
- const style = isSelected
33
- ? { ...baseStyle, outline: '2px solid #2684FF' }
34
- : baseStyle;
28
+ const isSelected = isNodeSelected({ previewMode, current, node });
29
+ const style = useMergedStyle(
30
+ baseStyle,
31
+ isSelected ? SELECTED_OUTLINE_STYLE : undefined,
32
+ );
35
33
 
36
34
  return (
37
35
  <div
@@ -55,6 +55,7 @@ export interface ButtonPropsGenerated {
55
55
  borderRadius?: string;
56
56
  width?: string;
57
57
  height?: string;
58
+ flex?: number;
58
59
  position?: PositionOptionType;
59
60
  top?: string;
60
61
  bottom?: string;
@@ -6,6 +6,8 @@ import useNode from '../useNode';
6
6
  import { useRenderStore } from '../../store';
7
7
  import { useLogRender } from '../../utils/useLogRender';
8
8
  import { extractViewStyle } from '../../utils/extractViewStyle';
9
+ import { isNodeSelected, SELECTED_OUTLINE_STYLE } from '../../utils/selection';
10
+ import { useMergedStyle } from '../../utils/useMergedStyle';
9
11
 
10
12
  function Carousel({ node }: CarouselComponentProps) {
11
13
  useLogRender('Carousel');
@@ -25,15 +27,11 @@ function Carousel({ node }: CarouselComponentProps) {
25
27
  () => extractViewStyle(node, { appConfig, projectColors }),
26
28
  [node, appConfig, projectColors],
27
29
  );
28
- const isSelected =
29
- previewMode &&
30
- !!current &&
31
- (current as any)?.key &&
32
- (node as any)?.key &&
33
- (current as any).key === (node as any).key;
34
- const style = isSelected
35
- ? { ...baseStyle, outline: '2px solid #2684FF' }
36
- : baseStyle;
30
+ const isSelected = isNodeSelected({ previewMode, current, node });
31
+ const style = useMergedStyle(
32
+ baseStyle,
33
+ isSelected ? SELECTED_OUTLINE_STYLE : undefined,
34
+ );
37
35
  // Ensure children are carouselItems
38
36
  const renderChildren = () => {
39
37
  if (Array.isArray(node.children)) {
@@ -43,6 +43,7 @@ export interface CarouselPropsGenerated {
43
43
  borderRadius?: string;
44
44
  width?: string;
45
45
  height?: string;
46
+ flex?: number;
46
47
  position?: PositionOptionType;
47
48
  top?: string;
48
49
  bottom?: string;
@@ -47,6 +47,7 @@ export interface CarouselButtonsPropsGenerated {
47
47
  borderRadius?: string;
48
48
  width?: string;
49
49
  height?: string;
50
+ flex?: number;
50
51
  position?: PositionOptionType;
51
52
  top?: string;
52
53
  bottom?: string;
@@ -50,6 +50,7 @@ export interface CarouselDotsPropsGenerated {
50
50
  borderRadius?: string;
51
51
  width?: string;
52
52
  height?: string;
53
+ flex?: number;
53
54
  position?: PositionOptionType;
54
55
  top?: string;
55
56
  bottom?: string;
@@ -43,6 +43,7 @@ export interface CarouselItemPropsGenerated {
43
43
  borderRadius?: string;
44
44
  width?: string;
45
45
  height?: string;
46
+ flex?: number;
46
47
  position?: PositionOptionType;
47
48
  top?: string;
48
49
  bottom?: string;
@@ -43,6 +43,7 @@ export interface CarouselProviderPropsGenerated {
43
43
  borderRadius?: string;
44
44
  width?: string;
45
45
  height?: string;
46
+ flex?: number;
46
47
  position?: PositionOptionType;
47
48
  top?: string;
48
49
  bottom?: string;
@@ -3,8 +3,9 @@ import type { ImageComponentProps } from './ImageProps.generated';
3
3
  import useNode from '../useNode';
4
4
  import { useRenderStore } from '../../store';
5
5
  import { extractImageStyle } from '../../utils/extractImageStyle';
6
- import { extractViewStyle } from '../../utils/extractViewStyle';
7
6
  import { useLogRender } from '../../utils/useLogRender';
7
+ import { isNodeSelected, SELECTED_OUTLINE_STYLE } from '../../utils/selection';
8
+ import { useMergedStyle } from '../../utils/useMergedStyle';
8
9
 
9
10
  function Image({ node }: ImageComponentProps) {
10
11
  useLogRender('Image');
@@ -20,30 +21,22 @@ function Image({ node }: ImageComponentProps) {
20
21
  projectColors: s.projectColors,
21
22
  }),
22
23
  );
23
- const viewStyle = useMemo(
24
- () => extractViewStyle(node, { appConfig, projectColors }),
24
+ const imageStyle = useMemo(
25
+ () => extractImageStyle(node, { appConfig, projectColors }),
25
26
  [node, appConfig, projectColors],
26
27
  );
27
- const imageStyle = extractImageStyle(node);
28
- const isSelected =
29
- previewMode &&
30
- !!current &&
31
- (current as any)?.key &&
32
- (node as any)?.key &&
33
- (current as any).key === (node as any).key;
34
- const style = {
35
- ...viewStyle,
36
- ...imageStyle,
37
- ...(isSelected ? { outline: '2px solid #2684FF' } : {}),
38
- };
28
+ const isSelected = isNodeSelected({ previewMode, current, node });
29
+ const style = useMergedStyle(
30
+ imageStyle,
31
+ isSelected ? SELECTED_OUTLINE_STYLE : undefined,
32
+ );
33
+
39
34
  return (
40
35
  <img
41
- key={node.key}
36
+ key={node.key + (node.attributes?.src ?? '-')}
42
37
  attribute-name={attributeName}
43
38
  attribute-key={attributeKey}
44
39
  src={node.attributes?.src}
45
- width={node.attributes?.width}
46
- height={node.attributes?.height}
47
40
  style={style}
48
41
  alt=""
49
42
  />
@@ -44,6 +44,7 @@ export interface ImagePropsGenerated {
44
44
  borderRadius?: string;
45
45
  width?: string;
46
46
  height?: string;
47
+ flex?: number;
47
48
  position?: PositionOptionType;
48
49
  top?: string;
49
50
  bottom?: string;
@@ -9,8 +9,7 @@
9
9
  "src": "string",
10
10
  "width": "size",
11
11
  "height": "size",
12
- "resizeMode": ["cover", "contain", "stretch", "center"],
13
- "borderRadius": "size"
12
+ "resizeMode": ["cover", "contain", "stretch", "center"]
14
13
  }
15
14
  },
16
15
  "meta": {
@@ -45,13 +44,6 @@
45
44
  "category": "style",
46
45
  "specialCategory": null,
47
46
  "sort": 4
48
- },
49
- "borderRadius": {
50
- "label": "Border Radius",
51
- "description": "Corner rounding.",
52
- "category": "style",
53
- "specialCategory": null,
54
- "sort": 5
55
47
  }
56
48
  }
57
49
  }
@@ -43,6 +43,7 @@ export interface OnboardPropsGenerated {
43
43
  borderRadius?: string;
44
44
  width?: string;
45
45
  height?: string;
46
+ flex?: number;
46
47
  position?: PositionOptionType;
47
48
  top?: string;
48
49
  bottom?: string;
@@ -57,7 +57,6 @@ function OnboardButton({ node }: OnboardButtonComponentProps) {
57
57
  mockPermissionManager.requestPermission(permission);
58
58
  handledEventsRef.current.push(e);
59
59
  //TODO: cause user to click second time
60
- return;
61
60
  } else if (e.type === 'Navigate') {
62
61
  const eventTargetIndex = e.targetIndex;
63
62
  if (typeof eventTargetIndex === 'number') {
@@ -65,7 +64,6 @@ function OnboardButton({ node }: OnboardButtonComponentProps) {
65
64
  navigateHandled = true;
66
65
  handledEventsRef.current.push(e);
67
66
  //TODO: cause user to click second time
68
- return;
69
67
  } else if (e.navigate_to) {
70
68
  const eventTarget = e.navigate_to;
71
69
  if (typeof eventTarget === 'string') {
@@ -78,7 +76,6 @@ function OnboardButton({ node }: OnboardButtonComponentProps) {
78
76
  alert('Mock OS context not available for navigation.');
79
77
  }
80
78
  }
81
- return;
82
79
  }
83
80
  }
84
81
  }
@@ -58,6 +58,7 @@ export interface OnboardButtonPropsGenerated {
58
58
  borderRadius?: string;
59
59
  width?: string;
60
60
  height?: string;
61
+ flex?: number;
61
62
  position?: PositionOptionType;
62
63
  top?: string;
63
64
  bottom?: string;
@@ -69,7 +70,6 @@ export interface OnboardButtonPropsGenerated {
69
70
  animation?: AnimationOptionType;
70
71
  animation_color?: string;
71
72
  button_background_color?: string;
72
- flex?: number;
73
73
  events?: EventObjectGenerated[];
74
74
  };
75
75
  }
@@ -49,6 +49,7 @@ export interface OnboardButtonsPropsGenerated {
49
49
  borderRadius?: string;
50
50
  width?: string;
51
51
  height?: string;
52
+ flex?: number;
52
53
  position?: PositionOptionType;
53
54
  top?: string;
54
55
  bottom?: string;