@developer_tribe/react-builder 1.2.44-test.1 → 1.2.44-test.2

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 (47) hide show
  1. package/dist/attributes-editor/Field.d.ts +3 -1
  2. package/dist/attributes-editor/attributesEditorModelTypes.d.ts +3 -0
  3. package/dist/attributes-editor/useAttributesEditorModel.d.ts +1 -1
  4. package/dist/build-components/FormSubmitButton/FormSubmitButtonProps.generated.d.ts +8 -3
  5. package/dist/build-components/GlobalProvider/globalProviderUtils.d.ts +4 -13
  6. package/dist/build-components/GlobalProvider/useGlobalProviderLogic.d.ts +15 -0
  7. package/dist/build-components/OnboardButton/OnboardButtonProps.generated.d.ts +8 -3
  8. package/dist/build-components/SystemButton/SystemButtonProps.generated.d.ts +8 -3
  9. package/dist/build-components/SystemButton/usePlacementButtonEvents.d.ts +9 -2
  10. package/dist/build-components/patterns.generated.d.ts +15 -9
  11. package/dist/index.cjs.js +1 -1
  12. package/dist/index.cjs.js.map +1 -1
  13. package/dist/index.esm.js +1 -1
  14. package/dist/index.esm.js.map +1 -1
  15. package/dist/index.web.cjs.js +3 -3
  16. package/dist/index.web.cjs.js.map +1 -1
  17. package/dist/index.web.esm.js +3 -3
  18. package/dist/index.web.esm.js.map +1 -1
  19. package/dist/utils/nodeTree.d.ts +18 -0
  20. package/package.json +1 -1
  21. package/scripts/prebuild/utils/validateAllComponentsOrThrow.js +68 -4
  22. package/src/assets/meta.json +1 -1
  23. package/src/assets/samples/global-onboard-flow.json +7 -1
  24. package/src/assets/samples/terms-and-privacy-no-form.json +1 -1
  25. package/src/assets/samples/terms-and-privacy.json +1 -1
  26. package/src/attributes-editor/AttributesEditorView.tsx +3 -0
  27. package/src/attributes-editor/Field.tsx +91 -2
  28. package/src/attributes-editor/attributesEditorModelTypes.ts +3 -0
  29. package/src/attributes-editor/useAttributesEditorModel.ts +8 -0
  30. package/src/build-components/FormCheckbox/FormCheckbox.tsx +3 -1
  31. package/src/build-components/FormSubmitButton/FormSubmitButton.tsx +3 -0
  32. package/src/build-components/FormSubmitButton/FormSubmitButtonProps.generated.ts +26 -3
  33. package/src/build-components/GlobalProvider/GlobalProvider.tsx +4 -144
  34. package/src/build-components/GlobalProvider/globalProviderUtils.ts +79 -38
  35. package/src/build-components/GlobalProvider/useGlobalNavigation.ts +0 -5
  36. package/src/build-components/GlobalProvider/useGlobalProviderLogic.ts +172 -0
  37. package/src/build-components/OnboardButton/OnboardButton.tsx +3 -0
  38. package/src/build-components/OnboardButton/OnboardButtonProps.generated.ts +26 -3
  39. package/src/build-components/OnboardButton/pattern.json +5 -3
  40. package/src/build-components/SystemButton/SystemButton.tsx +3 -0
  41. package/src/build-components/SystemButton/SystemButtonProps.generated.ts +26 -3
  42. package/src/build-components/SystemButton/pattern.json +5 -3
  43. package/src/build-components/SystemButton/usePlacementButtonEvents.ts +22 -9
  44. package/src/build-components/patterns.generated.ts +45 -9
  45. package/src/components/AttributesEditorPanel.tsx +1 -0
  46. package/src/patterns/event-constants.json +19 -0
  47. package/src/utils/nodeTree.ts +115 -0
@@ -1,155 +1,15 @@
1
- import React, { useCallback, useId, useMemo, useRef, useState } from 'react';
1
+ import React from 'react';
2
2
  import type { GlobalProviderComponentProps } from './GlobalProviderProps.generated';
