@developer_tribe/react-builder 1.0.7 → 1.0.9
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 +3 -0
- package/dist/build-components/BackgroundImage/BackgroundImageProps.generated.d.ts +1 -0
- package/dist/build-components/Button/ButtonProps.generated.d.ts +1 -0
- package/dist/build-components/Carousel/CarouselProps.generated.d.ts +5 -0
- package/dist/build-components/CarouselButtons/CarouselButtonsProps.generated.d.ts +1 -0
- package/dist/build-components/CarouselDots/CarouselDotsProps.generated.d.ts +1 -0
- package/dist/build-components/CarouselItem/CarouselItemProps.generated.d.ts +1 -0
- package/dist/build-components/CarouselProvider/CarouselProviderProps.generated.d.ts +1 -0
- package/dist/build-components/Image/ImageProps.generated.d.ts +1 -0
- package/dist/build-components/Main/MainProps.generated.d.ts +1 -1
- package/dist/build-components/Onboard/OnboardProps.generated.d.ts +1 -0
- package/dist/build-components/OnboardButton/OnboardButtonProps.generated.d.ts +1 -0
- package/dist/build-components/OnboardButtons/OnboardButtonsProps.generated.d.ts +1 -0
- package/dist/build-components/OnboardDot/OnboardDotProps.generated.d.ts +1 -0
- package/dist/build-components/OnboardFooter/OnboardFooterProps.generated.d.ts +3 -0
- package/dist/build-components/OnboardImage/OnboardImageProps.generated.d.ts +1 -0
- package/dist/build-components/OnboardItem/OnboardItemProps.generated.d.ts +1 -0
- package/dist/build-components/OnboardProvider/OnboardProviderProps.generated.d.ts +3 -0
- package/dist/build-components/OnboardSubtitle/OnboardSubtitleProps.generated.d.ts +3 -0
- package/dist/build-components/OnboardTitle/OnboardTitleProps.generated.d.ts +3 -0
- package/dist/build-components/PaywallBackground/PaywallBackgroundProps.generated.d.ts +1 -1
- package/dist/build-components/PaywallCloseButton/PaywallCloseButtonProps.generated.d.ts +3 -1
- package/dist/build-components/PaywallOptions/PaywallOptionsProps.generated.d.ts +1 -1
- package/dist/build-components/PaywallProvider/PaywallContext.d.ts +12 -0
- package/dist/build-components/PaywallProvider/PaywallProviderProps.generated.d.ts +1 -1
- package/dist/build-components/PaywallSubscribeButton/PaywallSubscribeButtonProps.generated.d.ts +1 -0
- package/dist/build-components/RadioButton/RadioButtonProps.generated.d.ts +1 -1
- package/dist/build-components/Text/TextProps.generated.d.ts +3 -0
- package/dist/build-components/View/ViewProps.generated.d.ts +1 -0
- package/dist/build-components/patterns.generated.d.ts +372 -374
- package/dist/components/BuilderProvider.d.ts +2 -0
- package/dist/components/ParamsProvider.d.ts +5 -0
- package/dist/components/RenderErrorBoundary.d.ts +28 -0
- package/dist/hooks/useSyncHtmlThemeClass.d.ts +7 -0
- package/dist/index.cjs.js +5 -5
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.esm.js +3 -3
- package/dist/index.esm.js.map +1 -1
- package/dist/index.native.cjs.js +4 -4
- package/dist/index.native.cjs.js.map +1 -1
- package/dist/index.native.d.ts +1 -0
- package/dist/index.native.esm.js +4 -4
- package/dist/index.native.esm.js.map +1 -1
- package/dist/migrations/migratePipe.d.ts +14 -0
- package/dist/migrations/migrations/1.1.0_normalize_style_attributes.d.ts +2 -0
- package/dist/migrations/semver.d.ts +8 -0
- package/dist/migrations/types.d.ts +8 -0
- package/dist/mockOS/components/SubscriptionModal.d.ts +7 -0
- package/dist/mockOS/context/MockOSContextBase.d.ts +1 -0
- package/dist/mockOS/hooks/useMockIap.d.ts +3 -0
- package/dist/mockOS/index.d.ts +4 -0
- package/dist/mockOS/managers/mockOSIapManager.d.ts +6 -0
- package/dist/mockOS/managers/subscriptionManager.d.ts +10 -0
- package/dist/pages/ProjectDebug.d.ts +14 -0
- package/dist/pages/ProjectMigrationPage.d.ts +23 -0
- package/dist/pages/ProjectValidationPage.d.ts +15 -0
- package/dist/styles.css +1 -1
- package/dist/types/Device.d.ts +5 -0
- package/dist/utils/__special_exceptions.d.ts +7 -0
- package/dist/utils/getImage.d.ts +23 -0
- package/dist/utils/pasteNode.d.ts +15 -0
- package/dist/utils/patterns.d.ts +1 -2
- package/package.json +6 -2
- package/scripts/migrate-patterns-to-v2.mjs +131 -0
- package/scripts/migrate-samples-to-current.ts +79 -0
- package/scripts/prebuild/utils/createGeneratedProps.js +4 -5
- package/scripts/prebuild/utils/validateAllComponentsOrThrow.js +32 -21
- package/scripts/prebuild/utils/validatePatternJson.js +12 -10
- package/src/.DS_Store +0 -0
- package/src/AttributesEditor.tsx +41 -11
- package/src/RenderPage.tsx +55 -0
- package/src/assets/.DS_Store +0 -0
- package/src/assets/devices.json +91 -0
- package/src/assets/samples/carousel-sample.json +141 -29
- package/src/assets/samples/getSamples.ts +9 -0
- package/src/assets/samples/paywall-1.json +119 -71
- package/src/assets/samples/simple-1.json +28 -16
- package/src/assets/samples/simple-2.json +157 -82
- package/src/assets/samples/unmigrated-builder1.json +42 -0
- package/src/assets/samples/unvalidated-builder1.json +49 -0
- package/src/assets/samples/unvalidated-crash1.json +19 -0
- package/src/assets/samples/unvalidated-crashcomponent1.json +16 -0
- package/src/assets/samples/vpn-onboard-1.json +91 -51
- package/src/assets/samples/vpn-onboard-2.json +318 -278
- package/src/assets/samples/vpn-onboard-3.json +286 -252
- package/src/assets/samples/vpn-onboard-4.json +286 -252
- package/src/assets/samples/vpn-onboard-5.json +434 -374
- package/src/assets/samples/vpn-onboard-6.json +290 -250
- package/src/attributes-editor/Field.tsx +1 -1
- package/src/attributes-editor/LayoutPreviewPicker.tsx +5 -2
- package/src/build-components/BIcon/BIconProps.generated.ts +3 -0
- package/src/build-components/BIcon/pattern.json +12 -9
- package/src/build-components/BackgroundImage/BackgroundImage.tsx +3 -1
- package/src/build-components/BackgroundImage/BackgroundImageProps.generated.ts +1 -0
- package/src/build-components/BackgroundImage/pattern.json +25 -16
- package/src/build-components/Button/Button.tsx +26 -3
- package/src/build-components/Button/ButtonProps.generated.ts +1 -0
- package/src/build-components/Button/pattern.json +10 -6
- package/src/build-components/Carousel/CarouselProps.generated.ts +5 -0
- package/src/build-components/Carousel/pattern.json +19 -8
- package/src/build-components/CarouselButtons/CarouselButtonsProps.generated.ts +1 -0
- package/src/build-components/CarouselButtons/pattern.json +11 -5
- package/src/build-components/CarouselDots/CarouselDotsProps.generated.ts +1 -0
- package/src/build-components/CarouselDots/pattern.json +5 -4
- package/src/build-components/CarouselItem/CarouselItemProps.generated.ts +1 -0
- package/src/build-components/CarouselItem/pattern.json +5 -4
- package/src/build-components/CarouselProvider/CarouselProvider.tsx +44 -2
- package/src/build-components/CarouselProvider/CarouselProviderProps.generated.ts +1 -0
- package/src/build-components/Image/Image.tsx +2 -1
- package/src/build-components/Image/ImageProps.generated.ts +1 -0
- package/src/build-components/Image/pattern.json +11 -5
- package/src/build-components/Main/MainProps.generated.ts +1 -1
- package/src/build-components/Main/pattern.json +12 -9
- package/src/build-components/Onboard/OnboardProps.generated.ts +1 -0
- package/src/build-components/Onboard/pattern.json +14 -9
- package/src/build-components/OnboardButton/OnboardButtonProps.generated.ts +1 -0
- package/src/build-components/OnboardButton/pattern.json +5 -4
- package/src/build-components/OnboardButtons/OnboardButtonsProps.generated.ts +1 -0
- package/src/build-components/OnboardButtons/pattern.json +5 -4
- package/src/build-components/OnboardDot/OnboardDotProps.generated.ts +1 -0
- package/src/build-components/OnboardDot/pattern.json +5 -4
- package/src/build-components/OnboardFooter/OnboardFooterProps.generated.ts +3 -0
- package/src/build-components/OnboardFooter/pattern.json +8 -5
- package/src/build-components/OnboardImage/OnboardImageProps.generated.ts +1 -0
- package/src/build-components/OnboardImage/pattern.json +7 -4
- package/src/build-components/OnboardItem/OnboardItemProps.generated.ts +1 -0
- package/src/build-components/OnboardItem/pattern.json +18 -9
- package/src/build-components/OnboardProvider/OnboardProviderProps.generated.ts +3 -0
- package/src/build-components/OnboardProvider/pattern.json +21 -6
- package/src/build-components/OnboardSubtitle/OnboardSubtitleProps.generated.ts +3 -0
- package/src/build-components/OnboardSubtitle/pattern.json +10 -6
- package/src/build-components/OnboardTitle/OnboardTitleProps.generated.ts +3 -0
- package/src/build-components/OnboardTitle/pattern.json +11 -7
- package/src/build-components/PaywallBackground/PaywallBackgroundProps.generated.ts +1 -1
- package/src/build-components/PaywallBackground/pattern.json +5 -4
- package/src/build-components/PaywallCloseButton/PaywallCloseButton.tsx +6 -1
- package/src/build-components/PaywallCloseButton/PaywallCloseButtonProps.generated.ts +3 -1
- package/src/build-components/PaywallCloseButton/pattern.json +15 -12
- package/src/build-components/PaywallOptions/PaywallOptionButton.tsx +0 -1
- package/src/build-components/PaywallOptions/PaywallOptions.tsx +3 -2
- package/src/build-components/PaywallOptions/PaywallOptionsProps.generated.ts +1 -1
- package/src/build-components/PaywallOptions/pattern.json +14 -11
- package/src/build-components/PaywallProvider/PaywallContext.ts +25 -0
- package/src/build-components/PaywallProvider/PaywallProvider.tsx +102 -5
- package/src/build-components/PaywallProvider/PaywallProviderProps.generated.ts +1 -1
- package/src/build-components/PaywallProvider/pattern.json +11 -8
- package/src/build-components/PaywallSubscribeButton/PaywallSubscribeButton.tsx +7 -0
- package/src/build-components/PaywallSubscribeButton/PaywallSubscribeButtonProps.generated.ts +1 -0
- package/src/build-components/PaywallSubscribeButton/pattern.json +16 -13
- package/src/build-components/RadioButton/RadioButtonProps.generated.ts +1 -1
- package/src/build-components/RadioButton/pattern.json +5 -4
- package/src/build-components/Text/Text.tsx +107 -4
- package/src/build-components/Text/TextProps.generated.ts +3 -0
- package/src/build-components/Text/pattern.json +19 -4
- package/src/build-components/View/ViewProps.generated.ts +1 -0
- package/src/build-components/View/pattern.json +28 -13
- package/src/build-components/other.tsx +15 -0
- package/src/build-components/patterns.generated.ts +340 -235
- package/src/build-components/useNode.ts +22 -3
- package/src/components/BottomBar.tsx +45 -45
- package/src/components/Builder.tsx +20 -6
- package/src/components/BuilderButton.tsx +75 -38
- package/src/components/BuilderProvider.tsx +22 -2
- package/src/components/DeviceButton.tsx +12 -5
- package/src/components/EditorHeader.tsx +296 -38
- package/src/components/ParamsProvider.tsx +7 -0
- package/src/components/RenderErrorBoundary.tsx +200 -0
- package/src/hooks/useParams.ts +5 -1
- package/src/hooks/useSyncHtmlThemeClass.ts +19 -0
- package/src/index.native.ts +7 -0
- package/src/index.ts +8 -0
- package/src/migrations/migratePipe.ts +59 -0
- package/src/migrations/migrations/1.1.0_normalize_style_attributes.ts +80 -0
- package/src/migrations/semver.ts +24 -0
- package/src/migrations/types.ts +9 -0
- package/src/mockOS/components/PermissionModal.tsx +3 -2
- package/src/mockOS/components/SubscriptionModal.tsx +400 -0
- package/src/mockOS/context/MockOSContext.tsx +61 -10
- package/src/mockOS/context/MockOSContextBase.ts +1 -0
- package/src/mockOS/hooks/useMockIap.ts +11 -0
- package/src/mockOS/index.ts +7 -0
- package/src/mockOS/managers/mockOSIapManager.ts +10 -0
- package/src/mockOS/managers/subscriptionManager.ts +36 -0
- package/src/modals/IconPickerModal.tsx +1 -1
- package/src/pages/ProjectDebug.tsx +331 -0
- package/src/pages/ProjectMigrationPage.tsx +92 -0
- package/src/pages/ProjectPage.tsx +318 -166
- package/src/pages/ProjectValidationPage.tsx +54 -0
- package/src/styles/base/_global.scss +58 -11
- package/src/styles/components/_attributes-editor.scss +1 -1
- package/src/styles/components/_bottom-bar.scss +7 -4
- package/src/styles/components/_editor-shell.scss +126 -4
- package/src/styles/components/_mockos-router.scss +3 -2
- package/src/styles/components/_ui-components.scss +10 -5
- package/src/styles/foundation/_colors.scss +78 -11
- package/src/styles/foundation/_mixins.scss +4 -1
- package/src/styles/foundation/_sizes.scss +4 -2
- package/src/styles/index.scss +1 -0
- package/src/styles/layout/_builder.scss +61 -0
- package/src/styles/layout/_project-validation.scss +214 -0
- package/src/styles/modals/_add-component.scss +4 -2
- package/src/styles/modals/_color-modal.scss +4 -2
- package/src/styles/modals/_modal-shell.scss +3 -1
- package/src/types/Device.ts +5 -0
- package/src/utils/__special_exceptions.ts +88 -0
- package/src/utils/analyseNode.ts +8 -2
- package/src/utils/analyseNodeByPatterns.ts +43 -9
- package/src/utils/extractTextStyle.ts +19 -6
- package/src/utils/extractViewStyle.ts +68 -59
- package/src/utils/getImage.ts +76 -0
- package/src/utils/novaToJson.ts +2 -1
- package/src/utils/pasteNode.ts +172 -0
- package/src/utils/patterns.ts +4 -3
- package/dist/android.svg +0 -43
- package/dist/apple.svg +0 -16
- package/dist/background.jpg +0 -0
|
@@ -7,9 +7,28 @@ export default function useNode<
|
|
|
7
7
|
const type = node?.type;
|
|
8
8
|
const defaults = getDefaultsForType(type) as Partial<T> | undefined;
|
|
9
9
|
if (!defaults) return node;
|
|
10
|
-
const
|
|
11
|
-
|
|
12
|
-
|
|
10
|
+
const nodeAttributes = ((node.attributes as T) ?? ({} as T)) as T & {
|
|
11
|
+
style?: Record<string, unknown>;
|
|
12
|
+
};
|
|
13
|
+
const defaultAttributes = defaults as T as T & {
|
|
14
|
+
style?: Record<string, unknown>;
|
|
13
15
|
};
|
|
16
|
+
const mergedAttributes: T = {
|
|
17
|
+
...(defaultAttributes as T),
|
|
18
|
+
...(nodeAttributes as T),
|
|
19
|
+
// Deep merge `style` so default style values aren't lost when the node provides partial style overrides.
|
|
20
|
+
style: {
|
|
21
|
+
...(defaultAttributes?.style ?? {}),
|
|
22
|
+
...(nodeAttributes?.style ?? {}),
|
|
23
|
+
},
|
|
24
|
+
} as T;
|
|
25
|
+
if (
|
|
26
|
+
mergedAttributes &&
|
|
27
|
+
typeof (mergedAttributes as any).style === 'object' &&
|
|
28
|
+
(mergedAttributes as any).style != null &&
|
|
29
|
+
Object.keys((mergedAttributes as any).style).length === 0
|
|
30
|
+
) {
|
|
31
|
+
delete (mergedAttributes as any).style;
|
|
32
|
+
}
|
|
14
33
|
return { ...node, attributes: mergedAttributes };
|
|
15
34
|
}
|
|
@@ -26,7 +26,7 @@ export function BottomBar({ className, data, setData }: BottomBarProps) {
|
|
|
26
26
|
const colorIcon: IconsType = 'colors';
|
|
27
27
|
|
|
28
28
|
const { appConfig, setAppConfig, previewMode, setPreviewMode } =
|
|
29
|
-
useRenderStore(s => ({
|
|
29
|
+
useRenderStore((s) => ({
|
|
30
30
|
appConfig: s.appConfig,
|
|
31
31
|
setAppConfig: s.setAppConfig,
|
|
32
32
|
previewMode: s.previewMode,
|
|
@@ -53,9 +53,9 @@ export function BottomBar({ className, data, setData }: BottomBarProps) {
|
|
|
53
53
|
<>
|
|
54
54
|
<div className={['rb-bottom-bar', className].filter(Boolean).join(' ')}>
|
|
55
55
|
<button
|
|
56
|
-
type=
|
|
56
|
+
type="button"
|
|
57
57
|
className={`rb-bottom-bar__button${themeIsActive ? ' is-active' : ''}`}
|
|
58
|
-
aria-label=
|
|
58
|
+
aria-label="Theme"
|
|
59
59
|
aria-pressed={themeIsActive}
|
|
60
60
|
onClick={() =>
|
|
61
61
|
setAppConfig({
|
|
@@ -64,85 +64,85 @@ export function BottomBar({ className, data, setData }: BottomBarProps) {
|
|
|
64
64
|
})
|
|
65
65
|
}
|
|
66
66
|
>
|
|
67
|
-
<Icon iconType={themeIcon} size={20} color=
|
|
67
|
+
<Icon iconType={themeIcon} size={20} color="currentColor" alt="" />
|
|
68
68
|
</button>
|
|
69
69
|
|
|
70
70
|
<button
|
|
71
|
-
type=
|
|
71
|
+
type="button"
|
|
72
72
|
className={`rb-bottom-bar__button rb-bottom-bar__button--rtl${
|
|
73
73
|
rtlIsActive ? ' is-active' : ''
|
|
74
74
|
}`}
|
|
75
|
-
aria-label=
|
|
75
|
+
aria-label="RTL"
|
|
76
76
|
aria-pressed={rtlIsActive}
|
|
77
77
|
onClick={() =>
|
|
78
78
|
setAppConfig({ ...appConfig, isRtl: !(appConfig.isRtl ?? false) })
|
|
79
79
|
}
|
|
80
80
|
>
|
|
81
|
-
<Icon iconType={rtlIcon} size={18} color=
|
|
82
|
-
<span className=
|
|
81
|
+
<Icon iconType={rtlIcon} size={18} color="currentColor" alt="" />
|
|
82
|
+
<span className="rb-bottom-bar__rtl-text">RTL</span>
|
|
83
83
|
</button>
|
|
84
84
|
|
|
85
85
|
<button
|
|
86
|
-
type=
|
|
86
|
+
type="button"
|
|
87
87
|
className={`rb-bottom-bar__button rb-bottom-bar__button--preview${
|
|
88
88
|
previewIsActive ? ' is-active' : ''
|
|
89
89
|
}`}
|
|
90
|
-
aria-label=
|
|
90
|
+
aria-label="Magic cursor tool"
|
|
91
91
|
aria-pressed={previewIsActive}
|
|
92
92
|
onClick={() => setPreviewMode(!previewMode)}
|
|
93
93
|
>
|
|
94
94
|
<Icon
|
|
95
95
|
iconType={magicCursorIcon}
|
|
96
96
|
size={20}
|
|
97
|
-
color=
|
|
98
|
-
alt=
|
|
97
|
+
color="currentColor"
|
|
98
|
+
alt=""
|
|
99
99
|
/>
|
|
100
100
|
</button>
|
|
101
101
|
|
|
102
102
|
<button
|
|
103
|
-
type=
|
|
103
|
+
type="button"
|
|
104
104
|
className={`rb-bottom-bar__button${isDebugOpen ? ' is-active' : ''}`}
|
|
105
|
-
aria-label=
|
|
105
|
+
aria-label="Debug"
|
|
106
106
|
aria-pressed={isDebugOpen}
|
|
107
107
|
onClick={() => setIsDebugOpen(true)}
|
|
108
108
|
>
|
|
109
|
-
<Icon iconType={debugIcon} size={20} color=
|
|
109
|
+
<Icon iconType={debugIcon} size={20} color="currentColor" alt="" />
|
|
110
110
|
</button>
|
|
111
111
|
|
|
112
112
|
<button
|
|
113
|
-
type=
|
|
113
|
+
type="button"
|
|
114
114
|
className={`rb-bottom-bar__button${
|
|
115
115
|
isLocalizationOpen ? ' is-active' : ''
|
|
116
116
|
}`}
|
|
117
|
-
aria-label=
|
|
117
|
+
aria-label="Localization"
|
|
118
118
|
aria-pressed={isLocalizationOpen}
|
|
119
119
|
onClick={() => setIsLocalizationOpen(true)}
|
|
120
120
|
>
|
|
121
121
|
<Icon
|
|
122
122
|
iconType={localizationIcon}
|
|
123
123
|
size={20}
|
|
124
|
-
color=
|
|
125
|
-
alt=
|
|
124
|
+
color="currentColor"
|
|
125
|
+
alt=""
|
|
126
126
|
/>
|
|
127
127
|
</button>
|
|
128
128
|
|
|
129
129
|
<button
|
|
130
|
-
type=
|
|
130
|
+
type="button"
|
|
131
131
|
className={`rb-bottom-bar__button${isColorsOpen ? ' is-active' : ''}`}
|
|
132
|
-
aria-label=
|
|
132
|
+
aria-label="Color"
|
|
133
133
|
aria-pressed={isColorsOpen}
|
|
134
134
|
onClick={() => setIsColorsOpen(true)}
|
|
135
135
|
>
|
|
136
|
-
<Icon iconType={colorIcon} size={20} color=
|
|
136
|
+
<Icon iconType={colorIcon} size={20} color="currentColor" alt="" />
|
|
137
137
|
</button>
|
|
138
138
|
|
|
139
|
-
<div className=
|
|
139
|
+
<div className="rb-bottom-bar__spacer" />
|
|
140
140
|
|
|
141
|
-
<div className=
|
|
142
|
-
{languages.map(language => (
|
|
141
|
+
<div className="rb-bottom-bar__langs" aria-label="Language">
|
|
142
|
+
{languages.map((language) => (
|
|
143
143
|
<button
|
|
144
144
|
key={language}
|
|
145
|
-
type=
|
|
145
|
+
type="button"
|
|
146
146
|
className={`rb-bottom-bar__lang${
|
|
147
147
|
activeLanguage === language ? ' is-active' : ''
|
|
148
148
|
}`}
|
|
@@ -175,38 +175,38 @@ export function BottomBar({ className, data, setData }: BottomBarProps) {
|
|
|
175
175
|
{isDebugOpen && (
|
|
176
176
|
<Modal
|
|
177
177
|
onClose={() => setIsDebugOpen(false)}
|
|
178
|
-
ariaLabelledBy=
|
|
179
|
-
className=
|
|
180
|
-
contentClassName=
|
|
178
|
+
ariaLabelledBy="debug-json-editor-title"
|
|
179
|
+
className="modal--large modal--scrollable"
|
|
180
|
+
contentClassName="localication-modal__content"
|
|
181
181
|
>
|
|
182
|
-
<div className=
|
|
183
|
-
<div className=
|
|
184
|
-
<h3 id=
|
|
182
|
+
<div className="modal__header localication-modal__header">
|
|
183
|
+
<div className="localication-modal__header-main">
|
|
184
|
+
<h3 id="debug-json-editor-title" className="modal__title">
|
|
185
185
|
Debug JSON
|
|
186
186
|
</h3>
|
|
187
|
-
<p className=
|
|
187
|
+
<p className="localication-modal__description">
|
|
188
188
|
Inspect and edit raw node JSON.
|
|
189
189
|
</p>
|
|
190
190
|
</div>
|
|
191
191
|
<button
|
|
192
|
-
type=
|
|
193
|
-
className=
|
|
192
|
+
type="button"
|
|
193
|
+
className="editor-button"
|
|
194
194
|
onClick={() => setIsDebugOpen(false)}
|
|
195
195
|
>
|
|
196
196
|
Close
|
|
197
197
|
</button>
|
|
198
198
|
</div>
|
|
199
|
-
<div className=
|
|
199
|
+
<div className="localication-modal__body">
|
|
200
200
|
<div style={{ display: 'flex', flexDirection: 'column', gap: 12 }}>
|
|
201
201
|
<Checkbox
|
|
202
|
-
label=
|
|
202
|
+
label="Preview mode"
|
|
203
203
|
checked={previewMode}
|
|
204
204
|
onChange={setPreviewMode}
|
|
205
205
|
/>
|
|
206
206
|
<Checkbox
|
|
207
|
-
label=
|
|
207
|
+
label="Dark Mode"
|
|
208
208
|
checked={appConfig.theme === 'dark'}
|
|
209
|
-
onChange={checked =>
|
|
209
|
+
onChange={(checked) =>
|
|
210
210
|
setAppConfig({
|
|
211
211
|
...appConfig,
|
|
212
212
|
theme: checked ? 'dark' : 'light',
|
|
@@ -214,24 +214,24 @@ export function BottomBar({ className, data, setData }: BottomBarProps) {
|
|
|
214
214
|
}
|
|
215
215
|
/>
|
|
216
216
|
<Checkbox
|
|
217
|
-
label=
|
|
217
|
+
label="Is RTL"
|
|
218
218
|
checked={appConfig.isRtl ?? false}
|
|
219
|
-
onChange={checked =>
|
|
219
|
+
onChange={(checked) =>
|
|
220
220
|
setAppConfig({ ...appConfig, isRtl: checked })
|
|
221
221
|
}
|
|
222
222
|
/>
|
|
223
223
|
</div>
|
|
224
224
|
<div
|
|
225
|
-
className=
|
|
225
|
+
className="localication-modal__editor"
|
|
226
226
|
style={{ marginTop: 12 }}
|
|
227
227
|
>
|
|
228
228
|
<JsonTextEditor
|
|
229
|
-
rootName=
|
|
229
|
+
rootName="node"
|
|
230
230
|
value={data ?? {}}
|
|
231
|
-
onChange={next =>
|
|
231
|
+
onChange={(next) =>
|
|
232
232
|
setData(analyseAndProccess(next as Node) as Node)
|
|
233
233
|
}
|
|
234
|
-
className=
|
|
234
|
+
className="localication-modal__json-editor"
|
|
235
235
|
/>
|
|
236
236
|
</div>
|
|
237
237
|
</div>
|
|
@@ -386,23 +386,37 @@ export function Builder({
|
|
|
386
386
|
function createDefaultNode(type: string): NodeData<NodeDefaultAttribute> {
|
|
387
387
|
const pattern = getPatternByType(type)?.pattern;
|
|
388
388
|
const defaults = getDefaultsForType(type) ?? {};
|
|
389
|
-
let children: Node = '';
|
|
390
389
|
const childrenSchema = pattern?.children as unknown;
|
|
390
|
+
|
|
391
|
+
// Special-case: CarouselProvider MUST contain a Carousel container inside the viewport
|
|
392
|
+
// otherwise embla-carousel will crash (it expects viewport.firstChild.children).
|
|
393
|
+
if (type === 'CarouselProvider') {
|
|
394
|
+
return {
|
|
395
|
+
type,
|
|
396
|
+
children: createDefaultNode('Carousel'),
|
|
397
|
+
attributes: { ...defaults },
|
|
398
|
+
} as NodeData<NodeDefaultAttribute>;
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
let children: Node = null;
|
|
391
402
|
if (childrenSchema === 'never') {
|
|
392
|
-
children =
|
|
403
|
+
children = null;
|
|
393
404
|
} else if (childrenSchema === 'string') {
|
|
394
405
|
children = '';
|
|
395
406
|
} else if (
|
|
396
407
|
childrenSchema === 'node' ||
|
|
397
408
|
(Array.isArray(childrenSchema) && childrenSchema.includes('node'))
|
|
398
409
|
) {
|
|
399
|
-
children
|
|
410
|
+
// Default to "no children yet". Using [] here is truthy and can mount
|
|
411
|
+
// child-dependent widgets (e.g. Embla) with an empty DOM, causing crashes.
|
|
412
|
+
children = null;
|
|
400
413
|
} else if (typeof childrenSchema === 'string') {
|
|
401
|
-
// Specific child type like '
|
|
402
|
-
children = [];
|
|
414
|
+
// Specific child type like 'CarouselItem' – seed with one child to match the pattern.
|
|
415
|
+
children = [createDefaultNode(childrenSchema)];
|
|
403
416
|
} else {
|
|
404
|
-
children =
|
|
417
|
+
children = null;
|
|
405
418
|
}
|
|
419
|
+
|
|
406
420
|
return {
|
|
407
421
|
type,
|
|
408
422
|
children,
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import { useRef } from 'react';
|
|
1
|
+
import { useEffect, useMemo, useRef, useState } from 'react';
|
|
2
2
|
import { isNodeNullOrUndefined, isNodeString } from '../utils/analyseNode';
|
|
3
3
|
import type { Node, NodeData, NodeDefaultAttribute } from '../types/Node';
|
|
4
4
|
import { getPatternByType } from '../utils/patterns';
|
|
5
|
+
import { Icon } from './Icon.generated';
|
|
5
6
|
|
|
6
7
|
export type BuilderButtonProps = {
|
|
7
8
|
node: Node;
|
|
@@ -26,8 +27,12 @@ export function BuilderButton({
|
|
|
26
27
|
}
|
|
27
28
|
const nodeData = node as NodeData<NodeDefaultAttribute>;
|
|
28
29
|
|
|
29
|
-
const
|
|
30
|
-
const
|
|
30
|
+
const [isMenuOpen, setIsMenuOpen] = useState(false);
|
|
31
|
+
const actionsRef = useRef<HTMLDivElement | null>(null);
|
|
32
|
+
const menuId = useMemo(
|
|
33
|
+
() => `builder-node-actions-${Math.random().toString(36).slice(2, 9)}`,
|
|
34
|
+
[],
|
|
35
|
+
);
|
|
31
36
|
|
|
32
37
|
const handleDelete = () => {
|
|
33
38
|
if (onDelete) {
|
|
@@ -35,34 +40,31 @@ export function BuilderButton({
|
|
|
35
40
|
}
|
|
36
41
|
};
|
|
37
42
|
|
|
38
|
-
|
|
39
|
-
if (longPressTimeoutRef.current !== null) {
|
|
40
|
-
window.clearTimeout(longPressTimeoutRef.current);
|
|
41
|
-
longPressTimeoutRef.current = null;
|
|
42
|
-
}
|
|
43
|
-
};
|
|
43
|
+
// Copy/Paste intentionally removed for now.
|
|
44
44
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
longPressTimeoutRef.current = window.setTimeout(() => {
|
|
48
|
-
longPressTriggeredRef.current = true;
|
|
49
|
-
const shouldDelete = window.confirm('Do you want to delete');
|
|
50
|
-
if (shouldDelete) {
|
|
51
|
-
handleDelete();
|
|
52
|
-
}
|
|
53
|
-
}, 600);
|
|
54
|
-
};
|
|
45
|
+
useEffect(() => {
|
|
46
|
+
if (!isMenuOpen) return;
|
|
55
47
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
48
|
+
const handlePointerDown = (e: MouseEvent | TouchEvent) => {
|
|
49
|
+
const el = actionsRef.current;
|
|
50
|
+
if (!el) return;
|
|
51
|
+
if (e.target instanceof Element && el.contains(e.target)) return;
|
|
52
|
+
setIsMenuOpen(false);
|
|
53
|
+
};
|
|
62
54
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
55
|
+
const handleKeyDown = (e: KeyboardEvent) => {
|
|
56
|
+
if (e.key === 'Escape') setIsMenuOpen(false);
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
document.addEventListener('mousedown', handlePointerDown);
|
|
60
|
+
document.addEventListener('touchstart', handlePointerDown);
|
|
61
|
+
document.addEventListener('keydown', handleKeyDown);
|
|
62
|
+
return () => {
|
|
63
|
+
document.removeEventListener('mousedown', handlePointerDown);
|
|
64
|
+
document.removeEventListener('touchstart', handlePointerDown);
|
|
65
|
+
document.removeEventListener('keydown', handleKeyDown);
|
|
66
|
+
};
|
|
67
|
+
}, [isMenuOpen]);
|
|
66
68
|
|
|
67
69
|
let extra = '';
|
|
68
70
|
if (nodeData.attributes?.condition) {
|
|
@@ -103,17 +105,52 @@ export function BuilderButton({
|
|
|
103
105
|
</button>
|
|
104
106
|
</div>
|
|
105
107
|
)}
|
|
106
|
-
<
|
|
107
|
-
className="builder__button-link"
|
|
108
|
-
onMouseDown={handlePressStart}
|
|
109
|
-
onMouseUp={handlePressEnd}
|
|
110
|
-
onMouseLeave={handlePressCancel}
|
|
111
|
-
onTouchStart={handlePressStart}
|
|
112
|
-
onTouchEnd={handlePressEnd}
|
|
113
|
-
onTouchCancel={handlePressCancel}
|
|
114
|
-
>
|
|
108
|
+
<button type="button" className="builder__button-link" onClick={onClick}>
|
|
115
109
|
{baseLabel}
|
|
116
|
-
</
|
|
110
|
+
</button>
|
|
111
|
+
<div className="builder__button-actions" ref={actionsRef}>
|
|
112
|
+
<button
|
|
113
|
+
type="button"
|
|
114
|
+
className="builder__button-actions-trigger"
|
|
115
|
+
aria-label="Open node actions"
|
|
116
|
+
aria-haspopup="menu"
|
|
117
|
+
aria-expanded={isMenuOpen}
|
|
118
|
+
aria-controls={menuId}
|
|
119
|
+
onClick={(event) => {
|
|
120
|
+
event.stopPropagation();
|
|
121
|
+
setIsMenuOpen((v) => !v);
|
|
122
|
+
}}
|
|
123
|
+
>
|
|
124
|
+
<Icon iconType="chevron-right" size={16} />
|
|
125
|
+
</button>
|
|
126
|
+
{isMenuOpen && (
|
|
127
|
+
<ul
|
|
128
|
+
id={menuId}
|
|
129
|
+
className="builder__button-actions-menu"
|
|
130
|
+
role="menu"
|
|
131
|
+
aria-label="Node actions"
|
|
132
|
+
>
|
|
133
|
+
<li role="none">
|
|
134
|
+
<button
|
|
135
|
+
type="button"
|
|
136
|
+
className="builder__button-actions-item builder__button-actions-item--danger"
|
|
137
|
+
role="menuitem"
|
|
138
|
+
disabled={!onDelete}
|
|
139
|
+
onClick={(event) => {
|
|
140
|
+
event.stopPropagation();
|
|
141
|
+
if (!onDelete) return;
|
|
142
|
+
const shouldDelete = window.confirm('Do you want to delete?');
|
|
143
|
+
if (!shouldDelete) return;
|
|
144
|
+
handleDelete();
|
|
145
|
+
setIsMenuOpen(false);
|
|
146
|
+
}}
|
|
147
|
+
>
|
|
148
|
+
Delete
|
|
149
|
+
</button>
|
|
150
|
+
</li>
|
|
151
|
+
</ul>
|
|
152
|
+
)}
|
|
153
|
+
</div>
|
|
117
154
|
{conditionLabel && (
|
|
118
155
|
<span className="builder__button-condition">{conditionLabel}</span>
|
|
119
156
|
)}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import React, { createContext, useContext, useMemo } from 'react';
|
|
2
2
|
import type { Product } from '../paywall/types/paywall-types';
|
|
3
3
|
import type { PaywallBenefits } from '../paywall/types/benefits';
|
|
4
|
+
import { RenderErrorBoundary } from './RenderErrorBoundary';
|
|
4
5
|
|
|
5
6
|
// NOTE: We keep this context intentionally tiny.
|
|
6
7
|
// IMPORTANT: This provider may be mounted once but consumed by multiple `build-components`
|
|
@@ -12,6 +13,8 @@ export type Products = Product;
|
|
|
12
13
|
export type BuilderProviderParams = {
|
|
13
14
|
products: Products[];
|
|
14
15
|
benefits: PaywallBenefits;
|
|
16
|
+
onPaywallClose?: () => void;
|
|
17
|
+
onPaywallSubscribe?: (product?: Product) => void | boolean | Promise<boolean>;
|
|
15
18
|
};
|
|
16
19
|
|
|
17
20
|
type BuilderProviderProps = {
|
|
@@ -31,12 +34,29 @@ export function BuilderProvider({ params, children }: BuilderProviderProps) {
|
|
|
31
34
|
params?.benefits && typeof params.benefits === 'object'
|
|
32
35
|
? (params.benefits as PaywallBenefits)
|
|
33
36
|
: {},
|
|
37
|
+
onPaywallClose:
|
|
38
|
+
typeof params?.onPaywallClose === 'function'
|
|
39
|
+
? params.onPaywallClose
|
|
40
|
+
: undefined,
|
|
41
|
+
onPaywallSubscribe:
|
|
42
|
+
typeof params?.onPaywallSubscribe === 'function'
|
|
43
|
+
? params.onPaywallSubscribe
|
|
44
|
+
: undefined,
|
|
34
45
|
}),
|
|
35
|
-
[
|
|
46
|
+
[
|
|
47
|
+
params?.benefits,
|
|
48
|
+
params?.products,
|
|
49
|
+
params?.onPaywallClose,
|
|
50
|
+
params?.onPaywallSubscribe,
|
|
51
|
+
],
|
|
36
52
|
);
|
|
37
53
|
|
|
38
54
|
return (
|
|
39
|
-
<BuilderContext.Provider value={value}>
|
|
55
|
+
<BuilderContext.Provider value={value}>
|
|
56
|
+
<RenderErrorBoundary subtitle="caught by BuilderProvider">
|
|
57
|
+
{children}
|
|
58
|
+
</RenderErrorBoundary>
|
|
59
|
+
</BuilderContext.Provider>
|
|
40
60
|
);
|
|
41
61
|
}
|
|
42
62
|
|
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { Device } from '../types/Device';
|
|
3
|
-
import
|
|
4
|
-
import iosIcon from '../assets/images/apple.svg';
|
|
3
|
+
import { getImage, TribeAssetName } from '../utils/getImage';
|
|
5
4
|
|
|
6
5
|
const platformIcons: Record<string, string> = {
|
|
7
|
-
android:
|
|
8
|
-
ios:
|
|
6
|
+
android: getImage(TribeAssetName.Android),
|
|
7
|
+
ios: getImage(TribeAssetName.Apple),
|
|
9
8
|
};
|
|
10
9
|
|
|
11
10
|
type DeviceButtonProps = {
|
|
@@ -20,6 +19,14 @@ export function DeviceButton({
|
|
|
20
19
|
onSelect,
|
|
21
20
|
}: DeviceButtonProps) {
|
|
22
21
|
const platformIcon = platformIcons[device.platform];
|
|
22
|
+
const aspect =
|
|
23
|
+
device.aspect ??
|
|
24
|
+
(() => {
|
|
25
|
+
const r = device.height / device.width;
|
|
26
|
+
if (r >= 2.05) return 'tall' as const;
|
|
27
|
+
if (r <= 1.75) return 'wide' as const;
|
|
28
|
+
return 'regular' as const;
|
|
29
|
+
})();
|
|
23
30
|
|
|
24
31
|
return (
|
|
25
32
|
<button
|
|
@@ -30,7 +37,7 @@ export function DeviceButton({
|
|
|
30
37
|
onClick={() => onSelect(device)}
|
|
31
38
|
>
|
|
32
39
|
{device.name} <br />
|
|
33
|
-
{device.width}
|
|
40
|
+
{device.width}×{device.height} ({aspect})
|
|
34
41
|
{platformIcon && <img src={platformIcon} alt="" aria-hidden="true" />}
|
|
35
42
|
</button>
|
|
36
43
|
);
|