@developer_tribe/react-builder 1.2.28 → 1.2.30
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/RenderPage.d.ts +7 -2
- package/dist/attributes-editor/attributesEditorModelTypes.d.ts +0 -1
- package/dist/build-components/index.generated.d.ts +38 -0
- package/dist/components/BuilderProvider.d.ts +9 -15
- package/dist/hooks/useLocalize.d.ts +3 -2
- package/dist/hooks/usePreviewSelection.d.ts +12 -0
- package/dist/index.cjs.js +1 -28
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.d.ts +4 -2
- package/dist/index.esm.js +1 -28
- 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/logger.d.ts +3 -6
- package/dist/modals/IconPickerModal.d.ts +1 -1
- package/dist/pages/DebugJsonPage.d.ts +1 -4
- package/dist/product-base/index.d.ts +24 -0
- package/dist/size-matters/index.d.ts +15 -6
- package/dist/store.d.ts +5 -3
- package/dist/types/Icons.generated.d.ts +2 -0
- package/dist/types/PreviewConfig.d.ts +6 -8
- package/dist/types/Project.d.ts +4 -3
- package/dist/utils/extractTextStyle/extractTextStyle.d.ts +2 -0
- package/dist/utils/extractTextStyle/extractTextStyleNative.d.ts +2 -0
- package/dist/utils/extractViewStyle/extractViewStyle.d.ts +2 -0
- package/dist/utils/extractViewStyle/extractViewStyleNative.d.ts +2 -0
- package/package.json +1 -1
- package/scripts/prebuild/utils/validateAllComponentsOrThrow.js +19 -9
- package/src/RenderPage.tsx +66 -57
- package/src/assets/.DS_Store +0 -0
- package/src/assets/meta.json +1 -1
- package/src/assets/samples/carousel-sample.json +2 -6
- package/src/assets/samples/getSamples.ts +14 -4
- package/src/attribute-analyser/style/native/useExtractImageStyle.ts +3 -3
- package/src/attribute-analyser/style/native/useExtractTextStyle.ts +8 -2
- package/src/attribute-analyser/style/native/useExtractViewStyle.ts +7 -3
- package/src/attribute-analyser/style/web/useExtractImageStyle.ts +3 -3
- package/src/attribute-analyser/style/web/useExtractTextStyle.ts +8 -2
- package/src/attribute-analyser/style/web/useExtractViewStyle.ts +3 -3
- package/src/attributes-editor/AttributesEditorFields.tsx +1 -1
- package/src/attributes-editor/attributesEditorModelTypes.ts +0 -3
- package/src/attributes-editor/useAttributesEditorModel.ts +0 -3
- package/src/build-components/BIcon/BIcon.tsx +1 -1
- package/src/build-components/Button/Button.tsx +2 -2
- package/src/build-components/CarouselDots/CarouselDots.tsx +3 -3
- package/src/build-components/OnboardButton/OnboardButton.tsx +2 -2
- package/src/build-components/OnboardDot/OnboardDot.tsx +9 -3
- package/src/build-components/OnboardFooter/OnboardFooter.tsx +4 -5
- package/src/build-components/PaywallCloseButton/PaywallCloseButton.tsx +1 -1
- package/src/build-components/PaywallSubscribeButton/PaywallSubscribeButton.tsx +2 -2
- package/src/build-components/Text/Text.tsx +2 -2
- package/src/build-components/index.generated.ts +184 -0
- package/src/components/BottomBar.tsx +7 -9
- package/src/components/BuilderProvider.tsx +47 -84
- package/src/components/EditorHeader.tsx +6 -3
- package/src/hooks/useLocalize.ts +14 -10
- package/src/hooks/usePreviewSelection.ts +66 -0
- package/src/index.ts +2 -2
- package/src/logger.ts +4 -20
- package/src/modals/IconPickerModal.tsx +1 -1
- package/src/modals/InspectModal.tsx +6 -7
- package/src/modals/ProductPresetsModal.tsx +2 -2
- package/src/pages/DebugJsonPage.tsx +0 -6
- package/src/pages/ProjectPage.tsx +12 -57
- package/src/pages/tabs/SideTool.tsx +7 -7
- package/src/product-base/extractAndroidParams.ts +4 -11
- package/src/product-base/extractIOSParams.ts +4 -10
- package/src/product-base/index.ts +36 -0
- package/src/size-matters/index.ts +44 -31
- package/src/store.ts +12 -6
- package/src/styles/modals/_inspect-modal.scss +7 -3
- package/src/types/Icons.generated.ts +244 -0
- package/src/types/PreviewConfig.ts +5 -9
- package/src/types/Project.ts +4 -3
- package/src/utils/extractImageStyle.ts +4 -2
- package/src/utils/extractTextStyle/extractTextStyle.ts +6 -1
- package/src/utils/extractTextStyle/extractTextStyleNative.ts +4 -1
- package/src/utils/extractViewStyle/extractViewStyle.ts +7 -5
- package/src/utils/extractViewStyle/extractViewStyleNative.ts +3 -1
- package/src/utils/getDefaultProject.ts +0 -1
- package/src/utils/replaceLocalizationParams.ts +1 -1
package/src/hooks/useLocalize.ts
CHANGED
|
@@ -1,27 +1,31 @@
|
|
|
1
1
|
import { useCallback } from 'react';
|
|
2
|
-
import type
|
|
3
|
-
import { defaultAppConfig } from '../types/PreviewConfig';
|
|
2
|
+
import { type Localication, defaultLocalization } from '../types/PreviewConfig';
|
|
4
3
|
import { useBuilderParams } from '../components/BuilderProvider';
|
|
5
4
|
import { useLocalizationParams } from './useLocalizationParams';
|
|
6
5
|
import { replaceLocalizationParams } from '../utils/replaceLocalizationParams';
|
|
7
6
|
|
|
8
7
|
export type LocalizeFn = (keyOrText: string) => string;
|
|
9
8
|
|
|
10
|
-
export function useLocalize(options?: {
|
|
9
|
+
export function useLocalize(options?: {
|
|
10
|
+
localization?: Localication;
|
|
11
|
+
defaultLanguage?: string;
|
|
12
|
+
}): LocalizeFn {
|
|
11
13
|
const {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
+
localization: builderLocalization,
|
|
15
|
+
mockDefaultLanguage: builderDefaultLanguage,
|
|
14
16
|
} = useBuilderParams();
|
|
15
|
-
|
|
16
|
-
const
|
|
17
|
-
|
|
17
|
+
|
|
18
|
+
const localization =
|
|
19
|
+
options?.localization ?? builderLocalization ?? defaultLocalization;
|
|
20
|
+
const defaultLanguage =
|
|
21
|
+
options?.defaultLanguage ?? builderDefaultLanguage ?? 'en';
|
|
18
22
|
const params = useLocalizationParams();
|
|
19
23
|
|
|
20
24
|
return useCallback(
|
|
21
25
|
(keyOrText: string) => {
|
|
22
|
-
const raw =
|
|
26
|
+
const raw = localization?.[defaultLanguage]?.[keyOrText] ?? keyOrText;
|
|
23
27
|
return replaceLocalizationParams(raw, params);
|
|
24
28
|
},
|
|
25
|
-
[defaultLanguage,
|
|
29
|
+
[defaultLanguage, localization, params],
|
|
26
30
|
);
|
|
27
31
|
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { useEffect, type RefObject } from 'react';
|
|
2
|
+
import type { Node } from '../types/Node';
|
|
3
|
+
import { findNodeByKeyNested } from '../utils/findNodeByKeyNested';
|
|
4
|
+
|
|
5
|
+
type UsePreviewSelectionParams = {
|
|
6
|
+
previewMode: boolean;
|
|
7
|
+
data: Node;
|
|
8
|
+
rootRef: RefObject<HTMLDivElement>;
|
|
9
|
+
onSelectNode?: (node: Node | null) => void;
|
|
10
|
+
setCurrent: (node: Node) => void;
|
|
11
|
+
forceRender: number;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
export function usePreviewSelection({
|
|
15
|
+
previewMode,
|
|
16
|
+
data,
|
|
17
|
+
rootRef,
|
|
18
|
+
onSelectNode,
|
|
19
|
+
setCurrent,
|
|
20
|
+
forceRender,
|
|
21
|
+
}: UsePreviewSelectionParams) {
|
|
22
|
+
useEffect(() => {
|
|
23
|
+
if (!previewMode) {
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const root = rootRef.current;
|
|
28
|
+
if (!root) {
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const handleClick = (event: MouseEvent) => {
|
|
33
|
+
const target = event.target as HTMLElement | null;
|
|
34
|
+
|
|
35
|
+
if (!target) return;
|
|
36
|
+
|
|
37
|
+
// Ignore clicks on carousel dots to avoid interfering with navigation
|
|
38
|
+
if (target.closest('.embla__dot')) {
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Some build-components may attach synthetic keys (e.g. React `useId()`),
|
|
43
|
+
// which are not present in the persisted node tree. Walk up until we find
|
|
44
|
+
// an attribute-key that resolves to a real node.
|
|
45
|
+
let element = target.closest('[attribute-key]') as HTMLElement | null;
|
|
46
|
+
while (element) {
|
|
47
|
+
const key = element.getAttribute('attribute-key');
|
|
48
|
+
if (key) {
|
|
49
|
+
const node = findNodeByKeyNested(data, key);
|
|
50
|
+
if (node) {
|
|
51
|
+
setCurrent(node);
|
|
52
|
+
onSelectNode?.(node);
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
element = element.parentElement?.closest('[attribute-key]') ?? null;
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
root.addEventListener('click', handleClick);
|
|
61
|
+
|
|
62
|
+
return () => {
|
|
63
|
+
root.removeEventListener('click', handleClick);
|
|
64
|
+
};
|
|
65
|
+
}, [previewMode, data, onSelectNode, setCurrent, forceRender, rootRef]); // forceRender: retrigger effect when we want to force a refresh (e.g. route change)
|
|
66
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -16,13 +16,11 @@ export type { Node, NodeData, NodeDefaultAttribute } from './types/Node';
|
|
|
16
16
|
export type { Project, ProjectColors, ProjectMeta } from './types/Project';
|
|
17
17
|
export type { Device } from './types/Device';
|
|
18
18
|
export type {
|
|
19
|
-
AppConfig,
|
|
20
19
|
Theme,
|
|
21
20
|
Localication,
|
|
22
21
|
LocalizationKey,
|
|
23
22
|
} from './types/PreviewConfig';
|
|
24
23
|
export {
|
|
25
|
-
defaultAppConfig,
|
|
26
24
|
defaultTheme,
|
|
27
25
|
defaultLocalization,
|
|
28
26
|
mergeLocalization,
|
|
@@ -79,6 +77,8 @@ export {
|
|
|
79
77
|
buildPaywallLocalizationParams,
|
|
80
78
|
usePaywallLocalizationParams,
|
|
81
79
|
} from './product-base';
|
|
80
|
+
export type { MockProductPresets } from './product-base';
|
|
81
|
+
export { getMockProducts, getMockProductsByPreset } from './product-base';
|
|
82
82
|
|
|
83
83
|
// Context (RN-safe). In React Native, `products` should come from an IAP wrapper
|
|
84
84
|
// (e.g. `react-native-iap`) and be passed into `BuilderProvider` by the host app.
|
package/src/logger.ts
CHANGED
|
@@ -6,33 +6,17 @@
|
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
type LogPayload = Record<string, unknown>;
|
|
9
|
-
type LogOptions = { remote?: boolean };
|
|
10
9
|
|
|
11
10
|
function noop() {}
|
|
12
|
-
|
|
11
|
+
//TODO: bizim logger'ı kullan
|
|
13
12
|
export const iapLogger = {
|
|
14
|
-
error(
|
|
15
|
-
_tags: string[],
|
|
16
|
-
message: string,
|
|
17
|
-
payload?: LogPayload,
|
|
18
|
-
_opts?: LogOptions,
|
|
19
|
-
) {
|
|
13
|
+
error(_tags: string[], message: string, payload?: LogPayload) {
|
|
20
14
|
console.error(`[iap] ${message}`, payload);
|
|
21
15
|
},
|
|
22
|
-
warn(
|
|
23
|
-
_tags: string[],
|
|
24
|
-
message: string,
|
|
25
|
-
payload?: LogPayload,
|
|
26
|
-
_opts?: LogOptions,
|
|
27
|
-
) {
|
|
16
|
+
warn(_tags: string[], message: string, payload?: LogPayload) {
|
|
28
17
|
console.warn(`[iap] ${message}`, payload);
|
|
29
18
|
},
|
|
30
|
-
info(
|
|
31
|
-
_tags: string[],
|
|
32
|
-
message: string,
|
|
33
|
-
payload?: LogPayload,
|
|
34
|
-
_opts?: LogOptions,
|
|
35
|
-
) {
|
|
19
|
+
info(_tags: string[], message: string, payload?: LogPayload) {
|
|
36
20
|
console.info(`[iap] ${message}`, payload);
|
|
37
21
|
},
|
|
38
22
|
debug: noop,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import React, { useMemo, useState } from 'react';
|
|
2
2
|
import Modal from './Modal';
|
|
3
|
-
import { Icons, type IconsType } from '../types/Icons';
|
|
3
|
+
import { Icons, type IconsType } from '../types/Icons.generated';
|
|
4
4
|
import { Icon } from '../components/Icon.generated';
|
|
5
5
|
|
|
6
6
|
type IconPickerModalProps = {
|
|
@@ -15,14 +15,13 @@ type InspectModalProps = {
|
|
|
15
15
|
export function InspectModal({ onClose }: InspectModalProps) {
|
|
16
16
|
const [activeTab, setActiveTab] = useState<InspectTab>('localizations');
|
|
17
17
|
|
|
18
|
-
const {
|
|
19
|
-
(s) => ({
|
|
20
|
-
|
|
18
|
+
const { localization, projectColors, theme, defaultLanguage } =
|
|
19
|
+
useRenderStore((s) => ({
|
|
20
|
+
localization: s.localization,
|
|
21
21
|
projectColors: s.projectColors,
|
|
22
22
|
theme: s.theme,
|
|
23
23
|
defaultLanguage: s.defaultLanguage,
|
|
24
|
-
})
|
|
25
|
-
);
|
|
24
|
+
}));
|
|
26
25
|
|
|
27
26
|
const paramsSnapshot = useMemo(() => getLastParamsSnapshot(), []);
|
|
28
27
|
|
|
@@ -69,7 +68,7 @@ export function InspectModal({ onClose }: InspectModalProps) {
|
|
|
69
68
|
<div className="inspect-modal__body">
|
|
70
69
|
{activeTab === 'localizations' && (
|
|
71
70
|
<LocalizationsTab
|
|
72
|
-
localication={
|
|
71
|
+
localication={localization ?? {}}
|
|
73
72
|
language={defaultLanguage}
|
|
74
73
|
/>
|
|
75
74
|
)}
|
|
@@ -107,7 +106,7 @@ function LocalizationsTab({
|
|
|
107
106
|
if (entries.length === 0) {
|
|
108
107
|
return (
|
|
109
108
|
<p className="inspect-modal__empty">
|
|
110
|
-
No localization keys found for
|
|
109
|
+
No localization keys found for "{language}".
|
|
111
110
|
</p>
|
|
112
111
|
);
|
|
113
112
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import React, { useMemo } from 'react';
|
|
2
2
|
import Modal from './Modal';
|
|
3
3
|
import type { Product } from '../paywall/types/paywall-types';
|
|
4
|
-
import
|
|
4
|
+
import { getMockProducts } from '../product-base';
|
|
5
5
|
|
|
6
6
|
type PresetMap = Record<string, Product[]>;
|
|
7
7
|
const DEFAULT_PRESET_KEY = 'preset-1';
|
|
@@ -18,7 +18,7 @@ function ensureProductId(p: Product): Product {
|
|
|
18
18
|
}
|
|
19
19
|
|
|
20
20
|
function getPresetMap(): PresetMap {
|
|
21
|
-
const raw =
|
|
21
|
+
const raw = getMockProducts();
|
|
22
22
|
const safe: PresetMap = {};
|
|
23
23
|
Object.entries(raw ?? {}).forEach(([key, list]) => {
|
|
24
24
|
if (!key || typeof key !== 'string') return;
|
|
@@ -1,7 +1,6 @@
|
|
|
1
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
|
-
import type { AppConfig } from '../types/PreviewConfig';
|
|
5
4
|
import { Checkbox } from '../components/Checkbox';
|
|
6
5
|
import { JsonTextEditor } from '../components/JsonTextEditor';
|
|
7
6
|
import { analyseAndProccess } from '../utils/analyseNode';
|
|
@@ -24,9 +23,6 @@ export type DebugJsonPageProps = {
|
|
|
24
23
|
previewMode?: boolean;
|
|
25
24
|
setPreviewMode?: (next: boolean) => void;
|
|
26
25
|
|
|
27
|
-
appConfig?: AppConfig;
|
|
28
|
-
setAppConfig?: (next: AppConfig) => void;
|
|
29
|
-
|
|
30
26
|
logLabel?: string;
|
|
31
27
|
};
|
|
32
28
|
|
|
@@ -39,8 +35,6 @@ export function DebugJsonPage({
|
|
|
39
35
|
description,
|
|
40
36
|
previewMode,
|
|
41
37
|
setPreviewMode,
|
|
42
|
-
appConfig,
|
|
43
|
-
setAppConfig,
|
|
44
38
|
logLabel,
|
|
45
39
|
}: DebugJsonPageProps) {
|
|
46
40
|
const setCurrent = useRenderStore((s) => s.setCurrent);
|
|
@@ -7,13 +7,7 @@ import { EditorHeader } from '../components/EditorHeader';
|
|
|
7
7
|
import { AttributesEditorPanel } from '../components/AttributesEditorPanel';
|
|
8
8
|
import { BuilderPanel } from './tabs/BuilderPanel';
|
|
9
9
|
import { BottomBar } from '../components/BottomBar';
|
|
10
|
-
import {
|
|
11
|
-
type AppConfig,
|
|
12
|
-
defaultAppConfig,
|
|
13
|
-
defaultLocalization,
|
|
14
|
-
mergeLocalization,
|
|
15
|
-
type Localication,
|
|
16
|
-
} from '../types/PreviewConfig';
|
|
10
|
+
import { type Localication } from '../types/PreviewConfig';
|
|
17
11
|
import { useRenderStore } from '../store';
|
|
18
12
|
import { logger } from '../utils/logger';
|
|
19
13
|
import { useLogRender } from '../utils/useLogRender';
|
|
@@ -24,7 +18,6 @@ import {
|
|
|
24
18
|
isNodeNullOrUndefined,
|
|
25
19
|
} from '../utils/analyseNode';
|
|
26
20
|
import { getImage, TribeAssetName } from '../utils/getImage';
|
|
27
|
-
import type { PaywallBenefits } from '../paywall/types/benefits';
|
|
28
21
|
import { LoadingComponent } from '../components/LoadingComponent';
|
|
29
22
|
import { ProjectValidationPage } from './ProjectValidationPage';
|
|
30
23
|
import { ProjectMigrationPage } from './ProjectMigrationPage';
|
|
@@ -89,32 +82,18 @@ export function ProjectPage({
|
|
|
89
82
|
setCurrent,
|
|
90
83
|
setProjectColors,
|
|
91
84
|
setProjectName,
|
|
92
|
-
|
|
93
|
-
storeAppConfig,
|
|
94
|
-
storeTheme,
|
|
95
|
-
storeDefaultLanguage,
|
|
85
|
+
setLocalization,
|
|
96
86
|
products,
|
|
97
87
|
benefits,
|
|
98
|
-
previewMode,
|
|
99
88
|
} = useRenderStore((s) => ({
|
|
100
89
|
current: s.current,
|
|
101
90
|
setCurrent: s.setCurrent,
|
|
102
91
|
setProjectColors: s.setProjectColors,
|
|
103
92
|
setProjectName: s.setProjectName,
|
|
104
|
-
|
|
105
|
-
storeAppConfig: s.appConfig,
|
|
106
|
-
storeTheme: s.theme,
|
|
107
|
-
storeDefaultLanguage: s.defaultLanguage,
|
|
93
|
+
setLocalization: s.setLocalization,
|
|
108
94
|
products: s.products,
|
|
109
95
|
benefits: s.benefits,
|
|
110
|
-
previewMode: s.previewMode,
|
|
111
96
|
}));
|
|
112
|
-
const resolvedAppConfig: AppConfig = localization
|
|
113
|
-
? {
|
|
114
|
-
...defaultAppConfig,
|
|
115
|
-
localication: mergeLocalization(defaultLocalization, localization),
|
|
116
|
-
}
|
|
117
|
-
: (storeAppConfig ?? defaultAppConfig);
|
|
118
97
|
const [overrideProject, setOverrideProject] = useState<Project | null>(null);
|
|
119
98
|
const activeProject = overrideProject ?? project;
|
|
120
99
|
const resolvedName = name ?? activeProject.name;
|
|
@@ -184,15 +163,16 @@ export function ProjectPage({
|
|
|
184
163
|
useEffect(() => {
|
|
185
164
|
logger.info('ProjectPage', 'mount', { projectName: project.name });
|
|
186
165
|
setOverrideProject(null);
|
|
187
|
-
if (localization) {
|
|
188
|
-
const merged = mergeLocalization(defaultLocalization, localization);
|
|
189
|
-
setAppConfig({ ...defaultAppConfig, localication: merged });
|
|
190
|
-
logger.verbose('ProjectPage', 'localization applied', merged);
|
|
191
|
-
}
|
|
192
166
|
return () => {
|
|
193
167
|
logger.info('ProjectPage', 'unmount');
|
|
194
168
|
};
|
|
195
|
-
}, [
|
|
169
|
+
}, [project.name]);
|
|
170
|
+
|
|
171
|
+
useEffect(() => {
|
|
172
|
+
if (localization) {
|
|
173
|
+
setLocalization(localization);
|
|
174
|
+
}
|
|
175
|
+
}, [localization, setLocalization]);
|
|
196
176
|
|
|
197
177
|
useEffect(() => {
|
|
198
178
|
setProjectName(resolvedName);
|
|
@@ -460,10 +440,7 @@ export function ProjectPage({
|
|
|
460
440
|
name: fixedName,
|
|
461
441
|
version: CURRENT_PROJECT_VERSION,
|
|
462
442
|
data: nodeCandidate,
|
|
463
|
-
|
|
464
|
-
projectColors: activeRecord?.projectColors as
|
|
465
|
-
| ProjectColors
|
|
466
|
-
| undefined,
|
|
443
|
+
projectColors: {},
|
|
467
444
|
type: isAllowedProjectType(activeRecord?.type)
|
|
468
445
|
? (activeRecord.type as 'paywall' | 'onboard' | 'other')
|
|
469
446
|
: undefined,
|
|
@@ -655,29 +632,7 @@ export function ProjectPage({
|
|
|
655
632
|
)}
|
|
656
633
|
{/* NOTE: In React Native, `products` should come from an IAP wrapper (e.g. `react-native-iap`). */}
|
|
657
634
|
{!showLoading && (
|
|
658
|
-
<RenderPage
|
|
659
|
-
data={editorData}
|
|
660
|
-
name={resolvedName}
|
|
661
|
-
params={{
|
|
662
|
-
mockProducts: products,
|
|
663
|
-
mockBenefits:
|
|
664
|
-
benefits && typeof benefits === 'object'
|
|
665
|
-
? (benefits as PaywallBenefits)
|
|
666
|
-
: {},
|
|
667
|
-
// Colors/fonts/theme must be passed via BuilderProvider params so build-components never touch useRenderStore.
|
|
668
|
-
theme: storeTheme,
|
|
669
|
-
defaultLanguage: storeDefaultLanguage,
|
|
670
|
-
appConfig: resolvedAppConfig,
|
|
671
|
-
projectColors: resolvedProjectColors,
|
|
672
|
-
fonts: typography.fonts,
|
|
673
|
-
appFont: resolvedAppFont,
|
|
674
|
-
previewMode,
|
|
675
|
-
selectedKey:
|
|
676
|
-
current && typeof current === 'object' && 'key' in current
|
|
677
|
-
? ((current as NodeData).key as string | undefined)
|
|
678
|
-
: undefined,
|
|
679
|
-
}}
|
|
680
|
-
/>
|
|
635
|
+
<RenderPage data={editorData} name={resolvedName} />
|
|
681
636
|
)}
|
|
682
637
|
</div>
|
|
683
638
|
{/* BOTOM BAR */}
|
|
@@ -19,8 +19,8 @@ export function SideTool({ data, setData }: SideToolProps) {
|
|
|
19
19
|
const [isLocalicationModalOpen, setIsLocalicationModalOpen] = useState(false);
|
|
20
20
|
const [isCompactPanelVisible, setIsCompactPanelVisible] = useState(false);
|
|
21
21
|
const {
|
|
22
|
-
|
|
23
|
-
|
|
22
|
+
localization,
|
|
23
|
+
setLocalization,
|
|
24
24
|
theme,
|
|
25
25
|
setTheme,
|
|
26
26
|
defaultLanguage,
|
|
@@ -30,8 +30,8 @@ export function SideTool({ data, setData }: SideToolProps) {
|
|
|
30
30
|
isRtl,
|
|
31
31
|
setIsRtl,
|
|
32
32
|
} = useRenderStore((s) => ({
|
|
33
|
-
|
|
34
|
-
|
|
33
|
+
localization: s.localization,
|
|
34
|
+
setLocalization: s.setLocalization,
|
|
35
35
|
theme: s.theme,
|
|
36
36
|
setTheme: s.setTheme,
|
|
37
37
|
defaultLanguage: s.defaultLanguage,
|
|
@@ -43,7 +43,7 @@ export function SideTool({ data, setData }: SideToolProps) {
|
|
|
43
43
|
}));
|
|
44
44
|
|
|
45
45
|
const handleLocalicationChange = (data: Localication) => {
|
|
46
|
-
|
|
46
|
+
setLocalization(data);
|
|
47
47
|
};
|
|
48
48
|
|
|
49
49
|
return (
|
|
@@ -63,7 +63,7 @@ export function SideTool({ data, setData }: SideToolProps) {
|
|
|
63
63
|
value={defaultLanguage}
|
|
64
64
|
onChange={(e) => setDefaultLanguage(e.target.value)}
|
|
65
65
|
>
|
|
66
|
-
{Object.keys(
|
|
66
|
+
{Object.keys(localization ?? {}).map((language) => (
|
|
67
67
|
<option key={language} value={language}>
|
|
68
68
|
{language}
|
|
69
69
|
</option>
|
|
@@ -129,7 +129,7 @@ export function SideTool({ data, setData }: SideToolProps) {
|
|
|
129
129
|
|
|
130
130
|
{isLocalicationModalOpen && (
|
|
131
131
|
<LocalicationModal
|
|
132
|
-
data={
|
|
132
|
+
data={localization ?? {}}
|
|
133
133
|
onChange={handleLocalicationChange}
|
|
134
134
|
onClose={() => setIsLocalicationModalOpen(false)}
|
|
135
135
|
/>
|
|
@@ -62,7 +62,6 @@ export function extractAndroidParams(
|
|
|
62
62
|
requestedOfferId: offerId,
|
|
63
63
|
availableOffers: subscriptionOffers.map((o) => o.id),
|
|
64
64
|
},
|
|
65
|
-
{ remote: true },
|
|
66
65
|
);
|
|
67
66
|
}
|
|
68
67
|
}
|
|
@@ -102,15 +101,10 @@ export function extractAndroidParams(
|
|
|
102
101
|
pricingPhases[pricingPhases.length - 1];
|
|
103
102
|
|
|
104
103
|
if (!regularPhase) {
|
|
105
|
-
iapLogger.error(
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
productId: product.id || product.productId,
|
|
110
|
-
pricingPhasesCount: pricingPhases.length,
|
|
111
|
-
},
|
|
112
|
-
{ remote: true },
|
|
113
|
-
);
|
|
104
|
+
iapLogger.error(['extractAndroidParams'], 'No regular phase found', {
|
|
105
|
+
productId: product.id || product.productId,
|
|
106
|
+
pricingPhasesCount: pricingPhases.length,
|
|
107
|
+
});
|
|
114
108
|
return getEmptyParams();
|
|
115
109
|
}
|
|
116
110
|
|
|
@@ -178,7 +172,6 @@ export function extractAndroidParams(
|
|
|
178
172
|
productId: product?.id || product?.productId,
|
|
179
173
|
error: error instanceof Error ? error.message : String(error),
|
|
180
174
|
},
|
|
181
|
-
{ remote: true },
|
|
182
175
|
);
|
|
183
176
|
return getEmptyParams();
|
|
184
177
|
}
|
|
@@ -134,7 +134,6 @@ export function extractIOSParams(
|
|
|
134
134
|
(d: IOSDiscount) => d.identifier,
|
|
135
135
|
),
|
|
136
136
|
},
|
|
137
|
-
{ remote: true },
|
|
138
137
|
);
|
|
139
138
|
}
|
|
140
139
|
}
|
|
@@ -163,15 +162,10 @@ export function extractIOSParams(
|
|
|
163
162
|
pricePerYear,
|
|
164
163
|
};
|
|
165
164
|
} catch (error) {
|
|
166
|
-
iapLogger.error(
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
productId: product?.id || product?.productId,
|
|
171
|
-
error: error instanceof Error ? error.message : String(error),
|
|
172
|
-
},
|
|
173
|
-
{ remote: true },
|
|
174
|
-
);
|
|
165
|
+
iapLogger.error(['extractIOSParams'], 'Failed to extract iOS params', {
|
|
166
|
+
productId: product?.id || product?.productId,
|
|
167
|
+
error: error instanceof Error ? error.message : String(error),
|
|
168
|
+
});
|
|
175
169
|
return getEmptyParams();
|
|
176
170
|
}
|
|
177
171
|
}
|
|
@@ -26,3 +26,39 @@ export * from './extractAndroidParams';
|
|
|
26
26
|
export * from './extractIOSParams';
|
|
27
27
|
export * from './buildPaywallLocalizationParams';
|
|
28
28
|
export * from './usePaywallLocalizationParams';
|
|
29
|
+
|
|
30
|
+
/* ── Mock Products ── */
|
|
31
|
+
|
|
32
|
+
import presetsJson from './mockProducts.json';
|
|
33
|
+
import type { Product } from './types';
|
|
34
|
+
|
|
35
|
+
/** Preset map: preset key → product array. */
|
|
36
|
+
export type MockProductPresets = Record<string, Product[]>;
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Returns a deep-copy of all mock product presets.
|
|
40
|
+
*
|
|
41
|
+
* Use this instead of importing `mockProducts.json` directly so that:
|
|
42
|
+
* 1. Consumers don't couple to a JSON file path.
|
|
43
|
+
* 2. Each call returns a fresh copy (safe to mutate).
|
|
44
|
+
*
|
|
45
|
+
* @example
|
|
46
|
+
* import { getMockProducts } from '@developer_tribe/react-builder';
|
|
47
|
+
* const presets = getMockProducts(); // { 'preset-1': [...], ... }
|
|
48
|
+
*/
|
|
49
|
+
export function getMockProducts(): MockProductPresets {
|
|
50
|
+
return JSON.parse(JSON.stringify(presetsJson)) as MockProductPresets;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Returns the product list for a specific preset key (deep-copied).
|
|
55
|
+
* Returns an empty array if the key doesn't exist.
|
|
56
|
+
*
|
|
57
|
+
* @example
|
|
58
|
+
* import { getMockProductsByPreset } from '@developer_tribe/react-builder';
|
|
59
|
+
* const products = getMockProductsByPreset('preset-1');
|
|
60
|
+
*/
|
|
61
|
+
export function getMockProductsByPreset(presetKey: string): Product[] {
|
|
62
|
+
const all = getMockProducts();
|
|
63
|
+
return all[presetKey] ?? [];
|
|
64
|
+
}
|
|
@@ -1,23 +1,31 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { defaultAppConfig } from '../types/PreviewConfig';
|
|
1
|
+
import { defaultBaseSize, type BaseSize } from '../types/PreviewConfig';
|
|
3
2
|
import { getDefaultDevice } from '../utils/getDevices';
|
|
3
|
+
import type { Device } from '../types/Device';
|
|
4
4
|
|
|
5
5
|
const fallbackDevice = getDefaultDevice();
|
|
6
|
-
const fallbackBaseSize =
|
|
6
|
+
const fallbackBaseSize = defaultBaseSize;
|
|
7
7
|
|
|
8
8
|
function ensureNumber(value: number | undefined, fallback: number) {
|
|
9
9
|
return typeof value === 'number' && Number.isFinite(value) ? value : fallback;
|
|
10
10
|
}
|
|
11
11
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
const
|
|
20
|
-
const
|
|
12
|
+
/**
|
|
13
|
+
* Calculates scaling factors based on baseSize and current device dimensions.
|
|
14
|
+
*/
|
|
15
|
+
export function getScalers(
|
|
16
|
+
customBaseSize: BaseSize = fallbackBaseSize,
|
|
17
|
+
customDevice: Device = fallbackDevice,
|
|
18
|
+
) {
|
|
19
|
+
const deviceWidth = ensureNumber(customDevice?.width, fallbackDevice.width);
|
|
20
|
+
const deviceHeight = ensureNumber(
|
|
21
|
+
customDevice?.height,
|
|
22
|
+
fallbackDevice.height,
|
|
23
|
+
);
|
|
24
|
+
const baseWidth = ensureNumber(customBaseSize?.width, fallbackBaseSize.width);
|
|
25
|
+
const baseHeight = ensureNumber(
|
|
26
|
+
customBaseSize?.height,
|
|
27
|
+
fallbackBaseSize.height,
|
|
28
|
+
);
|
|
21
29
|
|
|
22
30
|
const [shortDimension, longDimension] =
|
|
23
31
|
deviceWidth < deviceHeight
|
|
@@ -25,46 +33,51 @@ function getBaseDimensions() {
|
|
|
25
33
|
: [deviceHeight, deviceWidth];
|
|
26
34
|
|
|
27
35
|
return {
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
36
|
+
scale: (size: number) => (shortDimension / baseWidth) * size,
|
|
37
|
+
verticalScale: (size: number) => (longDimension / baseHeight) * size,
|
|
38
|
+
moderateScale: (size: number, factor = 0.5) => {
|
|
39
|
+
const s = (shortDimension / baseWidth) * size;
|
|
40
|
+
return size + (s - size) * factor;
|
|
41
|
+
},
|
|
31
42
|
};
|
|
32
43
|
}
|
|
33
|
-
export function scale(size: number) {
|
|
34
|
-
const { baseSize, shortDimension } = getBaseDimensions();
|
|
35
|
-
return (shortDimension / baseSize.width) * size;
|
|
36
|
-
}
|
|
37
|
-
export function verticalScale(size: number) {
|
|
38
|
-
const { baseSize, longDimension } = getBaseDimensions();
|
|
39
|
-
return (longDimension / baseSize.height) * size;
|
|
40
|
-
}
|
|
41
44
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
export const
|
|
45
|
+
// Deprecated: legacy exports that rely on defaults - prefer usage via getScalers or passing params
|
|
46
|
+
// For backward compatibility (if any), these will use defaults, effectively disabling dynamic store updates
|
|
47
|
+
export const s = (size: number) => getScalers().scale(size);
|
|
48
|
+
export const vs = (size: number) => getScalers().verticalScale(size);
|
|
49
|
+
export const fs = vs;
|
|
50
|
+
export const ms = (size: number, factor?: number) =>
|
|
51
|
+
getScalers().moderateScale(size, factor);
|
|
45
52
|
|
|
46
|
-
export function parseSize(
|
|
53
|
+
export function parseSize(
|
|
54
|
+
value: string | number | undefined,
|
|
55
|
+
baseSize?: BaseSize,
|
|
56
|
+
device?: Device,
|
|
57
|
+
) {
|
|
47
58
|
if (value === undefined) return undefined;
|
|
48
59
|
if (typeof value === 'number') {
|
|
49
60
|
return value;
|
|
50
61
|
}
|
|
51
62
|
|
|
63
|
+
const scalers = getScalers(baseSize, device);
|
|
64
|
+
|
|
52
65
|
const raw = String(value).trim();
|
|
53
66
|
const lower = raw.toLowerCase();
|
|
54
67
|
|
|
55
68
|
// Handle explicit scalers via suffixes
|
|
56
69
|
if (lower.endsWith('@s')) {
|
|
57
70
|
const n = parseFloat(lower.slice(0, -2));
|
|
58
|
-
return Number.isFinite(n) ?
|
|
71
|
+
return Number.isFinite(n) ? scalers.scale(n) : raw;
|
|
59
72
|
}
|
|
60
73
|
if (lower.endsWith('@vs')) {
|
|
61
74
|
const n = parseFloat(lower.slice(0, -3));
|
|
62
|
-
return Number.isFinite(n) ?
|
|
75
|
+
return Number.isFinite(n) ? scalers.verticalScale(n) : raw;
|
|
63
76
|
}
|
|
64
77
|
if (lower.endsWith('@f') || lower.endsWith('@fs')) {
|
|
65
78
|
const cut = lower.endsWith('@f') ? -2 : -3;
|
|
66
79
|
const n = parseFloat(lower.slice(0, cut));
|
|
67
|
-
return Number.isFinite(n) ?
|
|
80
|
+
return Number.isFinite(n) ? scalers.verticalScale(n) : raw;
|
|
68
81
|
}
|
|
69
82
|
|
|
70
83
|
// Preserve percentage values as-is
|
|
@@ -79,7 +92,7 @@ export function parseSize(value?: string | number) {
|
|
|
79
92
|
return Number.isFinite(n) ? n : raw;
|
|
80
93
|
}
|
|
81
94
|
|
|
82
|
-
// Plain numeric strings fall back to provided scaler
|
|
95
|
+
// Plain numeric strings fall back to provided scaler (implicit number)
|
|
83
96
|
const numeric = parseFloat(lower);
|
|
84
97
|
if (Number.isFinite(numeric)) {
|
|
85
98
|
return numeric;
|