3
- import type { GlobalContextValue, GlobalPage } from './GlobalContext';
4
3
  import { GlobalContext } from './GlobalContext';
5
- import {
6
- buildPages,
7
- loadProgress,
8
- normalizeSkipConditions,
9
- persistProgress,
10
- resolveEffectivePage,
11
- } from './globalProviderUtils';
4
+ import { useGlobalProviderLogic } from './useGlobalProviderLogic';
12
5
  import RenderNode from '../RenderNode.generated';
13
- import useNode from '../useNode';
14
6
  import { useLogRender } from '../../utils/useLogRender';
15
- import type { NodeData } from '../../types/Node';
16
7
 
17
8
  function GlobalProvider({ node }: GlobalProviderComponentProps) {
18
9
  useLogRender('GlobalProvider');
19
- node = useNode(node);
20
10
 
21
- const generatedId = useId();
22
- const attributeName = node.sourceType ?? node.type ?? 'GlobalProvider';
23
- const attributeKey = node.key ?? generatedId;
24
- const attrs = node.attributes;
25
- const shouldPersist = attrs?.persistProgress === true;
26
-
27
- const childNodes = useMemo((): NodeData[] => {
28
- const raw = node.children;
29
- if (!raw) return [];
30
- if (Array.isArray(raw)) return raw as NodeData[];
31
- return [raw as NodeData];
32
- }, [node.children]);
33
-
34
- // skipConditions is stored as SkipConditionEntry[] in the schema (array of
35
- // {pageKey, conditionKey} objects). Normalize to a lookup map here.
36
- const skipConditions = useMemo(
37
- () => normalizeSkipConditions(attrs?.skipConditions),
38
- [attrs?.skipConditions],
39
- );
40
-
41
- const pages = useMemo(
42
- () => buildPages(childNodes, skipConditions),
43
- [childNodes, skipConditions],
44
- );
45
-
46
- const storageKey = attributeKey;
47
-
48
- const [conditions, setConditions] = useState<Record<string, boolean>>(() => {
49
- if (shouldPersist) {
50
- return loadProgress(storageKey)?.conditions ?? {};
51
- }
52
- return {};
53
- });
54
-
55
- const [pageStack, setPageStack] = useState<string[]>(() => {
56
- const firstEffective = (() => {
57
- if (shouldPersist) {
58
- const saved = loadProgress(storageKey);
59
- if (saved?.currentPageKey) return saved.currentPageKey;
60
- }
61
- const requestedKey =
62
- typeof attrs?.initialPage === 'string'
63
- ? attrs.initialPage
64
- : (pages[0]?.key ?? '');
65
- return resolveEffectivePage(requestedKey, pages, {});
66
- })();
67
- return firstEffective ? [firstEffective] : [];
68
- });
69
-
70
- const currentPageKey = pageStack[pageStack.length - 1] ?? pages[0]?.key ?? '';
71
-
72
- // Keep ref of latest conditions for persist calls in callbacks
73
- const conditionsRef = useRef(conditions);
74
- conditionsRef.current = conditions;
75
-
76
- const setCondition = useCallback(
77
- (key: string, value: boolean) => {
78
- setConditions((prev) => {
79
- const next = { ...prev, [key]: value };
80
- if (shouldPersist) {
81
- const currentKey = pageStack[pageStack.length - 1] ?? '';
82
- persistProgress(storageKey, currentKey, next);
83
- }
84
- return next;
85
- });
86
- },
87
- [shouldPersist, storageKey, pageStack],
88
- );
89
-
90
- const navigate = useCallback(
91
- (key: string) => {
92
- const effective = resolveEffectivePage(key, pages, conditionsRef.current);
93
- setPageStack((prev) => {
94
- const last = prev[prev.length - 1];
95
- if (last === effective) return prev;
96
- const next = [...prev, effective];
97
- if (shouldPersist) {
98
- persistProgress(storageKey, effective, conditionsRef.current);
99
- }
100
- return next;
101
- });
102
- },
103
- [pages, shouldPersist, storageKey],
104
- );
105
-
106
- const goNext = useCallback(() => {
107
- const currentIdx = pages.findIndex((p) => p.key === currentPageKey);
108
- if (currentIdx === -1 || currentIdx >= pages.length - 1) return;
109
- const nextPage = pages[currentIdx + 1];
110
- if (!nextPage) return;
111
- navigate(nextPage.key);
112
- }, [pages, currentPageKey, navigate]);
113
-
114
- const goBack = useCallback((): boolean => {
115
- if (pageStack.length <= 1) return false;
116
- setPageStack((prev) => {
117
- const next = prev.slice(0, -1);
118
- const prevKey = next[next.length - 1] ?? '';
119
- if (shouldPersist) {
120
- persistProgress(storageKey, prevKey, conditionsRef.current);
121
- }
122
- return next;
123
- });
124
- return true;
125
- }, [pageStack.length, shouldPersist, storageKey]);
126
-
127
- const contextValue = useMemo<GlobalContextValue>(
128
- () => ({
129
- currentPageKey,
130
- pages,
131
- pageStack,
132
- navigate,
133
- goNext,
134
- goBack,
135
- conditions,
136
- setCondition,
137
- }),
138
- [
139
- currentPageKey,
140
- pages,
141
- pageStack,
142
- navigate,
143
- goNext,
144
- goBack,
145
- conditions,
146
- setCondition,
147
- ],
148
- );
149
-
150
- const activePage: GlobalPage | undefined = pages.find(
151
- (p) => p.key === currentPageKey,
152
- );
11
+ const { attributeName, attributeKey, attrs, activePage, contextValue } =
12
+ useGlobalProviderLogic({ node });
153
13
 
154
14
  const animationClass = (() => {
155
15
  const a = activePage?.animation;
@@ -11,22 +11,32 @@ const TYPE_KEY_MAP: Record<string, string> = {
11
11
  };
12
12
 
13
13
  /**
14
- * Derives a stable page key for a child node.
15
- * Priority: node.key (standard NodeData field) → TYPE_KEY_MAP → "${type}-{index}"
16
- *
17
- * Using node.key avoids adding unknown attributes to child components that
18
- * would fail builder validation.
14
+ * Derives a stable and unique page key for a child node.
15
+ * Priority: node.key → TYPE_KEY_MAP → "${type}-${index}"
19
16
  */
20
- export function derivePageKey(node: NodeData, index: number): string {
17
+ export function derivePageKey(
18
+ node: NodeData,
19
+ index: number,
20
+ existingKeys: Set<string>,
21
+ ): string {
22
+ let baseKey = '';
21
23
  if (typeof node.key === 'string' && node.key.trim()) {
22
- return node.key.trim();
24
+ baseKey = node.key.trim();
25
+ } else {
26
+ baseKey = TYPE_KEY_MAP[node.type] || node.type?.toLowerCase?.() || 'page';
23
27
  }
24
- const mapped = TYPE_KEY_MAP[node.type];
25
- if (mapped) {
26
- return mapped;
28
+
29
+ let finalKey = baseKey;
30
+ let counter = 1;
31
+
32
+ // Ensure uniqueness among siblings
33
+ while (existingKeys.has(finalKey)) {
34
+ finalKey = `${baseKey}-${index + counter}`;
35
+ counter++;
27
36
  }
28
- const typeSlug = node.type?.toLowerCase?.() ?? 'page';
29
- return `${typeSlug}-${index}`;
37
+
38
+ existingKeys.add(finalKey);
39
+ return finalKey;
30
40
  }
31
41
 
32
42
  /** Runtime shape of a SkipConditionEntry (mirrors the types block in pattern.json). */
@@ -37,8 +47,6 @@ export interface SkipConditionEntry {
37
47
 
38
48
  /**
39
49
  * Converts SkipConditionEntry[] (from pattern schema) to a lookup map for fast access.
40
- * Array format is used in pattern.json because the schema system doesn't support
41
- * plain Record/object attribute types.
42
50
  */
43
51
  export function normalizeSkipConditions(raw: unknown): Record<string, string> {
44
52
  if (!Array.isArray(raw)) return {};
@@ -60,17 +68,14 @@ export function normalizeSkipConditions(raw: unknown): Record<string, string> {
60
68
 
61
69
  /**
62
70
  * Builds the page list for GlobalProvider.
63
- *
64
- * @param children - direct child nodes of GlobalProvider
65
- * @param skipConditions - lookup map of pageKey → conditionKey, already normalized
66
- * from SkipConditionEntry[] via normalizeSkipConditions().
67
71
  */
68
72
  export function buildPages(
69
73
  children: NodeData[],
70
74
  skipConditions: Record<string, string> = {},
71
75
  ): GlobalPage[] {
76
+ const existingKeys = new Set<string>();
72
77
  return children.map((node, index) => {
73
- const key = derivePageKey(node, index);
78
+ const key = derivePageKey(node, index, existingKeys);
74
79
  const skipIf = skipConditions[key] ?? undefined;
75
80
  const animation =
76
81
  typeof (node.attributes as Record<string, unknown>)?.animation ===
@@ -92,10 +97,11 @@ export function resolveEffectivePage(
92
97
  targetKey: string,
93
98
  pages: GlobalPage[],
94
99
  conditions: Record<string, boolean>,
95
- ): string {
100
+ ): string | null {
96
101
  const targetIdx = pages.findIndex((p) => p.key === targetKey);
97
102
  if (targetIdx === -1) {
98
- return pages[0]?.key ?? targetKey;
103
+ // If target not found, start from the first non-skipped page
104
+ return resolveFirstNonSkippedPage(pages, conditions);
99
105
  }
100
106
 
101
107
  // Walk forward from target, skipping pages whose condition is met
@@ -108,34 +114,73 @@ export function resolveEffectivePage(
108
114
  return page.key;
109
115
  }
110
116
 
111
- // All remaining pages are skipped return last page key anyway
112
- return pages[pages.length - 1]?.key ?? targetKey;
117
+ // If all remaining pages are skipped, return null instead of falling back to the last page.
118
+ // This allows the caller to handle the "no available pages" state explicitly.
119
+ return null;
120
+ }
121
+
122
+ function resolveFirstNonSkippedPage(
123
+ pages: GlobalPage[],
124
+ conditions: Record<string, boolean>,
125
+ ): string | null {
126
+ for (const page of pages) {
127
+ if (!(page.skipIf && conditions[page.skipIf] === true)) {
128
+ return page.key;
129
+ }
130
+ }
131
+ return null;
113
132
  }
114
133
 
115
134
  const STORAGE_KEY_PREFIX = 'global-provider-progress';
116
135
 
136
+ /**
137
+ * Internal storage abstraction. In a real RN environment, this would be swapped
138
+ * for AsyncStorage. For now, it defaults to localStorage with safe checks.
139
+ */
140
+ const storage = {
141
+ getItem: (key: string) => {
142
+ try {
143
+ return typeof localStorage !== 'undefined'
144
+ ? localStorage.getItem(key)
145
+ : null;
146
+ } catch {
147
+ return null;
148
+ }
149
+ },
150
+ setItem: (key: string, value: string) => {
151
+ try {
152
+ if (typeof localStorage !== 'undefined') localStorage.setItem(key, value);
153
+ } catch {
154
+ // ignore
155
+ }
156
+ },
157
+ removeItem: (key: string) => {
158
+ try {
159
+ if (typeof localStorage !== 'undefined') localStorage.removeItem(key);
160
+ } catch {
161
+ // ignore
162
+ }
163
+ },
164
+ };
165
+
117
166
  export function persistProgress(
118
167
  storageKey: string,
119
168
  currentPageKey: string,
120
169
  conditions: Record<string, boolean>,
121
170
  ): void {
122
- try {
123
- localStorage.setItem(
124
- `${STORAGE_KEY_PREFIX}-${storageKey}`,
125
- JSON.stringify({ currentPageKey, conditions }),
126
- );
127
- } catch {
128
- // localStorage unavailable (SSR, private browsing etc.)
129
- }
171
+ storage.setItem(
172
+ `${STORAGE_KEY_PREFIX}-${storageKey}`,
173
+ JSON.stringify({ currentPageKey, conditions }),
174
+ );
130
175
  }
131
176
 
132
177
  export function loadProgress(storageKey: string): {
133
178
  currentPageKey: string;
134
179
  conditions: Record<string, boolean>;
135
180
  } | null {
181
+ const raw = storage.getItem(`${STORAGE_KEY_PREFIX}-${storageKey}`);
182
+ if (!raw) return null;
136
183
  try {
137
- const raw = localStorage.getItem(`${STORAGE_KEY_PREFIX}-${storageKey}`);
138
- if (!raw) return null;
139
184
  const parsed = JSON.parse(raw) as unknown;
140
185
  if (
141
186
  parsed &&
@@ -155,9 +200,5 @@ export function loadProgress(storageKey: string): {
155
200
  }
156
201
 
157
202
  export function clearProgress(storageKey: string): void {
158
- try {
159
- localStorage.removeItem(`${STORAGE_KEY_PREFIX}-${storageKey}`);
160
- } catch {
161
- // localStorage unavailable
162
- }
203
+ storage.removeItem(`${STORAGE_KEY_PREFIX}-${storageKey}`);
163
204
  }
@@ -36,8 +36,6 @@ export function useGlobalNavigation(): (target: string) => boolean {
36
36
  (target: string): boolean => {
37
37
  if (globalCtx) {
38
38
  // Direct page key match
39
- console.log('target', target);
40
- console.log('globalCtx.pages', globalCtx.pages);
41
39
  if (globalCtx.pages.some((p) => p.key === target)) {
42
40
  globalCtx.navigate(target);
43
41
  return true;
@@ -45,18 +43,15 @@ export function useGlobalNavigation(): (target: string) => boolean {
45
43
 
46
44
  // Alias resolution: map incoming target to canonical page key
47
45
  const canonicalKey = GLOBAL_ROUTE_ALIASES[target];
48
- console.log('canonicalKey', canonicalKey);
49
46
  if (
50
47
  canonicalKey &&
51
48
  globalCtx.pages.some((p) => p.key === canonicalKey)
52
49
  ) {
53
- console.log('navigate to canonicalKey', canonicalKey);
54
50
  globalCtx.navigate(canonicalKey);
55
51
  return true;
56
52
  }
57
53
  }
58
54
 
59
- console.log('fallback to MockOSContext', target);
60
55
  // Fallback: delegate to MockOSContext
61
56
  if (mockOS) {
62
57
  mockOS.navigation(target as RouteType);
@@ -0,0 +1,172 @@
1
+ import { useCallback, useId, useMemo, useRef, useState } from 'react';
2
+ import type { GlobalContextValue } from './GlobalContext';
3
+ import {
4
+ buildPages,
5
+ loadProgress,
6
+ normalizeSkipConditions,
7
+ persistProgress,
8
+ resolveEffectivePage,
9
+ } from './globalProviderUtils';
10
+ import useNode from '../useNode';
11
+ import type { NodeData } from '../../types/Node';
12
+
13
+ export interface UseGlobalProviderLogicOptions {
14
+ node: NodeData;
15
+ }
16
+
17
+ export function useGlobalProviderLogic({
18
+ node: rawNode,
19
+ }: UseGlobalProviderLogicOptions) {
20
+ const node = useNode(rawNode);
21
+
22
+ const generatedId = useId();
23
+ const attributeName = node.sourceType ?? node.type ?? 'GlobalProvider';
24
+ const attributeKey = node.key ?? generatedId;
25
+ const attrs = node.attributes;
26
+ const shouldPersist = attrs?.persistProgress === true;
27
+
28
+ const childNodes = useMemo((): NodeData[] => {
29
+ const raw = node.children;
30
+ if (!raw) return [];
31
+ if (Array.isArray(raw)) return raw as NodeData[];
32
+ return [raw as NodeData];
33
+ }, [node.children]);
34
+
35
+ const skipConditions = useMemo(
36
+ () => normalizeSkipConditions(attrs?.skipConditions),
37
+ [attrs?.skipConditions],
38
+ );
39
+
40
+ const pages = useMemo(
41
+ () => buildPages(childNodes, skipConditions),
42
+ [childNodes, skipConditions],
43
+ );
44
+
45
+ const storageKey = attributeKey;
46
+
47
+ const [conditions, setConditions] = useState<Record<string, boolean>>(() => {
48
+ if (shouldPersist) {
49
+ return loadProgress(storageKey)?.conditions ?? {};
50
+ }
51
+ return {};
52
+ });
53
+
54
+ const [pageStack, setPageStack] = useState<string[]>(() => {
55
+ const saved = shouldPersist ? loadProgress(storageKey) : null;
56
+ const initialConditions = saved?.conditions ?? {};
57
+
58
+ const firstEffective = (() => {
59
+ if (saved?.currentPageKey) {
60
+ return resolveEffectivePage(
61
+ saved.currentPageKey,
62
+ pages,
63
+ initialConditions,
64
+ );
65
+ }
66
+
67
+ const requestedKey =
68
+ typeof attrs?.initialPage === 'string'
69
+ ? attrs.initialPage
70
+ : (pages[0]?.key ?? '');
71
+ return resolveEffectivePage(requestedKey, pages, initialConditions);
72
+ })();
73
+ return firstEffective ? [firstEffective] : [];
74
+ });
75
+
76
+ const currentPageKey = pageStack[pageStack.length - 1] ?? '';
77
+
78
+ const conditionsRef = useRef(conditions);
79
+ conditionsRef.current = conditions;
80
+
81
+ const setCondition = useCallback(
82
+ (key: string, value: boolean) => {
83
+ setConditions((prev) => {
84
+ const next = { ...prev, [key]: value };
85
+ if (shouldPersist) {
86
+ const currentKey = pageStack[pageStack.length - 1] ?? '';
87
+ persistProgress(storageKey, currentKey, next);
88
+ }
89
+ return next;
90
+ });
91
+ },
92
+ [shouldPersist, storageKey, pageStack],
93
+ );
94
+
95
+ const navigate = useCallback(
96
+ (key: string) => {
97
+ const effective = resolveEffectivePage(key, pages, conditionsRef.current);
98
+ if (!effective) return;
99
+
100
+ setPageStack((prev) => {
101
+ const last = prev[prev.length - 1];
102
+ if (last === effective) return prev;
103
+ const next = [...prev, effective];
104
+ if (shouldPersist) {
105
+ persistProgress(storageKey, effective, conditionsRef.current);
106
+ }
107
+ return next;
108
+ });
109
+ },
110
+ [pages, shouldPersist, storageKey],
111
+ );
112
+
113
+ const goNext = useCallback(() => {
114
+ const currentIdx = pages.findIndex((p) => p.key === currentPageKey);
115
+ if (currentIdx === -1 || currentIdx >= pages.length - 1) return;
116
+ const nextPage = pages[currentIdx + 1];
117
+ if (!nextPage) return;
118
+ navigate(nextPage.key);
119
+ }, [pages, currentPageKey, navigate]);
120
+
121
+ const goBack = useCallback((): boolean => {
122
+ if (pageStack.length <= 1) return false;
123
+ setPageStack((prev) => {
124
+ const next = prev.slice(0, -1);
125
+ const prevKey = next[next.length - 1] ?? '';
126
+ if (shouldPersist) {
127
+ persistProgress(storageKey, prevKey, conditionsRef.current);
128
+ }
129
+ return next;
130
+ });
131
+ return true;
132
+ }, [pageStack.length, shouldPersist, storageKey]);
133
+
134
+ const activePage = useMemo(
135
+ () => pages.find((p) => p.key === currentPageKey),
136
+ [pages, currentPageKey],
137
+ );
138
+
139
+ const contextValue = useMemo<GlobalContextValue>(
140
+ () => ({
141
+ currentPageKey,
142
+ pages,
143
+ pageStack,
144
+ navigate,
145
+ goNext,
146
+ goBack,
147
+ conditions,
148
+ setCondition,
149
+ }),
150
+ [
151
+ currentPageKey,
152
+ pages,
153
+ pageStack,
154
+ navigate,
155
+ goNext,
156
+ goBack,
157
+ conditions,
158
+ setCondition,
159
+ ],
160
+ );
161
+
162
+ return {
163
+ node,
164
+ attributeName,
165
+ attributeKey,
166
+ attrs,
167
+ pages,
168
+ currentPageKey,
169
+ activePage,
170
+ contextValue,
171
+ };
172
+ }
@@ -13,6 +13,7 @@ import {
13
13
  type PlacementEventObject,
14
14
  } from '../SystemButton/usePlacementButtonEvents';
15
15
  import { useGlobalNavigation } from '../GlobalProvider/useGlobalNavigation';
16
+ import { useGlobalContext } from '../GlobalProvider/GlobalContext';
16
17
 
17
18
  export function OnboardButton({ node }: OnboardButtonComponentProps) {
18
19
  useLogRender('OnboardButton');
@@ -23,6 +24,7 @@ export function OnboardButton({ node }: OnboardButtonComponentProps) {
23
24
  const context = useMockOSContext();
24
25
  const mockPermissionManager = useMockPermission(context);
25
26
  const globalNavigate = useGlobalNavigation();
27
+ const globalCtx = useGlobalContext();
26
28
  const generatedId = useId();
27
29
  const attributeKey = node.key ?? generatedId;
28
30
  const attrs = node.attributes;
@@ -68,6 +70,7 @@ export function OnboardButton({ node }: OnboardButtonComponentProps) {
68
70
  mockPermissionManager.requestPermission(permission),
69
71
  onNavigateWithoutPlacement,
70
72
  globalNavigate,
73
+ setCondition: globalCtx?.setCondition,
71
74
  });
72
75
 
73
76
  return (
@@ -2,7 +2,28 @@
2
2
 
3
3
  import type { NodeData } from '../../types/Node';
4
4
 
5
- export type TypeOptionType = 'Permission' | 'Navigate' | 'Placement';
5
+ export type TypeOptionType =
6
+ | 'Permission'
7
+ | 'Navigate'
8
+ | 'Placement'
9
+ | 'SetCondition';
10
+ export type PermissionOptionType =
11
+ | 'notification'
12
+ | 'camera'
13
+ | 'microphone'
14
+ | 'location'
15
+ | 'photos'
16
+ | 'contacts'
17
+ | 'att'
18
+ | 'rating'
19
+ | 'GDPR';
20
+ export type PlacementKeyOptionType =
21
+ | 'terms'
22
+ | 'onboard'
23
+ | 'paywall'
24
+ | 'subscription'
25
+ | 'home';
26
+ export type ConditionKeyOptionType = 'termsAccepted';
6
27
  export type TestIDOptionType = 'onboardSkip' | 'onboardNext';
7
28
  export type AnimationOptionType =
8
29
  | 'simple-animation'
@@ -29,10 +50,12 @@ export type PositionOptionType = 'relative' | 'absolute';
29
50
 
30
51
  export interface EventObjectGenerated {
31
52
  type?: TypeOptionType;
32
- permission?: string;
53
+ permission?: PermissionOptionType;
33
54
  navigate_to?: string;
34
55
  targetIndex?: number;
35
- placementKey?: string;
56
+ placementKey?: PlacementKeyOptionType;
57
+ conditionKey?: ConditionKeyOptionType;
58
+ value?: boolean;
36
59
  }
37
60
 
38
61
  export interface OnboardButtonStyleGenerated {
@@ -32,11 +32,13 @@
32
32
  },
33
33
  "types": {
34
34
  "EventObject": {
35
- "type": ["Permission", "Navigate", "Placement"],
36
- "permission": "string",
35
+ "type": "$ref:event-constants.eventTypes",
36
+ "permission": "$ref:event-constants.permissionTypes",
37
37
  "navigate_to": "string",
38
38
  "targetIndex": "number",
39
- "placementKey": "string"
39
+ "placementKey": "$ref:event-constants.placementKeys",
40
+ "conditionKey": "$ref:event-constants.conditionKeys",
41
+ "value": "boolean"
40
42
  }
41
43
  },
42
44
  "meta": {
@@ -9,6 +9,7 @@ import { useMockOSContext, useMockPermission } from '../../mockOS';
9
9
  import { useLocalize } from '../../hooks/useLocalize';
10
10
  import { usePlacementButtonEvents } from './usePlacementButtonEvents';
11
11
  import { useGlobalNavigation } from '../GlobalProvider/useGlobalNavigation';
12
+ import { useGlobalContext } from '../GlobalProvider/GlobalContext';
12
13
 
13
14
  export type SystemButtonOptionalProps = {
14
15
  onClick?: () => void;
@@ -26,6 +27,7 @@ export function SystemButton({
26
27
  const context = useMockOSContext();
27
28
  const mockPermissionManager = useMockPermission(context);
28
29
  const globalNavigate = useGlobalNavigation();
30
+ const globalCtx = useGlobalContext();
29
31
  const generatedId = useId();
30
32
  const attributeKey = node.key ?? generatedId;
31
33
  const attrs = node.attributes;
@@ -40,6 +42,7 @@ export function SystemButton({
40
42
  requestPermission: (permission) =>
41
43
  mockPermissionManager.requestPermission(permission),
42
44
  globalNavigate,
45
+ setCondition: globalCtx?.setCondition,
43
46
  });
44
47
 
45
48
  const handleClick = onClickProp ?? placementClick;
@@ -2,7 +2,28 @@
2
2
 
3
3
  import type { NodeData } from '../../types/Node';
4
4
 
5
- export type TypeOptionType = 'Permission' | 'Navigate' | 'Placement';
5
+ export type TypeOptionType =
6
+ | 'Permission'
7
+ | 'Navigate'
8
+ | 'Placement'
9
+ | 'SetCondition';
10
+ export type PermissionOptionType =
11
+ | 'notification'
12
+ | 'camera'
13
+ | 'microphone'
14
+ | 'location'
15
+ | 'photos'
16
+ | 'contacts'
17
+ | 'att'
18
+ | 'rating'
19
+ | 'GDPR';
20
+ export type PlacementKeyOptionType =
21
+ | 'terms'
22
+ | 'onboard'
23
+ | 'paywall'
24
+ | 'subscription'
25
+ | 'home';
26
+ export type ConditionKeyOptionType = 'termsAccepted';
6
27
  export type FlexDirectionOptionType = 'row' | 'column';
7
28
  export type FlexWrapOptionType = 'nowrap' | 'wrap' | 'wrap-reverse';
8
29
  export type AlignItemsOptionType =
@@ -22,10 +43,12 @@ export type PositionOptionType = 'relative' | 'absolute';
22
43
 
23
44
  export interface EventObjectGenerated {
24
45
  type?: TypeOptionType;
25
- permission?: string;
46
+ permission?: PermissionOptionType;
26
47
  navigate_to?: string;
27
48
  targetIndex?: number;
28
- placementKey?: string;
49
+ placementKey?: PlacementKeyOptionType;
50
+ conditionKey?: ConditionKeyOptionType;
51
+ value?: boolean;
29
52
  }
30
53
 
31
54
  export interface SystemButtonStyleGenerated {