@developer_tribe/react-builder 1.2.40 → 1.2.42

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (71) hide show
  1. package/dist/attributes-editor/FallbackLocalizationField.d.ts +6 -0
  2. package/dist/build-components/PaywallFooter/PaywallFooter.d.ts +5 -0
  3. package/dist/build-components/PaywallFooter/PaywallFooterProps.generated.d.ts +68 -0
  4. package/dist/build-components/index.d.ts +2 -1
  5. package/dist/build-components/patterns.generated.d.ts +495 -3
  6. package/dist/index.cjs.js +1 -1
  7. package/dist/index.cjs.js.map +1 -1
  8. package/dist/index.esm.js +1 -1
  9. package/dist/index.esm.js.map +1 -1
  10. package/dist/index.web.cjs.js +4 -4
  11. package/dist/index.web.cjs.js.map +1 -1
  12. package/dist/index.web.d.ts +8 -0
  13. package/dist/index.web.esm.js +4 -4
  14. package/dist/index.web.esm.js.map +1 -1
  15. package/dist/modals/IconPickerModal.d.ts +1 -1
  16. package/dist/product-base/types.d.ts +3 -0
  17. package/dist/store.d.ts +25 -0
  18. package/dist/styles.css +1 -1
  19. package/dist/types/PreviewConfig.d.ts +1 -1
  20. package/package.json +2 -2
  21. package/scripts/public/bin.js +0 -0
  22. package/src/RenderPage.tsx +1 -1
  23. package/src/assets/meta.json +1 -1
  24. package/src/assets/prompt-scheme-onboard.generated.ts +1 -1
  25. package/src/assets/prompt-scheme-paywall.generated.ts +1 -1
  26. package/src/assets/samples/paywall-1.json +18 -1
  27. package/src/assets/samples/paywall-2.json +2 -2
  28. package/src/assets/samples/paywall-app-delete-offer.json +2 -2
  29. package/src/assets/samples/paywall-app-open-offer.json +2 -2
  30. package/src/assets/samples/paywall-back-offer.json +1 -1
  31. package/src/assets/samples/paywall-notification-offer.json +1 -1
  32. package/src/assets/samples/vpn-onboard-1.json +3 -3
  33. package/src/assets/samples/vpn-onboard-2.json +3 -3
  34. package/src/assets/samples/vpn-onboard-3.json +3 -3
  35. package/src/assets/samples/vpn-onboard-4.json +3 -3
  36. package/src/assets/samples/vpn-onboard-5.json +3 -3
  37. package/src/assets/samples/vpn-onboard-6.json +3 -3
  38. package/src/assets/samples/vpn-onboard-7.json +3 -3
  39. package/src/attributes-editor/AttributesEditorFields.tsx +1 -1
  40. package/src/attributes-editor/AttributesEditorView.tsx +17 -6
  41. package/src/attributes-editor/FallbackLocalizationField.tsx +384 -0
  42. package/src/build-components/BIcon/BIcon.tsx +1 -1
  43. package/src/build-components/OnboardButton/OnboardButton.tsx +1 -1
  44. package/src/build-components/OnboardButton/pattern.json +1 -1
  45. package/src/build-components/OnboardFooter/OnboardFooter.tsx +29 -20
  46. package/src/build-components/OnboardFooter/pattern.json +2 -1
  47. package/src/build-components/OnboardProvider/pattern.json +1 -1
  48. package/src/build-components/PaywallCloseButton/PaywallCloseButton.tsx +1 -1
  49. package/src/build-components/PaywallFooter/PaywallFooter.tsx +242 -0
  50. package/src/build-components/PaywallFooter/PaywallFooterProps.generated.ts +85 -0
  51. package/src/build-components/PaywallFooter/pattern.json +86 -0
  52. package/src/build-components/RenderNode.generated.tsx +5 -0
  53. package/src/build-components/index.ts +5 -0
  54. package/src/build-components/patterns.generated.ts +511 -3
  55. package/src/components/BottomBar.tsx +136 -32
  56. package/src/components/DeviceNavigationBar.tsx +2 -2
  57. package/src/hooks/useLocalize.ts +13 -1
  58. package/src/index.web.ts +19 -0
  59. package/src/mockOS/managers/mockPermissionManager.ts +5 -3
  60. package/src/mockOS/managers/navigationManager.ts +6 -4
  61. package/src/modals/IconPickerModal.tsx +1 -1
  62. package/src/modals/InspectModal.tsx +106 -0
  63. package/src/product-base/buildPaywallLocalizationParams.ts +3 -0
  64. package/src/product-base/types.ts +3 -0
  65. package/src/store.ts +39 -0
  66. package/src/styles/base/_global.scss +1 -1
  67. package/src/types/PreviewConfig.ts +30 -6
  68. package/dist/build-components/index.generated.d.ts +0 -38
  69. package/dist/types/Icons.generated.d.ts +0 -2
  70. package/src/build-components/index.generated.ts +0 -184
  71. package/src/types/Icons.generated.ts +0 -244
