@developer_tribe/react-builder 1.2.20 → 1.2.22
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/attribute-analyser/style/web/useExtractTextStyle.d.ts +1 -1
- package/dist/build-components/BIcon/BIconProps.generated.d.ts +2 -0
- package/dist/build-components/BackgroundImage/BackgroundImageProps.generated.d.ts +2 -0
- package/dist/build-components/Button/ButtonProps.generated.d.ts +2 -0
- package/dist/build-components/Carousel/CarouselProps.generated.d.ts +2 -0
- package/dist/build-components/CarouselButtons/CarouselButtonsProps.generated.d.ts +2 -0
- package/dist/build-components/CarouselDots/CarouselDotsProps.generated.d.ts +2 -0
- package/dist/build-components/CarouselItem/CarouselItemProps.generated.d.ts +2 -0
- package/dist/build-components/CarouselProvider/CarouselProviderProps.generated.d.ts +2 -0
- package/dist/build-components/CountDown/CountDownProps.generated.d.ts +2 -0
- package/dist/build-components/Counter/CounterProps.generated.d.ts +2 -0
- package/dist/build-components/Image/ImageProps.generated.d.ts +2 -0
- package/dist/build-components/Main/MainProps.generated.d.ts +2 -0
- package/dist/build-components/Onboard/OnboardProps.generated.d.ts +2 -0
- package/dist/build-components/OnboardButton/OnboardButtonProps.generated.d.ts +2 -0
- package/dist/build-components/OnboardButtons/OnboardButtonsProps.generated.d.ts +2 -0
- package/dist/build-components/OnboardDot/OnboardDotProps.generated.d.ts +2 -0
- package/dist/build-components/OnboardFooter/OnboardFooterProps.generated.d.ts +2 -0
- package/dist/build-components/OnboardImage/OnboardImageProps.generated.d.ts +2 -0
- package/dist/build-components/OnboardItem/OnboardItemProps.generated.d.ts +2 -0
- package/dist/build-components/OnboardProvider/OnboardProviderProps.generated.d.ts +2 -0
- package/dist/build-components/OnboardSubtitle/OnboardSubtitleProps.generated.d.ts +2 -0
- package/dist/build-components/OnboardTitle/OnboardTitleProps.generated.d.ts +2 -0
- package/dist/build-components/PaywallBackground/PaywallBackgroundProps.generated.d.ts +2 -0
- package/dist/build-components/PaywallCloseButton/PaywallCloseButtonProps.generated.d.ts +2 -0
- package/dist/build-components/PaywallCounter/PaywallCounterProps.generated.d.ts +2 -0
- package/dist/build-components/PaywallOptions/PaywallOptionsProps.generated.d.ts +2 -0
- package/dist/build-components/PaywallProvider/PaywallProviderProps.generated.d.ts +2 -0
- package/dist/build-components/PaywallSubscribeButton/PaywallSubscribeButtonProps.generated.d.ts +2 -0
- package/dist/build-components/RadioButton/RadioButtonProps.generated.d.ts +2 -0
- package/dist/build-components/Text/TextProps.generated.d.ts +2 -0
- package/dist/build-components/View/View.d.ts +1 -1
- package/dist/build-components/View/ViewProps.generated.d.ts +2 -0
- package/dist/build-components/patterns.generated.d.ts +287 -2
- package/dist/components/BuilderButton.d.ts +3 -1
- package/dist/index.cjs.js +3 -3
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.esm.js +4 -4
- package/dist/index.esm.js.map +1 -1
- package/dist/index.web.cjs.js +4 -4
- package/dist/index.web.cjs.js.map +1 -1
- package/dist/index.web.esm.js +4 -4
- package/dist/index.web.esm.js.map +1 -1
- package/dist/store.d.ts +2 -0
- package/dist/styles.css +1 -1
- package/dist/utils/extractTextStyle/extractTextStyle.d.ts +1 -0
- package/package.json +1 -1
- package/scripts/migrate-patterns-to-v2.mjs +13 -8
- package/scripts/prebuild/icon-generator.js +34 -37
- package/src/assets/loading_animation.json +2587 -1
- package/src/assets/meta.json +1 -1
- package/src/assets/samples/carousel-sample.json +279 -197
- package/src/assets/samples/getSamples.ts +16 -1
- package/src/assets/samples/paywall-1.json +16 -0
- package/src/assets/samples/paywall-2.json +2 -2
- package/src/assets/samples/paywall-app-delete-offer.json +353 -0
- package/src/assets/samples/paywall-app-open-offer.json +353 -0
- package/src/assets/samples/paywall-back-offer.json +353 -0
- package/src/assets/samples/paywall-notification-offer.json +353 -0
- package/src/assets/samples/vpn-onboard-1.json +23 -12
- package/src/assets/samples/vpn-onboard-2.json +23 -12
- package/src/assets/samples/vpn-onboard-3.json +23 -12
- package/src/assets/samples/vpn-onboard-4.json +23 -12
- package/src/assets/samples/vpn-onboard-5.json +23 -12
- package/src/assets/samples/vpn-onboard-6.json +23 -12
- package/src/attribute-analyser/style/web/useExtractTextStyle.ts +9 -2
- package/src/build-components/BIcon/BIconProps.generated.ts +2 -0
- package/src/build-components/BackgroundImage/BackgroundImageProps.generated.ts +2 -0
- package/src/build-components/Button/ButtonProps.generated.ts +2 -0
- package/src/build-components/Carousel/CarouselProps.generated.ts +2 -0
- package/src/build-components/Carousel/pattern.json +2 -8
- package/src/build-components/CarouselButtons/CarouselButtonsProps.generated.ts +2 -0
- package/src/build-components/CarouselButtons/pattern.json +2 -9
- package/src/build-components/CarouselDots/CarouselDotsProps.generated.ts +2 -0
- package/src/build-components/CarouselDots/pattern.json +1 -3
- package/src/build-components/CarouselItem/CarouselItemProps.generated.ts +2 -0
- package/src/build-components/CarouselItem/pattern.json +1 -3
- package/src/build-components/CarouselProvider/CarouselProvider.tsx +5 -44
- package/src/build-components/CarouselProvider/CarouselProviderProps.generated.ts +2 -0
- package/src/build-components/CarouselProvider/pattern.json +6 -0
- package/src/build-components/CountDown/CountDownProps.generated.ts +2 -0
- package/src/build-components/CountDown/pattern.json +0 -1
- package/src/build-components/Counter/CounterProps.generated.ts +2 -0
- package/src/build-components/Counter/pattern.json +0 -1
- package/src/build-components/Image/Image.tsx +1 -1
- package/src/build-components/Image/ImageProps.generated.ts +2 -0
- package/src/build-components/Main/MainProps.generated.ts +2 -0
- package/src/build-components/Main/pattern.json +1 -3
- package/src/build-components/Onboard/OnboardProps.generated.ts +2 -0
- package/src/build-components/Onboard/pattern.json +2 -6
- package/src/build-components/OnboardButton/OnboardButton.tsx +0 -4
- package/src/build-components/OnboardButton/OnboardButtonProps.generated.ts +2 -0
- package/src/build-components/OnboardButton/pattern.json +9 -14
- package/src/build-components/OnboardButtons/OnboardButtons.tsx +17 -20
- package/src/build-components/OnboardButtons/OnboardButtonsProps.generated.ts +2 -0
- package/src/build-components/OnboardButtons/pattern.json +15 -15
- package/src/build-components/OnboardDot/OnboardDot.tsx +0 -3
- package/src/build-components/OnboardDot/OnboardDotProps.generated.ts +2 -0
- package/src/build-components/OnboardFooter/OnboardFooter.tsx +63 -51
- package/src/build-components/OnboardFooter/OnboardFooterProps.generated.ts +2 -0
- package/src/build-components/OnboardFooter/pattern.json +6 -3
- package/src/build-components/OnboardImage/OnboardImageProps.generated.ts +2 -0
- package/src/build-components/OnboardImage/pattern.json +1 -5
- package/src/build-components/OnboardItem/OnboardItemProps.generated.ts +2 -0
- package/src/build-components/OnboardItem/pattern.json +3 -11
- package/src/build-components/OnboardProvider/OnboardProviderProps.generated.ts +2 -0
- package/src/build-components/OnboardProvider/pattern.json +2 -8
- package/src/build-components/OnboardSubtitle/OnboardSubtitleProps.generated.ts +2 -0
- package/src/build-components/OnboardSubtitle/pattern.json +1 -4
- package/src/build-components/OnboardTitle/OnboardTitleProps.generated.ts +2 -0
- package/src/build-components/OnboardTitle/pattern.json +1 -4
- package/src/build-components/PaywallBackground/PaywallBackgroundProps.generated.ts +2 -0
- package/src/build-components/PaywallCloseButton/PaywallCloseButtonProps.generated.ts +2 -0
- package/src/build-components/PaywallCloseButton/pattern.json +1 -3
- package/src/build-components/PaywallCounter/PaywallCounterProps.generated.ts +2 -0
- package/src/build-components/PaywallCounter/pattern.json +0 -1
- package/src/build-components/PaywallOptions/PaywallOptions.tsx +1 -1
- package/src/build-components/PaywallOptions/PaywallOptionsProps.generated.ts +2 -0
- package/src/build-components/PaywallOptions/pattern.json +1 -3
- package/src/build-components/PaywallProvider/PaywallProviderProps.generated.ts +2 -0
- package/src/build-components/PaywallProvider/pattern.json +1 -3
- package/src/build-components/PaywallSubscribeButton/PaywallSubscribeButtonProps.generated.ts +2 -0
- package/src/build-components/PaywallSubscribeButton/pattern.json +1 -3
- package/src/build-components/RadioButton/RadioButtonProps.generated.ts +2 -0
- package/src/build-components/RadioButton/pattern.json +1 -3
- package/src/build-components/RenderNode.generated.tsx +1 -1
- package/src/build-components/Text/TextProps.generated.ts +2 -0
- package/src/build-components/View/View.tsx +11 -7
- package/src/build-components/View/ViewProps.generated.ts +2 -0
- package/src/build-components/View/pattern.json +8 -0
- package/src/build-components/patterns.generated.ts +277 -2
- package/src/build-components/useNode.ts +2 -2
- package/src/components/Builder.tsx +98 -8
- package/src/components/BuilderButton.tsx +39 -7
- package/src/components/DeviceButton.tsx +5 -1
- package/src/pages/DebugJsonPage.tsx +30 -1
- package/src/pages/ProjectDebug.tsx +0 -1
- package/src/pages/ProjectPage.tsx +2 -2
- package/src/store.ts +8 -0
- package/src/styles/base/_global.scss +0 -5
- package/src/styles/components/_editor-shell.scss +18 -3
- package/src/styles/components/_onboard.scss +0 -17
- package/src/styles/foundation/_colors.scss +1 -4
- package/src/styles/foundation/_typography.scss +0 -1
- package/src/styles/layout/_builder.scss +20 -0
- package/src/styles/modals/_product-presets-modal.scss +0 -2
- package/src/utils/extractTextStyle/extractTextStyle.ts +47 -13
- package/src/utils/extractViewStyle/extractViewStyle.ts +118 -39
- package/src/utils/logRenderStore.ts +7 -9
- package/src/utils/logger.ts +1 -5
- package/src/utils/repairNodeKeys.ts +1 -4
|
@@ -20,10 +20,10 @@ export default function useNode<
|
|
|
20
20
|
...(defaultAttributes?.styles ?? {}),
|
|
21
21
|
...(defaultAttributes?.style ?? {}),
|
|
22
22
|
};
|
|
23
|
-
// Merge node style from both node.attributes.style and node.attributes.styles
|
|
23
|
+
// Merge node style from both node.attributes.style and node.attributes.styles (preferring styles)
|
|
24
24
|
const nodeStyle = {
|
|
25
|
-
...(nodeAttributes?.styles ?? {}),
|
|
26
25
|
...(nodeAttributes?.style ?? {}),
|
|
26
|
+
...(nodeAttributes?.styles ?? {}),
|
|
27
27
|
};
|
|
28
28
|
const mergedStyle = {
|
|
29
29
|
...defaultStyle,
|
|
@@ -13,6 +13,7 @@ import { AddComponentModal } from '../modals/AddComponentModal';
|
|
|
13
13
|
import { BuilderButton } from './BuilderButton';
|
|
14
14
|
import { generateRandomKeyForNode } from '../utils/generateRandomKeyForNode';
|
|
15
15
|
import { collectNodeKeys } from '../utils/repairNodeKeys';
|
|
16
|
+
import { useRenderStore } from '../store';
|
|
16
17
|
|
|
17
18
|
type BuilderEditorProps = {
|
|
18
19
|
data: Node;
|
|
@@ -30,6 +31,68 @@ interface BuilderEditorComponentProps {
|
|
|
30
31
|
onReorder?: (prev: Node[], next: Node[]) => void;
|
|
31
32
|
onMoveChildUp?: (parent: Node, childIndex: number) => void;
|
|
32
33
|
onMoveChildDown?: (parent: Node, childIndex: number) => void;
|
|
34
|
+
maxNestedDepth?: number;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
type BuilderTreeNodeProps = {
|
|
38
|
+
node: Node;
|
|
39
|
+
depthRemaining: number;
|
|
40
|
+
onClick: (node: Node) => void;
|
|
41
|
+
onDelete?: (node: Node) => void;
|
|
42
|
+
onMoveUp?: () => void;
|
|
43
|
+
onMoveDown?: () => void;
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
function BuilderTreeNode({
|
|
47
|
+
node,
|
|
48
|
+
depthRemaining,
|
|
49
|
+
onClick,
|
|
50
|
+
onDelete,
|
|
51
|
+
onMoveUp,
|
|
52
|
+
onMoveDown,
|
|
53
|
+
}: BuilderTreeNodeProps) {
|
|
54
|
+
const children = getNodeChildren(node);
|
|
55
|
+
const hasChildren = children.length > 0;
|
|
56
|
+
const childrenCount =
|
|
57
|
+
hasChildren && depthRemaining <= 0 ? children.length : undefined;
|
|
58
|
+
const onMore =
|
|
59
|
+
hasChildren && depthRemaining <= 0 ? () => onClick(node) : undefined;
|
|
60
|
+
const button = (
|
|
61
|
+
<BuilderButton
|
|
62
|
+
onClick={() => {
|
|
63
|
+
onClick(node);
|
|
64
|
+
}}
|
|
65
|
+
node={node}
|
|
66
|
+
onDelete={onDelete}
|
|
67
|
+
onMoveUp={onMoveUp}
|
|
68
|
+
onMoveDown={onMoveDown}
|
|
69
|
+
childrenCount={childrenCount}
|
|
70
|
+
onMore={onMore}
|
|
71
|
+
/>
|
|
72
|
+
);
|
|
73
|
+
|
|
74
|
+
if (!hasChildren) {
|
|
75
|
+
return button;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
if (depthRemaining <= 0) {
|
|
79
|
+
return <div className="builder__children">{button}</div>;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
return (
|
|
83
|
+
<div className="builder__children">
|
|
84
|
+
{button}
|
|
85
|
+
{children.map((child, index) => (
|
|
86
|
+
<BuilderTreeNode
|
|
87
|
+
key={index}
|
|
88
|
+
node={child}
|
|
89
|
+
depthRemaining={depthRemaining - 1}
|
|
90
|
+
onClick={onClick}
|
|
91
|
+
onDelete={onDelete}
|
|
92
|
+
/>
|
|
93
|
+
))}
|
|
94
|
+
</div>
|
|
95
|
+
);
|
|
33
96
|
}
|
|
34
97
|
|
|
35
98
|
function BuilderComponent({
|
|
@@ -40,6 +103,7 @@ function BuilderComponent({
|
|
|
40
103
|
onReorder,
|
|
41
104
|
onMoveChildUp,
|
|
42
105
|
onMoveChildDown,
|
|
106
|
+
maxNestedDepth,
|
|
43
107
|
}: BuilderEditorComponentProps) {
|
|
44
108
|
if (isNodeString(node)) {
|
|
45
109
|
return (
|
|
@@ -64,6 +128,12 @@ function BuilderComponent({
|
|
|
64
128
|
|
|
65
129
|
if (isNodeArray(node)) {
|
|
66
130
|
const list = node as Node[];
|
|
131
|
+
const depthRemaining = Math.max(
|
|
132
|
+
0,
|
|
133
|
+
Number.isFinite(maxNestedDepth) && (maxNestedDepth ?? 0) > 0
|
|
134
|
+
? Math.floor(maxNestedDepth ?? 1) - 1
|
|
135
|
+
: 4,
|
|
136
|
+
);
|
|
67
137
|
|
|
68
138
|
const moveItem = (index: number, direction: -1 | 1) => {
|
|
69
139
|
if (!onReorder) return;
|
|
@@ -79,11 +149,10 @@ function BuilderComponent({
|
|
|
79
149
|
<div className="builder__list">
|
|
80
150
|
{list.map((item, index) => (
|
|
81
151
|
<div key={index} className="builder__list-item">
|
|
82
|
-
<
|
|
83
|
-
onClick={() => {
|
|
84
|
-
onClick(item);
|
|
85
|
-
}}
|
|
152
|
+
<BuilderTreeNode
|
|
86
153
|
node={item}
|
|
154
|
+
depthRemaining={depthRemaining}
|
|
155
|
+
onClick={onClick}
|
|
87
156
|
onDelete={onDelete}
|
|
88
157
|
onMoveUp={
|
|
89
158
|
onReorder && index > 0 ? () => moveItem(index, -1) : undefined
|
|
@@ -109,6 +178,12 @@ function BuilderComponent({
|
|
|
109
178
|
? (rawChildren as Node[])
|
|
110
179
|
: [rawChildren]
|
|
111
180
|
: null;
|
|
181
|
+
const depthRemaining = Math.max(
|
|
182
|
+
0,
|
|
183
|
+
Number.isFinite(maxNestedDepth) && (maxNestedDepth ?? 0) > 0
|
|
184
|
+
? Math.floor(maxNestedDepth ?? 1) - 1
|
|
185
|
+
: 4,
|
|
186
|
+
);
|
|
112
187
|
|
|
113
188
|
return (
|
|
114
189
|
<div className="builder__node">
|
|
@@ -122,12 +197,11 @@ function BuilderComponent({
|
|
|
122
197
|
/>
|
|
123
198
|
{children &&
|
|
124
199
|
children.map((child, index) => (
|
|
125
|
-
<
|
|
126
|
-
onClick={() => {
|
|
127
|
-
onClick(child);
|
|
128
|
-
}}
|
|
200
|
+
<BuilderTreeNode
|
|
129
201
|
key={index}
|
|
130
202
|
node={child}
|
|
203
|
+
depthRemaining={depthRemaining}
|
|
204
|
+
onClick={onClick}
|
|
131
205
|
onDelete={onDelete}
|
|
132
206
|
onMoveUp={
|
|
133
207
|
onMoveChildUp && hasArrayChildren && index > 0
|
|
@@ -158,7 +232,12 @@ export function Builder({
|
|
|
158
232
|
}: BuilderEditorProps) {
|
|
159
233
|
useLogRender('Builder');
|
|
160
234
|
const [isAddModalOpen, setIsAddModalOpen] = useState(false);
|
|
235
|
+
const listMaxNested = useRenderStore((state) => state.listMaxNested);
|
|
161
236
|
const usedKeys = useMemo(() => collectNodeKeys(data), [data]);
|
|
237
|
+
const resolvedListMaxNested =
|
|
238
|
+
Number.isFinite(listMaxNested) && listMaxNested > 0
|
|
239
|
+
? Math.floor(listMaxNested)
|
|
240
|
+
: 5;
|
|
162
241
|
const breadcrumbPath = useMemo(() => {
|
|
163
242
|
const path = findNodePath(data, current);
|
|
164
243
|
if (path.length) return path;
|
|
@@ -482,6 +561,7 @@ export function Builder({
|
|
|
482
561
|
onReorder={handleReorder}
|
|
483
562
|
onMoveChildUp={handleMoveChildUp}
|
|
484
563
|
onMoveChildDown={handleMoveChildDown}
|
|
564
|
+
maxNestedDepth={resolvedListMaxNested}
|
|
485
565
|
node={current}
|
|
486
566
|
/>
|
|
487
567
|
{isAddModalOpen && (
|
|
@@ -510,6 +590,16 @@ function appendChild(children: Node, childToAppend: Node): Node {
|
|
|
510
590
|
return [children as Node, childToAppend];
|
|
511
591
|
}
|
|
512
592
|
|
|
593
|
+
function getNodeChildren(node: Node): Node[] {
|
|
594
|
+
if (isNodeNullOrUndefined(node) || isNodeString(node) || isNodeArray(node)) {
|
|
595
|
+
return [];
|
|
596
|
+
}
|
|
597
|
+
const nodeData = node as NodeData<NodeDefaultAttribute>;
|
|
598
|
+
const children = nodeData.children;
|
|
599
|
+
if (!children) return [];
|
|
600
|
+
return Array.isArray(children) ? children : [children];
|
|
601
|
+
}
|
|
602
|
+
|
|
513
603
|
function getNodeLabel(node: Node): string {
|
|
514
604
|
if (isNodeNullOrUndefined(node)) return 'Empty';
|
|
515
605
|
if (isNodeString(node)) return node as string;
|
|
@@ -10,6 +10,8 @@ export type BuilderButtonProps = {
|
|
|
10
10
|
onDelete?: (node: Node) => void;
|
|
11
11
|
onMoveUp?: () => void;
|
|
12
12
|
onMoveDown?: () => void;
|
|
13
|
+
childrenCount?: number;
|
|
14
|
+
onMore?: () => void;
|
|
13
15
|
};
|
|
14
16
|
|
|
15
17
|
export function BuilderButton({
|
|
@@ -18,6 +20,8 @@ export function BuilderButton({
|
|
|
18
20
|
onDelete,
|
|
19
21
|
onMoveUp,
|
|
20
22
|
onMoveDown,
|
|
23
|
+
childrenCount,
|
|
24
|
+
onMore,
|
|
21
25
|
}: BuilderButtonProps) {
|
|
22
26
|
// IMPORTANT: Hooks must be called unconditionally on every render.
|
|
23
27
|
// (Early returns before hooks can trigger React internal invariants.)
|
|
@@ -83,7 +87,8 @@ export function BuilderButton({
|
|
|
83
87
|
: '';
|
|
84
88
|
const hasTitle = title.length > 0;
|
|
85
89
|
const topLabel = hasTitle ? title : baseLabel;
|
|
86
|
-
const shouldShowBottom =
|
|
90
|
+
const shouldShowBottom =
|
|
91
|
+
hasTitle || conditionLabel.length > 0 || typeof childrenCount === 'number';
|
|
87
92
|
|
|
88
93
|
return (
|
|
89
94
|
<div className="builder__button">
|
|
@@ -140,6 +145,22 @@ export function BuilderButton({
|
|
|
140
145
|
role="menu"
|
|
141
146
|
aria-label="Node actions"
|
|
142
147
|
>
|
|
148
|
+
{onMore ? (
|
|
149
|
+
<li role="none">
|
|
150
|
+
<button
|
|
151
|
+
type="button"
|
|
152
|
+
className="builder__button-actions-item"
|
|
153
|
+
role="menuitem"
|
|
154
|
+
onClick={(event) => {
|
|
155
|
+
event.stopPropagation();
|
|
156
|
+
onMore();
|
|
157
|
+
setIsMenuOpen(false);
|
|
158
|
+
}}
|
|
159
|
+
>
|
|
160
|
+
More
|
|
161
|
+
</button>
|
|
162
|
+
</li>
|
|
163
|
+
) : null}
|
|
143
164
|
<li role="none">
|
|
144
165
|
<button
|
|
145
166
|
type="button"
|
|
@@ -163,14 +184,25 @@ export function BuilderButton({
|
|
|
163
184
|
</div>
|
|
164
185
|
{shouldShowBottom && (
|
|
165
186
|
<span className="builder__button-condition">
|
|
166
|
-
{hasTitle ? (
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
187
|
+
{hasTitle || conditionLabel.length > 0 ? (
|
|
188
|
+
<span className="builder__button-condition-text">
|
|
189
|
+
{hasTitle ? (
|
|
190
|
+
<>
|
|
191
|
+
<small>{baseLabel}</small>
|
|
192
|
+
{conditionLabel ? ` · ${conditionLabel}` : ''}
|
|
193
|
+
</>
|
|
194
|
+
) : (
|
|
195
|
+
conditionLabel
|
|
196
|
+
)}
|
|
197
|
+
</span>
|
|
171
198
|
) : (
|
|
172
|
-
|
|
199
|
+
<span className="builder__button-condition-text" />
|
|
173
200
|
)}
|
|
201
|
+
{typeof childrenCount === 'number' ? (
|
|
202
|
+
<span className="builder__button-children-count">
|
|
203
|
+
{childrenCount} children
|
|
204
|
+
</span>
|
|
205
|
+
) : null}
|
|
174
206
|
</span>
|
|
175
207
|
)}
|
|
176
208
|
</div>
|
|
@@ -38,7 +38,11 @@ export function DeviceButton({
|
|
|
38
38
|
>
|
|
39
39
|
{device.name} <br />
|
|
40
40
|
{device.width}×{device.height} ({aspect})
|
|
41
|
-
{platformIcon &&
|
|
41
|
+
{platformIcon && (
|
|
42
|
+
<span>
|
|
43
|
+
<img src={platformIcon} alt="" aria-hidden="true" />
|
|
44
|
+
</span>
|
|
45
|
+
)}
|
|
42
46
|
</button>
|
|
43
47
|
);
|
|
44
48
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React, { useMemo } from 'react';
|
|
1
|
+
import React, { useEffect, useMemo, useState } from 'react';
|
|
2
2
|
import type { Node } from '../types/Node';
|
|
3
3
|
import type { NodeData, NodeDefaultAttribute } from '../types/Node';
|
|
4
4
|
import type { AppConfig } from '../types/PreviewConfig';
|
|
@@ -44,6 +44,11 @@ export function DebugJsonPage({
|
|
|
44
44
|
logLabel,
|
|
45
45
|
}: DebugJsonPageProps) {
|
|
46
46
|
const setCurrent = useRenderStore((s) => s.setCurrent);
|
|
47
|
+
const listMaxNested = useRenderStore((s) => s.listMaxNested);
|
|
48
|
+
const setListMaxNested = useRenderStore((s) => s.setListMaxNested);
|
|
49
|
+
const [listMaxNestedInput, setListMaxNestedInput] = useState(
|
|
50
|
+
String(listMaxNested),
|
|
51
|
+
);
|
|
47
52
|
const canTogglePreviewMode = typeof setPreviewMode === 'function';
|
|
48
53
|
const canToggleTheme =
|
|
49
54
|
typeof setAppConfig === 'function' && typeof appConfig?.theme === 'string';
|
|
@@ -117,6 +122,19 @@ export function DebugJsonPage({
|
|
|
117
122
|
return checkNode(data);
|
|
118
123
|
}, [data]);
|
|
119
124
|
|
|
125
|
+
useEffect(() => {
|
|
126
|
+
setListMaxNestedInput(String(listMaxNested));
|
|
127
|
+
}, [listMaxNested]);
|
|
128
|
+
|
|
129
|
+
useEffect(() => {
|
|
130
|
+
const handle = window.setTimeout(() => {
|
|
131
|
+
const next = Number.parseInt(listMaxNestedInput, 10);
|
|
132
|
+
if (!Number.isFinite(next) || next <= 0) return;
|
|
133
|
+
setListMaxNested(next);
|
|
134
|
+
}, 300);
|
|
135
|
+
return () => window.clearTimeout(handle);
|
|
136
|
+
}, [listMaxNestedInput, setListMaxNested]);
|
|
137
|
+
|
|
120
138
|
const handleFixStyleToStyles = () => {
|
|
121
139
|
if (!data) return;
|
|
122
140
|
const migrated = migrateStyleToStyles(data);
|
|
@@ -213,6 +231,17 @@ export function DebugJsonPage({
|
|
|
213
231
|
</div>
|
|
214
232
|
) : null}
|
|
215
233
|
|
|
234
|
+
<div className="localication-modal__controls">
|
|
235
|
+
<label>List max nested depth</label>
|
|
236
|
+
<input
|
|
237
|
+
type="number"
|
|
238
|
+
min={1}
|
|
239
|
+
className="input"
|
|
240
|
+
value={listMaxNestedInput}
|
|
241
|
+
onChange={(event) => setListMaxNestedInput(event.target.value)}
|
|
242
|
+
/>
|
|
243
|
+
</div>
|
|
244
|
+
|
|
216
245
|
<div className="localication-modal__editor">
|
|
217
246
|
<JsonTextEditor
|
|
218
247
|
rootName="node"
|
|
@@ -265,7 +265,7 @@ export function ProjectPage({
|
|
|
265
265
|
setEditorData(processed);
|
|
266
266
|
setCurrent(processed);
|
|
267
267
|
} catch (error) {
|
|
268
|
-
|
|
268
|
+
logger.error('ProjectPage', 'Failed to process node', error);
|
|
269
269
|
setValidationError(
|
|
270
270
|
error instanceof Error ? error.message : 'Node is not valid',
|
|
271
271
|
);
|
|
@@ -601,7 +601,7 @@ export function ProjectPage({
|
|
|
601
601
|
<div
|
|
602
602
|
style={{
|
|
603
603
|
// Set as a CSS variable so `.dark .split-right` can override it.
|
|
604
|
-
|
|
604
|
+
|
|
605
605
|
['--rb-canvas-bg' as any]: `url(${getImage(
|
|
606
606
|
TribeAssetName.Background,
|
|
607
607
|
)})`,
|
package/src/store.ts
CHANGED
|
@@ -76,6 +76,8 @@ type RenderStore = {
|
|
|
76
76
|
// Cache loaded font families to avoid repeated loads
|
|
77
77
|
loadedFonts: string[];
|
|
78
78
|
markFontLoaded: (fontFamily: string) => void;
|
|
79
|
+
listMaxNested: number;
|
|
80
|
+
setListMaxNested: (depth: number) => void;
|
|
79
81
|
};
|
|
80
82
|
|
|
81
83
|
export const useRenderStore = createWithEqualityFn<RenderStore>()(
|
|
@@ -233,6 +235,11 @@ export const useRenderStore = createWithEqualityFn<RenderStore>()(
|
|
|
233
235
|
if (prev.includes(family)) return state;
|
|
234
236
|
return { loadedFonts: [...prev, family] };
|
|
235
237
|
}),
|
|
238
|
+
listMaxNested: 3,
|
|
239
|
+
setListMaxNested: (depth) =>
|
|
240
|
+
set({
|
|
241
|
+
listMaxNested: Number.isFinite(depth) && depth > 0 ? depth : 5,
|
|
242
|
+
}),
|
|
236
243
|
}),
|
|
237
244
|
{
|
|
238
245
|
name: 'render-store',
|
|
@@ -242,6 +249,7 @@ export const useRenderStore = createWithEqualityFn<RenderStore>()(
|
|
|
242
249
|
logLevel: state.logLevel,
|
|
243
250
|
products: state.products,
|
|
244
251
|
benefits: state.benefits,
|
|
252
|
+
listMaxNested: state.listMaxNested,
|
|
245
253
|
}),
|
|
246
254
|
storage: createJSONStorage(() => localStorage),
|
|
247
255
|
},
|
|
@@ -94,12 +94,27 @@
|
|
|
94
94
|
}
|
|
95
95
|
}
|
|
96
96
|
|
|
97
|
-
.editor-device-button
|
|
97
|
+
.editor-device-button span {
|
|
98
98
|
position: absolute;
|
|
99
99
|
bottom: 4px;
|
|
100
100
|
right: 4px;
|
|
101
|
-
|
|
102
|
-
|
|
101
|
+
background-color: colors.$muted;
|
|
102
|
+
border-radius: 50%;
|
|
103
|
+
display: block;
|
|
104
|
+
width: 22px;
|
|
105
|
+
height: 22px;
|
|
106
|
+
overflow: hidden;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
.editor-device-button img {
|
|
110
|
+
position: absolute;
|
|
111
|
+
top: 50%;
|
|
112
|
+
left: 50%;
|
|
113
|
+
transform: translate(-50%, -50%);
|
|
114
|
+
max-width: 11px;
|
|
115
|
+
max-height: 11px;
|
|
116
|
+
width: auto;
|
|
117
|
+
height: auto;
|
|
103
118
|
}
|
|
104
119
|
|
|
105
120
|
.editor-header__actions {
|
|
@@ -4,20 +4,3 @@
|
|
|
4
4
|
height: 1px;
|
|
5
5
|
width: 100%;
|
|
6
6
|
}
|
|
7
|
-
|
|
8
|
-
.onboard__buttons {
|
|
9
|
-
display: flex;
|
|
10
|
-
height: 40px;
|
|
11
|
-
gap: 12px;
|
|
12
|
-
align-items: center;
|
|
13
|
-
justify-content: center;
|
|
14
|
-
margin: 12px 24px;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
.onboard__buttons--row {
|
|
18
|
-
flex-direction: row;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
.onboard__buttons--column {
|
|
22
|
-
flex-direction: column;
|
|
23
|
-
}
|
|
@@ -78,10 +78,7 @@ $primaryForeground: hsl(
|
|
|
78
78
|
);
|
|
79
79
|
$destructive: hsl(var(--destructive, var(--rb-destructive, 0 84.2% 60.2%)));
|
|
80
80
|
$destructiveForeground: hsl(
|
|
81
|
-
var(
|
|
82
|
-
--destructive-foreground,
|
|
83
|
-
var(--rb-destructive-foreground, 0 0% 100%)
|
|
84
|
-
)
|
|
81
|
+
var(--destructive-foreground, var(--rb-destructive-foreground, 0 0% 100%))
|
|
85
82
|
);
|
|
86
83
|
$border: hsl(var(--border, var(--rb-border, 220 13% 91%)));
|
|
87
84
|
|
|
@@ -147,11 +147,19 @@
|
|
|
147
147
|
position: absolute;
|
|
148
148
|
bottom: -10px;
|
|
149
149
|
font-size: 8px;
|
|
150
|
+
left: 0;
|
|
151
|
+
right: 0;
|
|
152
|
+
display: flex;
|
|
153
|
+
align-items: center;
|
|
154
|
+
justify-content: space-between;
|
|
155
|
+
padding-left: sizes.$spaceComfy;
|
|
156
|
+
padding-right: sizes.$spaceTight;
|
|
150
157
|
}
|
|
151
158
|
|
|
152
159
|
.builder__node {
|
|
153
160
|
@include card;
|
|
154
161
|
padding: sizes.$spaceSnug;
|
|
162
|
+
box-shadow: none;
|
|
155
163
|
}
|
|
156
164
|
|
|
157
165
|
.builder__node-type {
|
|
@@ -178,6 +186,18 @@
|
|
|
178
186
|
}
|
|
179
187
|
}
|
|
180
188
|
|
|
189
|
+
.builder__button-condition-text {
|
|
190
|
+
color: colors.$mutedTextColor;
|
|
191
|
+
white-space: nowrap;
|
|
192
|
+
overflow: hidden;
|
|
193
|
+
text-overflow: ellipsis;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
.builder__button-children-count {
|
|
197
|
+
color: colors.$mutedTextColor;
|
|
198
|
+
white-space: nowrap;
|
|
199
|
+
}
|
|
200
|
+
|
|
181
201
|
.builder__text,
|
|
182
202
|
.builder__placeholder {
|
|
183
203
|
color: colors.$mutedTextColor;
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import type { NodeData } from '../../types/Node';
|
|
2
|
-
import type {
|
|
2
|
+
import type {
|
|
3
|
+
TextPropsGenerated,
|
|
4
|
+
TextStyleGenerated,
|
|
5
|
+
} from '../../build-components/Text/TextProps.generated';
|
|
3
6
|
import type { AppConfig } from '../../types/PreviewConfig';
|
|
4
7
|
import { defaultAppConfig } from '../../types/PreviewConfig';
|
|
5
8
|
import type { ProjectColors } from '../../types/Project';
|
|
@@ -86,6 +89,7 @@ export type ExtractTextStyleOptions = {
|
|
|
86
89
|
fonts?: Fonts;
|
|
87
90
|
onFontLoaded?: (fontFamily: string) => void;
|
|
88
91
|
onError?: (error: string) => void;
|
|
92
|
+
directlyTextStyle?: boolean;
|
|
89
93
|
};
|
|
90
94
|
|
|
91
95
|
export function extractTextStyle<T extends TextPropsGenerated['attributes']>(
|
|
@@ -93,13 +97,13 @@ export function extractTextStyle<T extends TextPropsGenerated['attributes']>(
|
|
|
93
97
|
options: ExtractTextStyleOptions = {},
|
|
94
98
|
) {
|
|
95
99
|
const attributes = node.attributes;
|
|
96
|
-
const styleBag = (attributes as
|
|
97
|
-
|
|
|
100
|
+
const styleBag = (attributes as Record<string, unknown>)?.style as
|
|
101
|
+
| TextStyleGenerated
|
|
98
102
|
| undefined;
|
|
99
103
|
const get = (key: string): unknown => {
|
|
100
|
-
const direct = (attributes as
|
|
104
|
+
const direct = (attributes as Record<string, unknown>)?.[key];
|
|
101
105
|
if (direct !== undefined && direct !== null) return direct;
|
|
102
|
-
return styleBag?.[key];
|
|
106
|
+
return styleBag?.[key as keyof TextStyleGenerated];
|
|
103
107
|
};
|
|
104
108
|
const resolvedAppConfig = options.appConfig ?? defaultAppConfig;
|
|
105
109
|
const { screenStyle, theme } = resolvedAppConfig;
|
|
@@ -115,15 +119,15 @@ export function extractTextStyle<T extends TextPropsGenerated['attributes']>(
|
|
|
115
119
|
}
|
|
116
120
|
|
|
117
121
|
// Typography
|
|
118
|
-
const fontSize = get('fontSize') as
|
|
122
|
+
const fontSize = get('fontSize') as string | number | undefined;
|
|
119
123
|
if (fontSize !== undefined) {
|
|
120
124
|
const parsed = parseSize(fontSize);
|
|
121
125
|
style.fontSize = parsed as React.CSSProperties['fontSize'];
|
|
122
126
|
} else {
|
|
123
127
|
style.fontSize = fs(14);
|
|
124
128
|
}
|
|
125
|
-
const fontFamily = get('fontFamily') as
|
|
126
|
-
const fontWeight = get('fontWeight') as
|
|
129
|
+
const fontFamily = get('fontFamily') as string | undefined;
|
|
130
|
+
const fontWeight = get('fontWeight') as string | number | undefined;
|
|
127
131
|
const requestedWeight = weightToNumericKey(fontWeight);
|
|
128
132
|
const normalizedFontFamily =
|
|
129
133
|
typeof fontFamily === 'string' && fontFamily.trim().length > 0
|
|
@@ -163,18 +167,48 @@ export function extractTextStyle<T extends TextPropsGenerated['attributes']>(
|
|
|
163
167
|
// If no fontFamily is set, keep previous behavior.
|
|
164
168
|
if (!normalizedFontFamily && normalizedFontWeight)
|
|
165
169
|
style.fontWeight = normalizedFontWeight;
|
|
166
|
-
const resolvedTextColor = parseColor(get('color') as
|
|
170
|
+
const resolvedTextColor = parseColor(get('color') as string | undefined, {
|
|
167
171
|
projectColors: options.projectColors,
|
|
168
172
|
theme,
|
|
169
173
|
});
|
|
170
174
|
style.color = resolvedTextColor ?? fallbackColor;
|
|
171
|
-
const textAlign = get('textAlign')
|
|
172
|
-
|
|
173
|
-
|
|
175
|
+
const textAlign = get('textAlign') as
|
|
176
|
+
| React.CSSProperties['textAlign']
|
|
177
|
+
| undefined;
|
|
178
|
+
if (textAlign) {
|
|
179
|
+
style.textAlign = textAlign;
|
|
180
|
+
}
|
|
174
181
|
|
|
175
182
|
const viewStyle = extractViewStyle(node, {
|
|
176
183
|
projectColors: options.projectColors,
|
|
177
184
|
theme,
|
|
178
185
|
});
|
|
179
|
-
|
|
186
|
+
const fullStyle = { ...viewStyle, ...style };
|
|
187
|
+
|
|
188
|
+
// If directlyTextStyle is true, return only text-related styles
|
|
189
|
+
if (options.directlyTextStyle) {
|
|
190
|
+
const textStyleProperties = new Set<keyof React.CSSProperties>([
|
|
191
|
+
'fontSize',
|
|
192
|
+
'fontFamily',
|
|
193
|
+
'fontWeight',
|
|
194
|
+
'color',
|
|
195
|
+
'textAlign',
|
|
196
|
+
'lineHeight',
|
|
197
|
+
'letterSpacing',
|
|
198
|
+
'textDecoration',
|
|
199
|
+
'textTransform',
|
|
200
|
+
]);
|
|
201
|
+
|
|
202
|
+
const textStyle: React.CSSProperties = {};
|
|
203
|
+
for (const key in fullStyle) {
|
|
204
|
+
const typedKey = key as keyof React.CSSProperties;
|
|
205
|
+
if (textStyleProperties.has(typedKey)) {
|
|
206
|
+
(textStyle as Record<string, unknown>)[key] = fullStyle[typedKey];
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
return textStyle;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
return fullStyle;
|
|
180
214
|
}
|