@developer_tribe/react-builder 1.2.14 → 1.2.17
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/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/CountDown.d.ts +2 -0
- package/dist/build-components/CountDown/CountDownProps.generated.d.ts +61 -0
- package/dist/build-components/CountDown/formatCountdownTime.d.ts +1 -0
- package/dist/build-components/Counter/Counter.d.ts +2 -0
- package/dist/build-components/Counter/CounterProps.generated.d.ts +61 -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/PaywallCloseButton.d.ts +1 -1
- package/dist/build-components/PaywallCloseButton/PaywallCloseButtonProps.generated.d.ts +2 -0
- package/dist/build-components/PaywallCounter/PaywallCounter.d.ts +2 -0
- package/dist/build-components/PaywallCounter/PaywallCounterProps.generated.d.ts +61 -0
- package/dist/build-components/PaywallOptions/PaywallOptionsProps.generated.d.ts +2 -0
- package/dist/build-components/PaywallProvider/PaywallContext.d.ts +2 -1
- package/dist/build-components/PaywallProvider/PaywallProviderProps.generated.d.ts +3 -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/ViewProps.generated.d.ts +2 -0
- package/dist/build-components/index.d.ts +4 -1
- package/dist/build-components/patterns.generated.d.ts +2969 -1550
- package/dist/components/BuilderProvider.d.ts +0 -1
- package/dist/index.cjs.js +4 -4
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.esm.js +4 -4
- package/dist/index.esm.js.map +1 -1
- package/dist/index.native.d.ts +7 -0
- package/dist/index.web.cjs.js +7 -5
- package/dist/index.web.cjs.js.map +1 -1
- package/dist/index.web.esm.js +7 -5
- package/dist/index.web.esm.js.map +1 -1
- package/dist/mockOS/backHandler.d.ts +4 -0
- package/dist/paywall/hooks/index.d.ts +3 -3
- package/dist/paywall/hooks/useChangeDelayByPaywall.d.ts +4 -0
- package/dist/paywall/hooks/useHandleGoBack.d.ts +1 -0
- package/dist/paywall/hooks/useMockOSBackHandler.d.ts +1 -0
- package/dist/utils/getDefaultProject.d.ts +4 -0
- package/dist/utils/patterns.d.ts +3 -0
- package/package.json +5 -1
- package/scripts/prebuild/utils/createComponentTsx.js +1 -1
- package/scripts/prebuild/utils/createGeneratedProps.js +6 -2
- package/scripts/prebuild/utils/createRenderNodeGenerated.js +26 -5
- package/scripts/prebuild/utils/validateAllComponentsOrThrow.js +17 -5
- package/scripts/prebuild/utils/validateExistingComponentTsx.js +12 -8
- package/src/AttributesEditor.tsx +7 -2
- package/src/RenderPage.tsx +34 -3
- package/src/assets/meta.json +1 -1
- package/src/assets/samples/carousel-sample.json +44 -14
- package/src/assets/samples/getSamples.ts +3 -1
- package/src/assets/samples/paywall-1.json +73 -29
- package/src/assets/samples/paywall-2.json +339 -0
- package/src/assets/samples/simple-1.json +9 -3
- package/src/assets/samples/simple-2.json +72 -24
- package/src/assets/samples/unmigrated-builder-1.1.1.json +19 -7
- package/src/assets/samples/unmigrated-builder1.json +10 -4
- package/src/assets/samples/unvalidated-builder1.json +10 -4
- package/src/assets/samples/unvalidated-crash1.json +3 -1
- package/src/assets/samples/unvalidated-crashcomponent1.json +3 -1
- package/src/assets/samples/vpn-onboard-1.json +88 -40
- package/src/assets/samples/vpn-onboard-2.json +85 -37
- package/src/assets/samples/vpn-onboard-3.json +93 -45
- package/src/assets/samples/vpn-onboard-4.json +93 -45
- package/src/assets/samples/vpn-onboard-5.json +121 -53
- package/src/assets/samples/vpn-onboard-6.json +93 -45
- package/src/attributes-editor/AttributesEditorView.tsx +39 -1
- package/src/attributes-editor/Field.tsx +30 -0
- package/src/build-components/BIcon/BIconProps.generated.ts +2 -0
- package/src/build-components/BIcon/pattern.json +2 -0
- package/src/build-components/BackgroundImage/BackgroundImageProps.generated.ts +2 -0
- package/src/build-components/BackgroundImage/pattern.json +2 -0
- package/src/build-components/Button/ButtonProps.generated.ts +2 -0
- package/src/build-components/Button/pattern.json +2 -0
- package/src/build-components/Carousel/CarouselProps.generated.ts +2 -0
- package/src/build-components/Carousel/pattern.json +2 -0
- package/src/build-components/CarouselButtons/CarouselButtonsProps.generated.ts +2 -0
- package/src/build-components/CarouselButtons/pattern.json +2 -0
- package/src/build-components/CarouselDots/CarouselDotsProps.generated.ts +2 -0
- package/src/build-components/CarouselDots/pattern.json +2 -0
- package/src/build-components/CarouselItem/CarouselItemProps.generated.ts +2 -0
- package/src/build-components/CarouselItem/pattern.json +4 -1
- package/src/build-components/CarouselProvider/CarouselProviderProps.generated.ts +2 -0
- package/src/build-components/CarouselProvider/pattern.json +4 -1
- package/src/build-components/CountDown/CountDown.tsx +81 -0
- package/src/build-components/CountDown/CountDownProps.generated.ts +78 -0
- package/src/build-components/CountDown/formatCountdownTime.ts +10 -0
- package/src/build-components/CountDown/pattern.json +24 -0
- package/src/build-components/Counter/Counter.tsx +44 -0
- package/src/build-components/Counter/CounterProps.generated.ts +78 -0
- package/src/build-components/Counter/pattern.json +26 -0
- package/src/build-components/Image/ImageProps.generated.ts +2 -0
- package/src/build-components/Image/pattern.json +2 -0
- package/src/build-components/Main/MainProps.generated.ts +2 -0
- package/src/build-components/Main/pattern.json +2 -0
- package/src/build-components/Onboard/OnboardProps.generated.ts +2 -0
- package/src/build-components/Onboard/pattern.json +4 -1
- package/src/build-components/OnboardButton/OnboardButtonProps.generated.ts +2 -0
- package/src/build-components/OnboardButton/pattern.json +2 -0
- package/src/build-components/OnboardButtons/OnboardButtonsProps.generated.ts +2 -0
- package/src/build-components/OnboardButtons/pattern.json +2 -0
- package/src/build-components/OnboardDot/OnboardDotProps.generated.ts +2 -0
- package/src/build-components/OnboardDot/pattern.json +2 -0
- package/src/build-components/OnboardFooter/OnboardFooterProps.generated.ts +2 -0
- package/src/build-components/OnboardFooter/pattern.json +2 -0
- package/src/build-components/OnboardImage/OnboardImageProps.generated.ts +2 -0
- package/src/build-components/OnboardImage/pattern.json +2 -0
- package/src/build-components/OnboardItem/OnboardItemProps.generated.ts +2 -0
- package/src/build-components/OnboardItem/pattern.json +2 -0
- package/src/build-components/OnboardProvider/OnboardProviderProps.generated.ts +2 -0
- package/src/build-components/OnboardProvider/pattern.json +2 -0
- package/src/build-components/OnboardSubtitle/OnboardSubtitleProps.generated.ts +2 -0
- package/src/build-components/OnboardSubtitle/pattern.json +4 -1
- package/src/build-components/OnboardTitle/OnboardTitleProps.generated.ts +2 -0
- package/src/build-components/OnboardTitle/pattern.json +4 -1
- package/src/build-components/PaywallBackground/PaywallBackgroundProps.generated.ts +2 -0
- package/src/build-components/PaywallBackground/pattern.json +4 -1
- package/src/build-components/PaywallCloseButton/PaywallCloseButton.tsx +12 -6
- package/src/build-components/PaywallCloseButton/PaywallCloseButtonProps.generated.ts +2 -0
- package/src/build-components/PaywallCloseButton/pattern.json +7 -1
- package/src/build-components/PaywallCounter/PaywallCounter.tsx +46 -0
- package/src/build-components/PaywallCounter/PaywallCounterProps.generated.ts +78 -0
- package/src/build-components/PaywallCounter/pattern.json +24 -0
- package/src/build-components/PaywallOptions/PaywallOptions.tsx +2 -2
- package/src/build-components/PaywallOptions/PaywallOptionsProps.generated.ts +2 -0
- package/src/build-components/PaywallOptions/pattern.json +4 -1
- package/src/build-components/PaywallProvider/PaywallContext.ts +4 -2
- package/src/build-components/PaywallProvider/PaywallProvider.tsx +51 -21
- package/src/build-components/PaywallProvider/PaywallProviderProps.generated.ts +3 -0
- package/src/build-components/PaywallProvider/pattern.json +14 -1
- package/src/build-components/PaywallSubscribeButton/PaywallSubscribeButtonProps.generated.ts +2 -0
- package/src/build-components/PaywallSubscribeButton/pattern.json +4 -1
- package/src/build-components/RadioButton/RadioButtonProps.generated.ts +2 -0
- package/src/build-components/RadioButton/pattern.json +2 -0
- package/src/build-components/RenderNode.generated.tsx +17 -2
- package/src/build-components/Text/TextProps.generated.ts +2 -0
- package/src/build-components/Text/pattern.json +3 -1
- package/src/build-components/View/View.tsx +1 -1
- package/src/build-components/View/ViewProps.generated.ts +2 -0
- package/src/build-components/View/pattern.json +35 -38
- package/src/build-components/index.ts +15 -0
- package/src/build-components/patterns.generated.ts +3094 -1569
- package/src/components/BuilderButton.tsx +23 -6
- package/src/components/BuilderProvider.tsx +10 -7
- package/src/components/DeviceNavigationBar.tsx +5 -0
- package/src/index.native.ts +7 -0
- package/src/index.ts +2 -3
- package/src/mockOS/backHandler.ts +35 -0
- package/src/mockOS/context/MockOSContext.tsx +22 -8
- package/src/mockOS/managers/navigationManager.ts +4 -0
- package/src/pages/ProjectMigrationPage.tsx +10 -1
- package/src/pages/ProjectPage.tsx +7 -0
- package/src/pages/projectPageUtils.ts +3 -2
- package/src/paywall/hooks/index.ts +3 -3
- package/src/paywall/hooks/useChangeDelayByPaywall.ts +25 -0
- package/src/paywall/hooks/useHandleGoBack.ts +60 -0
- package/src/paywall/hooks/useMockOSBackHandler.ts +9 -0
- package/src/utils/analyseNodeByPatterns.ts +21 -0
- package/src/utils/getDefaultProject.ts +34 -0
- package/src/utils/patterns.ts +6 -7
- package/dist/paywall/hooks/useCarouselOptionsSeperator.d.ts +0 -6
- package/dist/paywall/hooks/useCloseStatusPaywall.d.ts +0 -4
- package/dist/paywall/hooks/usePaywallCounter.d.ts +0 -4
- package/src/paywall/hooks/useCarouselOptionsSeperator.ts +0 -8
- package/src/paywall/hooks/useCloseStatusPaywall.ts +0 -6
- package/src/paywall/hooks/usePaywallCounter.ts +0 -6
|
@@ -68,14 +68,22 @@ export function BuilderButton({
|
|
|
68
68
|
}
|
|
69
69
|
|
|
70
70
|
const nodeData = node as NodeData<NodeDefaultAttribute>;
|
|
71
|
-
let
|
|
71
|
+
let conditionText = '';
|
|
72
72
|
if (nodeData.attributes?.condition) {
|
|
73
|
-
|
|
73
|
+
conditionText =
|
|
74
|
+
`${nodeData.attributes.condition} ${nodeData.attributes.conditionVariable ?? ''}`.trim();
|
|
74
75
|
}
|
|
75
76
|
const patternLabel = getPatternByType(nodeData.type)?.meta?.label?.trim();
|
|
76
77
|
const baseLabel =
|
|
77
78
|
patternLabel && patternLabel.length > 0 ? patternLabel : nodeData.type;
|
|
78
|
-
const conditionLabel =
|
|
79
|
+
const conditionLabel = conditionText.trim() ? conditionText : '';
|
|
80
|
+
const title =
|
|
81
|
+
typeof nodeData.attributes?.title === 'string'
|
|
82
|
+
? nodeData.attributes.title.trim()
|
|
83
|
+
: '';
|
|
84
|
+
const hasTitle = title.length > 0;
|
|
85
|
+
const topLabel = hasTitle ? title : baseLabel;
|
|
86
|
+
const shouldShowBottom = hasTitle || conditionLabel.length > 0;
|
|
79
87
|
|
|
80
88
|
return (
|
|
81
89
|
<div className="builder__button">
|
|
@@ -108,7 +116,7 @@ export function BuilderButton({
|
|
|
108
116
|
</div>
|
|
109
117
|
)}
|
|
110
118
|
<button type="button" className="builder__button-link" onClick={onClick}>
|
|
111
|
-
{
|
|
119
|
+
{topLabel}
|
|
112
120
|
</button>
|
|
113
121
|
<div className="builder__button-actions" ref={actionsRef}>
|
|
114
122
|
<button
|
|
@@ -153,8 +161,17 @@ export function BuilderButton({
|
|
|
153
161
|
</ul>
|
|
154
162
|
)}
|
|
155
163
|
</div>
|
|
156
|
-
{
|
|
157
|
-
<span className="builder__button-condition">
|
|
164
|
+
{shouldShowBottom && (
|
|
165
|
+
<span className="builder__button-condition">
|
|
166
|
+
{hasTitle ? (
|
|
167
|
+
<>
|
|
168
|
+
<small>{baseLabel}</small>
|
|
169
|
+
{conditionLabel ? ` · ${conditionLabel}` : ''}
|
|
170
|
+
</>
|
|
171
|
+
) : (
|
|
172
|
+
conditionLabel
|
|
173
|
+
)}
|
|
174
|
+
</span>
|
|
158
175
|
)}
|
|
159
176
|
</div>
|
|
160
177
|
);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React, { createContext, useContext, useMemo } from 'react';
|
|
1
|
+
import React, { createContext, useContext, useEffect, useMemo } from 'react';
|
|
2
2
|
import type { Product } from '../paywall/types/paywall-types';
|
|
3
3
|
import type { PaywallBenefits } from '../paywall/types/benefits';
|
|
4
4
|
import type { AppConfig } from '../types/PreviewConfig';
|
|
@@ -21,7 +21,6 @@ export type BuilderProviderParams = {
|
|
|
21
21
|
* - 'native': returns React Native-friendly styles (no DOM-only props like overflowX/Y).
|
|
22
22
|
*/
|
|
23
23
|
platform?: 'web' | 'native';
|
|
24
|
-
onPaywallClose?: () => void;
|
|
25
24
|
onPaywallSubscribe?: (product?: Product) => void | boolean | Promise<boolean>;
|
|
26
25
|
/**
|
|
27
26
|
* Optional runtime config + styling inputs.
|
|
@@ -56,10 +55,6 @@ export function BuilderProvider({ params, children }: BuilderProviderProps) {
|
|
|
56
55
|
params?.benefits && typeof params.benefits === 'object'
|
|
57
56
|
? (params.benefits as PaywallBenefits)
|
|
58
57
|
: {},
|
|
59
|
-
onPaywallClose:
|
|
60
|
-
typeof params?.onPaywallClose === 'function'
|
|
61
|
-
? params.onPaywallClose
|
|
62
|
-
: undefined,
|
|
63
58
|
onPaywallSubscribe:
|
|
64
59
|
typeof params?.onPaywallSubscribe === 'function'
|
|
65
60
|
? params.onPaywallSubscribe
|
|
@@ -85,7 +80,6 @@ export function BuilderProvider({ params, children }: BuilderProviderProps) {
|
|
|
85
80
|
params?.benefits,
|
|
86
81
|
params?.products,
|
|
87
82
|
params?.platform,
|
|
88
|
-
params?.onPaywallClose,
|
|
89
83
|
params?.onPaywallSubscribe,
|
|
90
84
|
params?.appConfig,
|
|
91
85
|
params?.projectColors,
|
|
@@ -96,6 +90,15 @@ export function BuilderProvider({ params, children }: BuilderProviderProps) {
|
|
|
96
90
|
],
|
|
97
91
|
);
|
|
98
92
|
|
|
93
|
+
useEffect(() => {
|
|
94
|
+
console.info('[preview] BuilderProvider params', {
|
|
95
|
+
previewMode: value.previewMode,
|
|
96
|
+
selectedKey: value.selectedKey,
|
|
97
|
+
platform: value.platform,
|
|
98
|
+
productsCount: Array.isArray(value.products) ? value.products.length : 0,
|
|
99
|
+
});
|
|
100
|
+
}, [value.previewMode, value.selectedKey, value.platform, value.products]);
|
|
101
|
+
|
|
99
102
|
return (
|
|
100
103
|
<BuilderContext.Provider value={value}>{children}</BuilderContext.Provider>
|
|
101
104
|
);
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { Device } from '../types/Device';
|
|
3
|
+
import { emitMockOSBackPress } from '../mockOS/backHandler';
|
|
3
4
|
import { useMockOSContext } from '../mockOS/context/MockOSContextBase';
|
|
4
5
|
|
|
5
6
|
type DeviceNavigationBarProps = {
|
|
@@ -37,6 +38,10 @@ export function DeviceNavigationBar({
|
|
|
37
38
|
: 'rgba(0, 0, 0, 0.4)';
|
|
38
39
|
|
|
39
40
|
function handleBackButton() {
|
|
41
|
+
if (!emitMockOSBackPress()) {
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
|
|
40
45
|
if (context) {
|
|
41
46
|
const canGoBack = context.goBack();
|
|
42
47
|
// If can't go back, go to launchscreen
|
package/src/index.ts
CHANGED
|
@@ -27,11 +27,9 @@ export type {
|
|
|
27
27
|
|
|
28
28
|
// Paywall hooks (RN-safe placeholders)
|
|
29
29
|
export {
|
|
30
|
-
usePaywallCounter,
|
|
31
|
-
useCloseStatusPaywall,
|
|
32
|
-
useCarouselOptionsSeperator,
|
|
33
30
|
useCalculateLocalizedPrice,
|
|
34
31
|
useDiscountRate,
|
|
32
|
+
useChangeDelayByPaywall,
|
|
35
33
|
} from './paywall/hooks';
|
|
36
34
|
|
|
37
35
|
// Context (RN-safe). In React Native, `products` should come from an IAP wrapper
|
|
@@ -91,4 +89,5 @@ export {
|
|
|
91
89
|
getBasicSamples,
|
|
92
90
|
getOnboardSamples,
|
|
93
91
|
} from './assets/samples/getSamples';
|
|
92
|
+
export { getDefaultProject } from './utils/getDefaultProject';
|
|
94
93
|
export type { EventObjectGenerated } from './build-components/OnboardButton/OnboardButtonProps.generated';
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
export type MockOSBackHandler = () => boolean;
|
|
2
|
+
|
|
3
|
+
const backHandlers: MockOSBackHandler[] = [];
|
|
4
|
+
|
|
5
|
+
export function registerMockOSBackHandler(
|
|
6
|
+
handler: MockOSBackHandler,
|
|
7
|
+
): () => void {
|
|
8
|
+
backHandlers.push(handler);
|
|
9
|
+
|
|
10
|
+
return () => {
|
|
11
|
+
const index = backHandlers.lastIndexOf(handler);
|
|
12
|
+
if (index >= 0) {
|
|
13
|
+
backHandlers.splice(index, 1);
|
|
14
|
+
}
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function emitMockOSBackPress(): boolean {
|
|
19
|
+
if (backHandlers.length === 0) {
|
|
20
|
+
return true;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
for (let i = backHandlers.length - 1; i >= 0; i -= 1) {
|
|
24
|
+
const allow = backHandlers[i]?.();
|
|
25
|
+
if (!allow) {
|
|
26
|
+
return false;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
return true;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export function clearMockOSBackHandlers(): void {
|
|
34
|
+
backHandlers.length = 0;
|
|
35
|
+
}
|
|
@@ -78,6 +78,9 @@ export function MockOSProvider({
|
|
|
78
78
|
// replace it instead of adding new item
|
|
79
79
|
setNavigationStack((prev) => {
|
|
80
80
|
const lastItem = prev[prev.length - 1];
|
|
81
|
+
if (lastItem && lastItem.route === route) {
|
|
82
|
+
return prev;
|
|
83
|
+
}
|
|
81
84
|
if (
|
|
82
85
|
lastItem &&
|
|
83
86
|
lastItem.route === 'launchscreen' &&
|
|
@@ -94,17 +97,28 @@ export function MockOSProvider({
|
|
|
94
97
|
}, []);
|
|
95
98
|
|
|
96
99
|
const goBack = useCallback(() => {
|
|
97
|
-
if (navigationStack.length
|
|
98
|
-
|
|
100
|
+
if (navigationStack.length <= 1) {
|
|
101
|
+
return false;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const newStack = [...navigationStack];
|
|
105
|
+
// Pop once, then keep popping if the route doesn't change (avoid duplicates).
|
|
106
|
+
do {
|
|
99
107
|
newStack.pop();
|
|
100
|
-
|
|
108
|
+
} while (
|
|
109
|
+
newStack.length > 0 &&
|
|
110
|
+
newStack[newStack.length - 1].route === currentRoute
|
|
111
|
+
);
|
|
101
112
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
return true;
|
|
113
|
+
if (newStack.length === 0) {
|
|
114
|
+
return false;
|
|
105
115
|
}
|
|
106
|
-
|
|
107
|
-
|
|
116
|
+
|
|
117
|
+
const previousRoute = newStack[newStack.length - 1];
|
|
118
|
+
setCurrentRoute(previousRoute.route);
|
|
119
|
+
setNavigationStack(newStack);
|
|
120
|
+
return true;
|
|
121
|
+
}, [currentRoute, navigationStack]);
|
|
108
122
|
|
|
109
123
|
const requestSubscriptionPurchase = useCallback((productId: string) => {
|
|
110
124
|
// Cancel any existing pending request (resolve false)
|
|
@@ -71,7 +71,16 @@ export function ProjectMigrationPage({
|
|
|
71
71
|
type="button"
|
|
72
72
|
className="editor-button"
|
|
73
73
|
disabled={!!migrating}
|
|
74
|
-
onClick={() =>
|
|
74
|
+
onClick={() => {
|
|
75
|
+
console.info('[ProjectMigrationPage] onMigrateNow clicked', {
|
|
76
|
+
name,
|
|
77
|
+
projectVersion,
|
|
78
|
+
requiredVersion,
|
|
79
|
+
pendingMigrationsCount: pendingMigrations.length,
|
|
80
|
+
migrating: !!migrating,
|
|
81
|
+
});
|
|
82
|
+
onMigrateNow?.();
|
|
83
|
+
}}
|
|
75
84
|
>
|
|
76
85
|
{migrating ? 'Migrating…' : 'Migrate now'}
|
|
77
86
|
</button>
|
|
@@ -289,6 +289,13 @@ export function ProjectPage({
|
|
|
289
289
|
<EditorHeader
|
|
290
290
|
onSaveProject={() => {
|
|
291
291
|
try {
|
|
292
|
+
console.info('[ProjectPage] onSaveProject clicked', {
|
|
293
|
+
name: project.name,
|
|
294
|
+
hasOverrideProject: !!overrideProject,
|
|
295
|
+
hasEditorData: !!editorData,
|
|
296
|
+
hasResolvedProjectColors: !!resolvedProjectColors,
|
|
297
|
+
bypassValidation,
|
|
298
|
+
});
|
|
292
299
|
logger.info('ProjectPage', 'save project', { name: project.name });
|
|
293
300
|
if (onSaveProjectColors && resolvedProjectColors) {
|
|
294
301
|
onSaveProjectColors(resolvedProjectColors);
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { Project } from '../types/Project';
|
|
2
2
|
import type { Node } from '../types/Node';
|
|
3
|
+
import { getDefaultProject } from '../utils/getDefaultProject';
|
|
3
4
|
|
|
4
5
|
export function resolveProjectForSave(args: {
|
|
5
6
|
project: Project;
|
|
@@ -7,8 +8,8 @@ export function resolveProjectForSave(args: {
|
|
|
7
8
|
data: Node;
|
|
8
9
|
}): Project {
|
|
9
10
|
const base = args.overrideProject ?? args.project;
|
|
10
|
-
return {
|
|
11
|
+
return getDefaultProject({
|
|
11
12
|
...base,
|
|
12
13
|
data: args.data,
|
|
13
|
-
};
|
|
14
|
+
});
|
|
14
15
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
export { usePaywallCounter } from './usePaywallCounter';
|
|
2
|
-
export { useCloseStatusPaywall } from './useCloseStatusPaywall';
|
|
3
|
-
export { useCarouselOptionsSeperator } from './useCarouselOptionsSeperator';
|
|
4
1
|
export { useCalculateLocalizedPrice } from './useCalculateLocalizedPrice';
|
|
5
2
|
export { useDiscountRate } from './useDiscountRate';
|
|
3
|
+
export { useChangeDelayByPaywall } from './useChangeDelayByPaywall';
|
|
4
|
+
export { useHandleGoBack } from './useHandleGoBack';
|
|
5
|
+
export { useMockOSBackHandler } from './useMockOSBackHandler';
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { useEffect, useMemo } from 'react';
|
|
2
|
+
import type { NodeData } from '../../types/Node';
|
|
3
|
+
|
|
4
|
+
export function useChangeDelayByPaywall(
|
|
5
|
+
node: NodeData<{ delay?: number }> | null | undefined,
|
|
6
|
+
setIsBackAllowed: (value: boolean) => void,
|
|
7
|
+
) {
|
|
8
|
+
const delay = useMemo(() => {
|
|
9
|
+
const delayAttribute = node?.attributes?.delay
|
|
10
|
+
? parseInt(String(node?.attributes?.delay), 10)
|
|
11
|
+
: 1000;
|
|
12
|
+
return delayAttribute;
|
|
13
|
+
}, [node?.attributes?.delay]);
|
|
14
|
+
|
|
15
|
+
useEffect(() => {
|
|
16
|
+
if (!delay || delay <= 0) {
|
|
17
|
+
setIsBackAllowed(true);
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
const timeout = setTimeout(() => {
|
|
21
|
+
setIsBackAllowed(true);
|
|
22
|
+
}, delay);
|
|
23
|
+
return () => clearTimeout(timeout);
|
|
24
|
+
}, [delay, setIsBackAllowed]);
|
|
25
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { useEffect } from 'react';
|
|
2
|
+
import { useMockOSContext } from '../../mockOS/context/MockOSContextBase';
|
|
3
|
+
|
|
4
|
+
export function useHandleGoBack(
|
|
5
|
+
handleGoBack: () => boolean | null | undefined,
|
|
6
|
+
) {
|
|
7
|
+
const mockOS = useMockOSContext();
|
|
8
|
+
|
|
9
|
+
useEffect(() => {
|
|
10
|
+
if (!mockOS?.isEnabled) {
|
|
11
|
+
return;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const handleBackPress = (event?: KeyboardEvent | PopStateEvent) => {
|
|
15
|
+
const result = handleGoBack();
|
|
16
|
+
|
|
17
|
+
// Only allow navigation if result is explicitly true
|
|
18
|
+
if (result !== true) {
|
|
19
|
+
// Prevent navigation
|
|
20
|
+
if (event instanceof KeyboardEvent) {
|
|
21
|
+
event.preventDefault();
|
|
22
|
+
event.stopPropagation();
|
|
23
|
+
} else if (event instanceof PopStateEvent) {
|
|
24
|
+
// Prevent browser navigation by pushing state back
|
|
25
|
+
window.history.pushState(null, '', window.location.href);
|
|
26
|
+
}
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// Allow navigation - execute MockOS goBack
|
|
31
|
+
const canGoBack = mockOS.goBack();
|
|
32
|
+
if (!canGoBack) {
|
|
33
|
+
mockOS.navigation('launchscreen');
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
// Handle Escape key
|
|
38
|
+
const handleKeyDown = (e: KeyboardEvent) => {
|
|
39
|
+
if (e.key === 'Escape') {
|
|
40
|
+
handleBackPress(e);
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
// Handle browser back button
|
|
45
|
+
const handlePopState = (e: PopStateEvent) => {
|
|
46
|
+
handleBackPress(e);
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
// Push a state to track back navigation
|
|
50
|
+
window.history.pushState(null, '', window.location.href);
|
|
51
|
+
|
|
52
|
+
window.addEventListener('keydown', handleKeyDown);
|
|
53
|
+
window.addEventListener('popstate', handlePopState);
|
|
54
|
+
|
|
55
|
+
return () => {
|
|
56
|
+
window.removeEventListener('keydown', handleKeyDown);
|
|
57
|
+
window.removeEventListener('popstate', handlePopState);
|
|
58
|
+
};
|
|
59
|
+
}, [handleGoBack, mockOS]);
|
|
60
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { useEffect } from 'react';
|
|
2
|
+
import { registerMockOSBackHandler } from '../../mockOS/backHandler';
|
|
3
|
+
|
|
4
|
+
export function useMockOSBackHandler(isBackAllowed: boolean) {
|
|
5
|
+
useEffect(() => {
|
|
6
|
+
const unsubscribe = registerMockOSBackHandler(() => isBackAllowed);
|
|
7
|
+
return unsubscribe;
|
|
8
|
+
}, [isBackAllowed]);
|
|
9
|
+
}
|
|
@@ -244,6 +244,17 @@ function validatePrimitiveValue(
|
|
|
244
244
|
return typeof value === 'string'
|
|
245
245
|
? ok()
|
|
246
246
|
: fail(`Expected color (string)`, path);
|
|
247
|
+
case 'title':
|
|
248
|
+
if (typeof value !== 'string') {
|
|
249
|
+
return fail(`Expected title (string)`, path);
|
|
250
|
+
}
|
|
251
|
+
return value.length <= 20
|
|
252
|
+
? ok()
|
|
253
|
+
: fail(`Expected title to be <= 20 characters`, path);
|
|
254
|
+
case 'description':
|
|
255
|
+
return typeof value === 'string'
|
|
256
|
+
? ok()
|
|
257
|
+
: fail(`Expected description (string)`, path);
|
|
247
258
|
case 'size':
|
|
248
259
|
// Sizes can be numbers or strings like "16@fs" / "100%" / "20px"
|
|
249
260
|
return typeof value === 'number' || typeof value === 'string'
|
|
@@ -403,6 +414,16 @@ function validateAttributesByPattern(
|
|
|
403
414
|
if (attrName === 'style') continue;
|
|
404
415
|
const attrSpec = schema?.[attrName] as AttributeTypeSpec | undefined;
|
|
405
416
|
if (!attrSpec) {
|
|
417
|
+
if (attrName === 'title' || attrName === 'description') {
|
|
418
|
+
const res = validateAttributeValue(
|
|
419
|
+
pattern.pattern.type,
|
|
420
|
+
attrValue,
|
|
421
|
+
attrName,
|
|
422
|
+
joinPath(path, attrName),
|
|
423
|
+
);
|
|
424
|
+
if (!res.valid) return res;
|
|
425
|
+
continue;
|
|
426
|
+
}
|
|
406
427
|
// Back-compat: allow legacy flat style keys even when the schema nests them.
|
|
407
428
|
const legacyStyleSpec = styleSchema?.[attrName] as
|
|
408
429
|
| AttributeTypeSpec
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import metaJson from '../assets/meta.json';
|
|
2
|
+
import type { Project } from '../types/Project';
|
|
3
|
+
import type { Node } from '../types/Node';
|
|
4
|
+
|
|
5
|
+
type GetDefaultProjectArgs = Partial<Project>;
|
|
6
|
+
|
|
7
|
+
export function getDefaultProject(
|
|
8
|
+
overrides: GetDefaultProjectArgs = {},
|
|
9
|
+
): Project {
|
|
10
|
+
const supportedVersion =
|
|
11
|
+
typeof metaJson?.supportedProjectVersion === 'string' &&
|
|
12
|
+
metaJson.supportedProjectVersion.trim()
|
|
13
|
+
? metaJson.supportedProjectVersion.trim()
|
|
14
|
+
: '0.0.0';
|
|
15
|
+
|
|
16
|
+
const version =
|
|
17
|
+
typeof overrides.version === 'string' && overrides.version.trim()
|
|
18
|
+
? overrides.version.trim()
|
|
19
|
+
: supportedVersion;
|
|
20
|
+
|
|
21
|
+
const name =
|
|
22
|
+
typeof overrides.name === 'string' && overrides.name.trim()
|
|
23
|
+
? overrides.name.trim()
|
|
24
|
+
: 'project';
|
|
25
|
+
|
|
26
|
+
return {
|
|
27
|
+
name,
|
|
28
|
+
version,
|
|
29
|
+
data: (overrides.data ?? null) as Node,
|
|
30
|
+
appConfig: overrides.appConfig,
|
|
31
|
+
projectColors: overrides.projectColors,
|
|
32
|
+
type: overrides.type,
|
|
33
|
+
};
|
|
34
|
+
}
|
package/src/utils/patterns.ts
CHANGED
|
@@ -7,7 +7,10 @@ export type AttributeMeta = {
|
|
|
7
7
|
category?: 'style' | 'container' | 'other' | string;
|
|
8
8
|
specialCategory?: 'padding' | 'margin' | 'offset' | null | string;
|
|
9
9
|
sort?: number;
|
|
10
|
+
/** Legacy typo kept for backwards compatibility with older patterns. */
|
|
10
11
|
preferedScale?: 's' | 'vs' | 'f' | '%' | '' | string;
|
|
12
|
+
/** Correct spelling. Prefer this in new/updated patterns. */
|
|
13
|
+
preferredScale?: 's' | 'vs' | 'f' | '%' | '' | string;
|
|
11
14
|
/**
|
|
12
15
|
* When true, this attribute should remain visible even if the component
|
|
13
16
|
* metadata requests hiding all attributes (e.g. `meta.hideAllAttributes`).
|
|
@@ -108,13 +111,7 @@ function adjustMetaForPlatform(
|
|
|
108
111
|
// Values that are not directly style-friendly for the target platform should be shown under "other"
|
|
109
112
|
// (so they remain editable, but are clearly non-style / platform-specific behavior).
|
|
110
113
|
const moveToOtherAlways = new Set<string>(['scrollable']);
|
|
111
|
-
const moveToOtherOnWeb = new Set<string>([
|
|
112
|
-
// RN-only shorthands; on web they are translated to paddingLeft/Right, etc.
|
|
113
|
-
'paddingHorizontal',
|
|
114
|
-
'paddingVertical',
|
|
115
|
-
'marginHorizontal',
|
|
116
|
-
'marginVertical',
|
|
117
|
-
]);
|
|
114
|
+
const moveToOtherOnWeb = new Set<string>([]);
|
|
118
115
|
const moveToOtherOnNative = new Set<string>([
|
|
119
116
|
// CSS-only layout convenience (not widely supported in RN style across versions).
|
|
120
117
|
'gap',
|
|
@@ -277,6 +274,8 @@ export function getTypeSchema(
|
|
|
277
274
|
export function isPrimitiveType(typeName: string): boolean {
|
|
278
275
|
return (
|
|
279
276
|
typeName === 'string' ||
|
|
277
|
+
typeName === 'title' ||
|
|
278
|
+
typeName === 'description' ||
|
|
280
279
|
typeName === 'number' ||
|
|
281
280
|
typeName === 'boolean' ||
|
|
282
281
|
typeName === 'color' ||
|