@@ -41,7 +41,7 @@
41
41
  "meta": {
42
42
  "desiredParent": ["=OnboardButtons"],
43
43
  "label": "Onboard Button",
44
- "description": "Single action button for onboarding.",
44
+ "description": "Single action button for onboard.",
45
45
  "attributes": {
46
46
  "labelKey": {
47
47
  "label": "Label Key",
@@ -7,7 +7,7 @@ import { useExtractViewStyle } from '../../attribute-analyser/style/web/useExtra
7
7
  import { useLogRender } from '../../utils/useLogRender';
8
8
  import { isNodeSelected, SELECTED_OUTLINE_STYLE } from '../../utils/selection';
9
9
  import { useMergedStyle } from '../../utils/useMergedStyle';
10
- import { defaultLocalization } from '../../types/PreviewConfig';
10
+ import { useLocalize } from '../../hooks/useLocalize';
11
11
  import { parseColor } from '../../utils/parseColor';
12
12
  import { toAttributeRecord } from '../../utils/attributeStyle';
13
13
 
@@ -96,17 +96,14 @@ function OnboardFooter({ node }: OnboardFooterComponentProps) {
96
96
  node = useNode(node);
97
97
  const attributeName = node.sourceType ?? node.type ?? 'OnboardFooter';
98
98
  const {
99
- localization: builderLocalization,
100
99
  selectedTheme: theme,
101
- selectedLanguage,
102
100
  previewMode,
103
101
  selectedKey,
104
102
  projectColors: builderProjectColors,
105
103
  } = useBuilderParams();
106
- const localization = builderLocalization ?? defaultLocalization;
107
104
  const projectColors = builderProjectColors;
108
- const lang = selectedLanguage ?? 'en';
109
- const t = (key?: string) => (key ? (localization?.[lang]?.[key] ?? key) : '');
105
+ const localize = useLocalize();
106
+ const t = (key?: string) => (key ? localize(key) : '');
110
107
 
111
108
  const attrs = node?.attributes;
112
109
  const attrRecord = toAttributeRecord(attrs);
@@ -178,18 +175,25 @@ function OnboardFooter({ node }: OnboardFooterComponentProps) {
178
175
  };
179
176
 
180
177
  return (
181
- <>
178
+ <span
179
+ style={{
180
+ ...mergedTextStyle,
181
+ display: '-webkit-box',
182
+ WebkitLineClamp: 2,
183
+ WebkitBoxOrient: 'vertical',
184
+ overflow: 'hidden',
185
+ textOverflow: 'ellipsis',
186
+ }}
187
+ >
182
188
  {segments.map((seg, i) =>
183
189
  seg.type === 'text' ? (
184
- <span key={i} style={mergedTextStyle}>
185
- {seg.value}
186
- </span>
190
+ <React.Fragment key={i}>{seg.value}</React.Fragment>
187
191
  ) : (
188
192
  <span
189
193
  key={i}
190
194
  style={{
191
- ...mergedTextStyle,
192
- color: seg.color,
195
+ ...(seg.color ? { color: seg.color } : {}),
196
+ cursor: 'pointer',
193
197
  }}
194
198
  onClick={() => handleClick(seg.page)}
195
199
  >
@@ -198,32 +202,37 @@ function OnboardFooter({ node }: OnboardFooterComponentProps) {
198
202
  ),
199
203
  )}
200
204
  {matchCount === 0 && (
201
- <div>
205
+ <>
206
+ {' '}
202
207
  {attrs?.linkedWordFirstLocalizationKey && (
203
208
  <span
204
209
  style={{
205
- ...mergedTextStyle,
206
- color: parsedFirstColor ?? undefined,
210
+ ...(parsedFirstColor
211
+ ? { color: parsedFirstColor }
212
+ : {}),
213
+ cursor: 'pointer',
207
214
  }}
208
215
  onClick={() => handleClick(attrs?.linkedWordFirstPage)}
209
216
  >
210
217
  {firstText}
211
218
  </span>
212
- )}
219
+ )}{' '}
213
220
  {attrs?.linkedWordSecondLocalizationKey && (
214
221
  <span
215
222
  style={{
216
- ...mergedTextStyle,
217
- color: parsedSecondColor ?? undefined,
223
+ ...(parsedSecondColor
224
+ ? { color: parsedSecondColor }
225
+ : {}),
226
+ cursor: 'pointer',
218
227
  }}
219
228
  onClick={() => handleClick(attrs?.linkedWordSecondPage)}
220
229
  >
221
230
  {secondText}
222
231
  </span>
223
232
  )}
224
- </div>
233
+ </>
225
234
  )}
226
- </>
235
+ </span>
227
236
  );
228
237
  })()}
229
238
  </div>
@@ -18,12 +18,13 @@
18
18
  },
19
19
  "defaults": {
20
20
  "styles": {
21
+ "minHeight":"50@vs",
21
22
  "flexDirection": "row",
22
23
  "flexWrap": "wrap",
23
24
  "alignItems": "center",
24
25
  "justifyContent": "center",
25
26
  "textAlign": "center",
26
- "paddingHorizontal": "24@s"
27
+ "paddingHorizontal": "16@s"
27
28
  }
28
29
  },
29
30
  "meta": {
@@ -20,7 +20,7 @@
20
20
  "meta": {
21
21
  "desiredParent": ["root"],
22
22
  "label": "Onboard Provider",
23
- "description": "Provides shared settings for onboarding.",
23
+ "description": "Provides shared settings for onboard.",
24
24
  "styles": {},
25
25
  "attributes": {
26
26
  "theme": {
@@ -8,7 +8,7 @@ import { useLogRender } from '../../utils/useLogRender';
8
8
  import { isNodeSelected, SELECTED_OUTLINE_STYLE } from '../../utils/selection';
9
9
  import { useMergedStyle } from '../../utils/useMergedStyle';
10
10
  import { Icon } from '../../components/Icon.generated';
11
- import { IconsType } from '../../types/Icons.generated';
11
+ import { IconsType } from '../../types/Icons';
12
12
  import { usePaywallContext } from '../PaywallProvider/PaywallContext';
13
13
 
14
14
  export function PaywallCloseButton({ node }: PaywallCloseButtonComponentProps) {
@@ -0,0 +1,242 @@
1
+ import React, { useId, useMemo } from 'react';
2
+ import type { PaywallFooterComponentProps } from './PaywallFooterProps.generated';
3
+ import useNode from '../useNode';
4
+ import { useBuilderParams } from '../../components/BuilderProvider';
5
+ import { useExtractTextStyle } from '../../attribute-analyser/style/web/useExtractTextStyle';
6
+ import { useExtractViewStyle } from '../../attribute-analyser/style/web/useExtractViewStyle';
7
+ import { useLogRender } from '../../utils/useLogRender';
8
+ import { isNodeSelected, SELECTED_OUTLINE_STYLE } from '../../utils/selection';
9
+ import { useMergedStyle } from '../../utils/useMergedStyle';
10
+ import { useLocalize } from '../../hooks/useLocalize';
11
+ import { parseColor } from '../../utils/parseColor';
12
+ import { toAttributeRecord } from '../../utils/attributeStyle';
13
+
14
+ type Segment =
15
+ | { type: 'text'; value: string }
16
+ | {
17
+ type: 'match';
18
+ value: string;
19
+ color?: string;
20
+ page?: string;
21
+ };
22
+
23
+ function escapeRegExp(str: string) {
24
+ return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
25
+ }
26
+
27
+ function buildSegments(
28
+ text: string,
29
+ patterns: { value: string; color?: string; page?: string }[],
30
+ ): { segments: Segment[]; matchCount: number } {
31
+ type RawMatch = {
32
+ start: number;
33
+ end: number;
34
+ value: string;
35
+ color?: string;
36
+ page?: string;
37
+ };
38
+
39
+ const rawMatches: RawMatch[] = [];
40
+
41
+ for (const p of patterns) {
42
+ if (!p.value) continue;
43
+ const re = new RegExp(escapeRegExp(p.value), 'gi');
44
+ for (const m of text.matchAll(re)) {
45
+ if (m.index == null) continue;
46
+ const matched = m[0];
47
+ if (!matched) continue;
48
+ rawMatches.push({
49
+ start: m.index,
50
+ end: m.index + matched.length,
51
+ value: matched,
52
+ color: p.color,
53
+ page: p.page,
54
+ });
55
+ }
56
+ }
57
+
58
+ if (rawMatches.length === 0) {
59
+ return { segments: [{ type: 'text', value: text }], matchCount: 0 };
60
+ }
61
+
62
+ rawMatches.sort((a, b) => {
63
+ if (a.start !== b.start) return a.start - b.start;
64
+ const lenA = a.end - a.start;
65
+ const lenB = b.end - b.start;
66
+ return lenB - lenA;
67
+ });
68
+
69
+ const kept: RawMatch[] = [];
70
+ let lastEnd = -1;
71
+ for (const m of rawMatches) {
72
+ if (m.start >= lastEnd) {
73
+ kept.push(m);
74
+ lastEnd = m.end;
75
+ }
76
+ }
77
+
78
+ const segments: Segment[] = [];
79
+ let cursor = 0;
80
+ for (const { start, end, value, color, page } of kept) {
81
+ if (start > cursor) {
82
+ segments.push({ type: 'text', value: text.slice(cursor, start) });
83
+ }
84
+ segments.push({ type: 'match', value, color, page });
85
+ cursor = end;
86
+ }
87
+ if (cursor < text.length) {
88
+ segments.push({ type: 'text', value: text.slice(cursor) });
89
+ }
90
+
91
+ return { segments, matchCount: kept.length };
92
+ }
93
+
94
+ function PaywallFooter({ node }: PaywallFooterComponentProps) {
95
+ useLogRender('PaywallFooter');
96
+ node = useNode(node);
97
+ const attributeName = node.sourceType ?? node.type ?? 'PaywallFooter';
98
+ const {
99
+ selectedTheme: theme,
100
+ previewMode,
101
+ selectedKey,
102
+ projectColors: builderProjectColors,
103
+ } = useBuilderParams();
104
+ const projectColors = builderProjectColors;
105
+ const localize = useLocalize();
106
+ const t = (key?: string) => (key ? localize(key) : '');
107
+
108
+ const attrs = node?.attributes;
109
+ const attrRecord = toAttributeRecord(attrs);
110
+ const text = t(attrs?.textLocalizationKey);
111
+ const textStyle = useExtractTextStyle(node, true);
112
+ const viewStyle = useExtractViewStyle(node);
113
+
114
+ const rawFirstColor = attrRecord.linkedWordSecondColor as string | undefined;
115
+ const rawSecondColor = attrRecord.linkedWordFirstColor as string | undefined;
116
+
117
+ // Parse colors for linked words
118
+ const parsedFirstColor = useMemo(
119
+ () =>
120
+ parseColor(rawFirstColor, {
121
+ projectColors,
122
+ theme,
123
+ }),
124
+ [rawFirstColor, projectColors, theme],
125
+ );
126
+ const parsedSecondColor = useMemo(
127
+ () =>
128
+ parseColor(rawSecondColor, {
129
+ projectColors,
130
+ theme,
131
+ }),
132
+ [rawSecondColor, projectColors, theme],
133
+ );
134
+
135
+ const mergedTextStyle = useMergedStyle(textStyle, {
136
+ cursor: 'pointer',
137
+ });
138
+ const isSelected = isNodeSelected({
139
+ previewMode: !!previewMode,
140
+ current: selectedKey ? { key: selectedKey } : undefined,
141
+ node,
142
+ });
143
+ const mergedViewStyle = useMergedStyle(
144
+ viewStyle,
145
+ isSelected ? SELECTED_OUTLINE_STYLE : undefined,
146
+ );
147
+ const generatedId = useId();
148
+ const attributeKey = node.key ?? generatedId;
149
+
150
+ return (
151
+ <div
152
+ attribute-name={attributeName}
153
+ attribute-key={attributeKey}
154
+ style={mergedViewStyle}
155
+ >
156
+ {!!text &&
157
+ (() => {
158
+ const firstText = t(attrs?.linkedWordFirstLocalizationKey);
159
+ const secondText = t(attrs?.linkedWordSecondLocalizationKey);
160
+ const { segments, matchCount } = buildSegments(text, [
161
+ {
162
+ value: firstText,
163
+ color: parsedFirstColor ?? undefined,
164
+ page: attrs?.linkedWordFirstPage,
165
+ },
166
+ {
167
+ value: secondText,
168
+ color: parsedSecondColor ?? undefined,
169
+ page: attrs?.linkedWordSecondPage,
170
+ },
171
+ ]);
172
+
173
+ const handleClick = (page?: string) => {
174
+ if (!page) return;
175
+ };
176
+
177
+ return (
178
+ <span
179
+ style={{
180
+ ...mergedTextStyle,
181
+ display: '-webkit-box',
182
+ WebkitLineClamp: 2,
183
+ WebkitBoxOrient: 'vertical',
184
+ overflow: 'hidden',
185
+ textOverflow: 'ellipsis',
186
+ }}
187
+ >
188
+ {segments.map((seg, i) =>
189
+ seg.type === 'text' ? (
190
+ <React.Fragment key={i}>{seg.value}</React.Fragment>
191
+ ) : (
192
+ <span
193
+ key={i}
194
+ style={{
195
+ ...(seg.color ? { color: seg.color } : {}),
196
+ cursor: 'pointer',
197
+ }}
198
+ onClick={() => handleClick(seg.page)}
199
+ >
200
+ {seg.value}
201
+ </span>
202
+ ),
203
+ )}
204
+ {matchCount === 0 && (
205
+ <>
206
+ {' '}
207
+ {attrs?.linkedWordFirstLocalizationKey && (
208
+ <span
209
+ style={{
210
+ ...(parsedFirstColor
211
+ ? { color: parsedFirstColor }
212
+ : {}),
213
+ cursor: 'pointer',
214
+ }}
215
+ onClick={() => handleClick(attrs?.linkedWordFirstPage)}
216
+ >
217
+ {firstText}
218
+ </span>
219
+ )}{' '}
220
+ {attrs?.linkedWordSecondLocalizationKey && (
221
+ <span
222
+ style={{
223
+ ...(parsedSecondColor
224
+ ? { color: parsedSecondColor }
225
+ : {}),
226
+ cursor: 'pointer',
227
+ }}
228
+ onClick={() => handleClick(attrs?.linkedWordSecondPage)}
229
+ >
230
+ {secondText}
231
+ </span>
232
+ )}
233
+ </>
234
+ )}
235
+ </span>
236
+ );
237
+ })()}
238
+ </div>
239
+ );
240
+ }
241
+
242
+ export default React.memo(PaywallFooter);
@@ -0,0 +1,85 @@
1
+ /* AUTO-GENERATED FILE - DO NOT EDIT */
2
+
3
+ import type { NodeData } from '../../types/Node';
4
+
5
+ export type FlexDirectionOptionType = 'row' | 'column';
6
+ export type FlexWrapOptionType = 'nowrap' | 'wrap' | 'wrap-reverse';
7
+ export type AlignItemsOptionType =
8
+ | 'flex-start'
9
+ | 'center'
10
+ | 'flex-end'
11
+ | 'stretch'
12
+ | 'baseline';
13
+ export type JustifyContentOptionType =
14
+ | 'flex-start'
15
+ | 'center'
16
+ | 'flex-end'
17
+ | 'space-between'
18
+ | 'space-around'
19
+ | 'space-evenly';
20
+ export type PositionOptionType = 'relative' | 'absolute';
21
+
22
+ export interface PaywallFooterStyleGenerated {
23
+ color?: string;
24
+ fontSize?: string;
25
+ fontFamily?: string;
26
+ fontWeight?: string;
27
+ textAlign?: string;
28
+ flexDirection?: FlexDirectionOptionType;
29
+ flexWrap?: FlexWrapOptionType;
30
+ alignItems?: AlignItemsOptionType;
31
+ justifyContent?: JustifyContentOptionType;
32
+ gap?: string;
33
+ padding?: string;
34
+ paddingHorizontal?: string;
35
+ paddingVertical?: string;
36
+ paddingTop?: string;
37
+ paddingBottom?: string;
38
+ paddingLeft?: string;
39
+ paddingRight?: string;
40
+ margin?: string;
41
+ marginHorizontal?: string;
42
+ marginVertical?: string;
43
+ marginTop?: string;
44
+ marginBottom?: string;
45
+ marginLeft?: string;
46
+ marginRight?: string;
47
+ backgroundColor?: string;
48
+ borderRadius?: string;
49
+ width?: string;
50
+ minWidth?: string;
51
+ maxWidth?: string;
52
+ height?: string;
53
+ minHeight?: string;
54
+ maxHeight?: string;
55
+ flex?: number;
56
+ position?: PositionOptionType;
57
+ top?: string;
58
+ bottom?: string;
59
+ left?: string;
60
+ right?: string;
61
+ zIndex?: number;
62
+ }
63
+
64
+ export interface PaywallFooterPropsGenerated {
65
+ child: string;
66
+ attributes: {
67
+ styles?: PaywallFooterStyleGenerated;
68
+ adjustsFontSizeToFit?: boolean;
69
+ showEllipsis?: boolean;
70
+ translateCounter?: number;
71
+ scrollable?: boolean;
72
+ testID?: string;
73
+ textLocalizationKey?: string;
74
+ linkedWordFirstLocalizationKey?: string;
75
+ linkedWordFirstColor?: string;
76
+ linkedWordFirstPage?: string;
77
+ linkedWordSecondLocalizationKey?: string;
78
+ linkedWordSecondColor?: string;
79
+ linkedWordSecondPage?: string;
80
+ };
81
+ }
82
+
83
+ export interface PaywallFooterComponentProps {
84
+ node: NodeData<PaywallFooterPropsGenerated['attributes']>;
85
+ }
@@ -0,0 +1,86 @@
1
+ {
2
+ "schemaVersion": 2,
3
+ "pattern": {
4
+ "type": "PaywallFooter",
5
+ "title": "title",
6
+ "description": "description",
7
+ "children": "never",
8
+ "extends": "Text",
9
+ "attributes": {
10
+ "textLocalizationKey": "string",
11
+ "linkedWordFirstLocalizationKey": "string",
12
+ "linkedWordFirstColor": "color",
13
+ "linkedWordFirstPage": "string",
14
+ "linkedWordSecondLocalizationKey": "string",
15
+ "linkedWordSecondColor": "color",
16
+ "linkedWordSecondPage": "string"
17
+ }
18
+ },
19
+ "defaults": {
20
+ "styles": {
21
+ "minHeight":"50@vs",
22
+ "flexDirection": "row",
23
+ "flexWrap": "wrap",
24
+ "alignItems": "center",
25
+ "justifyContent": "center",
26
+ "textAlign": "center",
27
+ "paddingHorizontal": "16@s"
28
+ }
29
+ },
30
+ "meta": {
31
+ "desiredParent": [">PaywallProvider"],
32
+ "label": "Paywall Footer",
33
+ "description": "Footer text with optional links for paywall screens.",
34
+ "attributes": {
35
+ "textLocalizationKey": {
36
+ "label": "Text Localization Key",
37
+ "description": "Localization key for the footer text.",
38
+ "category": "other",
39
+ "specialCategory": null,
40
+ "sort": 1
41
+ },
42
+ "linkedWordFirstLocalizationKey": {
43
+ "label": "Linked Word First Localization Key",
44
+ "description": "Key for the first linked word.",
45
+ "category": "other",
46
+ "specialCategory": null,
47
+ "sort": 2
48
+ },
49
+ "linkedWordFirstColor": {
50
+ "label": "Linked Word First Color",
51
+ "description": "Color of the first linked word.",
52
+ "category": "other",
53
+ "specialCategory": null,
54
+ "sort": 3
55
+ },
56
+ "linkedWordFirstPage": {
57
+ "label": "Linked Word First Page",
58
+ "description": "Page opened by the first link.",
59
+ "category": "other",
60
+ "specialCategory": null,
61
+ "sort": 4
62
+ },
63
+ "linkedWordSecondLocalizationKey": {
64
+ "label": "Linked Word Second Localization Key",
65
+ "description": "Key for the second linked word.",
66
+ "category": "other",
67
+ "specialCategory": null,
68
+ "sort": 5
69
+ },
70
+ "linkedWordSecondColor": {
71
+ "label": "Linked Word Second Color",
72
+ "description": "Color of the second linked word.",
73
+ "category": "other",
74
+ "specialCategory": null,
75
+ "sort": 6
76
+ },
77
+ "linkedWordSecondPage": {
78
+ "label": "Linked Word Second Page",
79
+ "description": "Page opened by the second link.",
80
+ "category": "other",
81
+ "specialCategory": null,
82
+ "sort": 7
83
+ }
84
+ }
85
+ }
86
+ }
@@ -37,6 +37,7 @@ import type { OnboardSubtitleComponentProps } from './OnboardSubtitle/OnboardSub
37
37
  import type { OnboardTitleComponentProps } from './OnboardTitle/OnboardTitleProps.generated';
38
38
  import type { PaywallBackgroundComponentProps } from './PaywallBackground/PaywallBackgroundProps.generated';
39
39
  import type { PaywallCloseButtonComponentProps } from './PaywallCloseButton/PaywallCloseButtonProps.generated';
40
+ import type { PaywallFooterComponentProps } from './PaywallFooter/PaywallFooterProps.generated';
40
41
  import type { PaywallOptionsComponentProps } from './PaywallOptions/PaywallOptionsProps.generated';
41
42
  import type { PaywallProviderComponentProps } from './PaywallProvider/PaywallProviderProps.generated';
42
43
  import type { PaywallSubscribeButtonComponentProps } from './PaywallSubscribeButton/PaywallSubscribeButtonProps.generated';
@@ -72,6 +73,7 @@ import OnboardSubtitle from './OnboardSubtitle/OnboardSubtitle';
72
73
  import OnboardTitle from './OnboardTitle/OnboardTitle';
73
74
  import PaywallBackground from './PaywallBackground/PaywallBackground';
74
75
  import { PaywallCloseButton } from './PaywallCloseButton/PaywallCloseButton';
76
+ import PaywallFooter from './PaywallFooter/PaywallFooter';
75
77
  import PaywallOptions from './PaywallOptions/PaywallOptions';
76
78
  import PaywallProvider from './PaywallProvider/PaywallProvider';
77
79
  import PaywallSubscribeButton from './PaywallSubscribeButton/PaywallSubscribeButton';
@@ -109,6 +111,7 @@ type BuilderNode =
109
111
  | (OnboardTitleComponentProps['node'] & { type: 'OnboardTitle' })
110
112
  | (PaywallBackgroundComponentProps['node'] & { type: 'PaywallBackground' })
111
113
  | (PaywallCloseButtonComponentProps['node'] & { type: 'PaywallCloseButton' })
114
+ | (PaywallFooterComponentProps['node'] & { type: 'PaywallFooter' })
112
115
  | (PaywallOptionsComponentProps['node'] & { type: 'PaywallOptions' })
113
116
  | (PaywallProviderComponentProps['node'] & { type: 'PaywallProvider' })
114
117
  | (PaywallSubscribeButtonComponentProps['node'] & {
@@ -201,6 +204,8 @@ function RenderNode({ node }: { node: Node }) {
201
204
  return <PaywallBackground node={normalizedNode} />;
202
205
  case 'PaywallCloseButton':
203
206
  return <PaywallCloseButton node={normalizedNode} />;
207
+ case 'PaywallFooter':
208
+ return <PaywallFooter node={normalizedNode} />;
204
209
  case 'PaywallOptions':
205
210
  return <PaywallOptions node={normalizedNode} />;
206
211
  case 'PaywallProvider':
@@ -29,6 +29,7 @@ export const allcomponentNames = [
29
29
  'OnboardTitle',
30
30
  'PaywallBackground',
31
31
  'PaywallCloseButton',
32
+ 'PaywallFooter',
32
33
  'PaywallOptions',
33
34
  'PaywallProvider',
34
35
  'PaywallSubscribeButton',
@@ -138,6 +139,10 @@ export type {
138
139
  PaywallCloseButtonPropsGenerated,
139
140
  PaywallCloseButtonComponentProps,
140
141
  } from './PaywallCloseButton/PaywallCloseButtonProps.generated';
142
+ export type {
143
+ PaywallFooterPropsGenerated,
144
+ PaywallFooterComponentProps,
145
+ } from './PaywallFooter/PaywallFooterProps.generated';
141
146
  export type {
142
147
  PaywallOptionsPropsGenerated,
143
148
  PaywallOptionsComponentProps,