@bravostudioai/react 0.1.42 → 0.1.46

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.
@@ -1 +1 @@
1
- {"version":3,"file":"components.d.ts","sourceRoot":"","sources":["../../src/components.tsx"],"names":[],"mappings":"AACA,OAAO,KAMN,MAAM,OAAO,CAAC;AA8sGf,QAAA,MAAM,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,aAAa,CAAC,GAAG,CAAC,CAwDxD,CAAC;AAEF,eAAe,UAAU,CAAC"}
1
+ {"version":3,"file":"components.d.ts","sourceRoot":"","sources":["../../src/components.tsx"],"names":[],"mappings":"AACA,OAAO,KAMN,MAAM,OAAO,CAAC;AA4uGf,QAAA,MAAM,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,aAAa,CAAC,GAAG,CAAC,CAwDxD,CAAC;AAEF,eAAe,UAAU,CAAC"}
@@ -1,2 +1,2 @@
1
- export declare const PACKAGE_VERSION = "0.1.42";
1
+ export declare const PACKAGE_VERSION = "0.1.46";
2
2
  //# sourceMappingURL=version.d.ts.map
package/dist/version.js CHANGED
@@ -1,4 +1,4 @@
1
- const o = "0.1.42";
1
+ const o = "0.1.46";
2
2
  export {
3
3
  o as PACKAGE_VERSION
4
4
  };
@@ -1 +1 @@
1
- {"version":3,"file":"version.js","sources":["../src/version.ts"],"sourcesContent":["export const PACKAGE_VERSION = \"0.1.42\";\n"],"names":["PACKAGE_VERSION"],"mappings":"AAAO,MAAMA,IAAkB;"}
1
+ {"version":3,"file":"version.js","sources":["../src/version.ts"],"sourcesContent":["export const PACKAGE_VERSION = \"0.1.46\";\n"],"names":["PACKAGE_VERSION"],"mappings":"AAAO,MAAMA,IAAkB;"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bravostudioai/react",
3
- "version": "0.1.42",
3
+ "version": "0.1.46",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/src/index.d.ts",
@@ -40,7 +40,7 @@ interface ComponentProps {
40
40
 
41
41
  const EncorePageContext = React.createContext<EncorePageContextType>({});
42
42
  const EncoreContainerContext = React.createContext<EncoreContainerContextType>(
43
- {}
43
+ {},
44
44
  );
45
45
 
46
46
  const argbAlphaToOpacity = (color: string): number => {
@@ -58,7 +58,7 @@ const argbIsFullyTransparent = (color: string): boolean => {
58
58
 
59
59
  // Parse input-group tag: "input-group:single:product_purpose" -> { type: "single", groupName: "product_purpose" }
60
60
  const parseInputGroupTag = (
61
- tags: string[] | undefined
61
+ tags: string[] | undefined,
62
62
  ): { type: string; groupName: string } | null => {
63
63
  if (!Array.isArray(tags)) return null;
64
64
  const inputGroupTag = tags.find((tag) => tag.startsWith("input-group:"));
@@ -111,7 +111,7 @@ const useContainerDimensions = (): {
111
111
 
112
112
  const useEncoreStyle = (
113
113
  style: any,
114
- opts: { debug?: boolean } = {}
114
+ opts: { debug?: boolean } = {},
115
115
  ): React.CSSProperties | undefined => {
116
116
  let shouldCenterHorizontally = false; // Track if element should be centered
117
117
  const pageContext = useContext(EncorePageContext);
@@ -125,7 +125,7 @@ const useEncoreStyle = (
125
125
  (v: number) => {
126
126
  return (containerContext.dimensions?.width ?? 0) * 0.01 * v;
127
127
  },
128
- [containerContext.dimensions?.width]
128
+ [containerContext.dimensions?.width],
129
129
  );
130
130
 
131
131
  const result: React.CSSProperties = {};
@@ -145,6 +145,14 @@ const useEncoreStyle = (
145
145
  const containerHeight =
146
146
  containerContext.dimensions?.height ?? viewport.height;
147
147
 
148
+ // DEBUG: Trace GRID mode
149
+ if (style.layout?.mode === "GRID") {
150
+ console.log("[DEBUG] GRID MODE DETECTED", {
151
+ id: "unknown", // style object doesn't have ID context here easily without prop drill
152
+ layout: style.layout,
153
+ });
154
+ }
155
+
148
156
  // Check if this node has layout sizing directives (even if mode is null)
149
157
  const hasLayoutSizing =
150
158
  style.layout?.layoutSizingHorizontal || style.layout?.layoutSizingVertical;
@@ -271,7 +279,7 @@ const useEncoreStyle = (
271
279
 
272
280
  // Map Figma primaryAxisAlignItems values to CSS justify-content values
273
281
  const mapJustifyContent = (
274
- figmaValue: string | undefined
282
+ figmaValue: string | undefined,
275
283
  ): string | undefined => {
276
284
  if (!figmaValue) return undefined;
277
285
  const mapping: Record<string, string> = {
@@ -285,19 +293,20 @@ const useEncoreStyle = (
285
293
  return mapping[figmaValue] || figmaValue.toLowerCase().replace(/_/g, "-");
286
294
  };
287
295
 
288
- if (style.layout.mode == "HORIZONTAL") {
296
+ if (style.layout.mode == "HORIZONTAL" || style.layout.mode == "GRID") {
289
297
  result.flexDirection = "row";
298
+
290
299
  if (style.layout.flexWrap) {
291
300
  result.flexWrap = style.layout.flexWrap;
292
301
  }
293
302
  result.justifyContent = mapJustifyContent(
294
- style.layout.primaryAxisAlignItems
303
+ style.layout.primaryAxisAlignItems,
295
304
  );
296
305
  result.alignItems = style.layout.counterAxisAlignItems;
297
306
  } else {
298
307
  result.flexDirection = "column";
299
308
  result.justifyContent = mapJustifyContent(
300
- style.layout.primaryAxisAlignItems
309
+ style.layout.primaryAxisAlignItems,
301
310
  );
302
311
  result.alignItems = style.layout.counterAxisAlignItems;
303
312
  }
@@ -306,7 +315,12 @@ const useEncoreStyle = (
306
315
  // Set flexWrap: if wrap is explicitly "NO_WRAP", use "nowrap", otherwise default to "wrap" for layouts that need it
307
316
  // Set flexWrap: default to "nowrap" to match Figma's default behavior
308
317
  // Only wrap if explicitly requested
309
- if (style.layout.wrap === "WRAP" || style.layout.flexWrap === "wrap") {
318
+ // Only wrap if explicitly requested or if mode is GRID
319
+ if (
320
+ style.layout.wrap === "WRAP" ||
321
+ style.layout.flexWrap === "wrap" ||
322
+ style.layout.mode === "GRID"
323
+ ) {
310
324
  result.flexWrap = "wrap";
311
325
  } else if (style.layout.flexWrap) {
312
326
  result.flexWrap = style.layout.flexWrap;
@@ -358,7 +372,7 @@ const useEncoreStyle = (
358
372
  "[useEncoreStyle] No gap properties set",
359
373
  opts.debug,
360
374
  style.layout?.mode,
361
- itemSpacing
375
+ itemSpacing,
362
376
  );
363
377
  }
364
378
 
@@ -368,7 +382,12 @@ const useEncoreStyle = (
368
382
  }
369
383
 
370
384
  if (style.layout?.layoutSizingHorizontal === "FILL") {
371
- result.width = "100%";
385
+ // Only set width 100% if NOT in a flex container.
386
+ // In a flex container, 'FILL' should be handled by flex-grow (Row) or align-self (Col).
387
+ // Setting width 100% explicitly forces it to take full width which breaks horizontal layouts.
388
+ if (!parentIsFlex) {
389
+ result.width = "100%";
390
+ }
372
391
  }
373
392
 
374
393
  if (style.layout?.layoutSizingHorizontal === "FIXED") {
@@ -757,7 +776,7 @@ const EncoreLinkActionWrapper: React.FC<{
757
776
  const pageId = useEncoreState((state) => state.pageId);
758
777
  const { onAction } = React.useContext(EncoreActionContext);
759
778
  const { statefulSetId: parentStatefulSetIdFromContext } = React.useContext(
760
- EncoreComponentIdContext
779
+ EncoreComponentIdContext,
761
780
  );
762
781
  // Prefer prop over context (more reliable)
763
782
  const parentStatefulSetId =
@@ -766,10 +785,10 @@ const EncoreLinkActionWrapper: React.FC<{
766
785
  // const [searchParams] = useSearchParams();
767
786
  const { patchedNodeData } = useEncoreBinding({ id, nodeData });
768
787
  const setStatefulSetVariant = useEncoreState(
769
- (state) => state.setStatefulSetVariant
788
+ (state) => state.setStatefulSetVariant,
770
789
  );
771
790
  const setInputGroupValue = useEncoreState(
772
- (state) => state.setInputGroupValue
791
+ (state) => state.setInputGroupValue,
773
792
  );
774
793
  const setAccessToken = useEncoreState((state) => state.setAccessToken);
775
794
  const { app } = useEncoreState((state) => state.app);
@@ -777,30 +796,6 @@ const EncoreLinkActionWrapper: React.FC<{
777
796
  const startPageId = app.data?.app?.startPageId;
778
797
  const loginPageId = app.data?.app?.PageId;
779
798
 
780
- const style = useEncoreStyle(
781
- {
782
- positioning: nodeData.style?.positioning,
783
- },
784
- { debug: false }
785
- );
786
-
787
- // Make the wrapper div cover the entire button area
788
- // Use a very high z-index to ensure it's above all children
789
- // IMPORTANT: The overlay should be scoped to the immediate parent (the button),
790
- // not the entire StatefulSetComponent, to avoid overlapping with other buttons
791
- style.zIndex = 99999;
792
- style.backgroundColor = "transparent";
793
- style.position = "absolute";
794
- style.top = "0";
795
- style.left = "0";
796
- style.right = "0";
797
- style.bottom = "0";
798
- style.width = "100%";
799
- style.height = "100%";
800
- style.pointerEvents = "auto";
801
- // Ensure the overlay is contained within its parent button element
802
- style.isolation = "isolate";
803
-
804
799
  const handleAction = async (action: any) => {
805
800
  let proceed = false;
806
801
  const cancel = () => {
@@ -949,27 +944,23 @@ const EncoreLinkActionWrapper: React.FC<{
949
944
 
950
945
  if (nodeData.href) {
951
946
  return (
952
- <>
953
- <button
954
- type="button"
955
- onClick={() =>
956
- handleAction({
957
- action: "goto",
958
- params: { href: nodeData.href },
959
- event: "tap",
960
- })
961
- }
962
- style={{
963
- ...style,
964
- cursor: "pointer",
965
- background: "transparent",
966
- border: "none",
967
- padding: 0,
968
- }}
969
- aria-label={`Go to ${nodeData.href}`}
970
- ></button>
947
+ <div
948
+ onClick={() =>
949
+ handleAction({
950
+ action: "goto",
951
+ params: { href: nodeData.href },
952
+ event: "tap",
953
+ })
954
+ }
955
+ style={{
956
+ display: "contents",
957
+ cursor: "pointer",
958
+ }}
959
+ role="button"
960
+ aria-label={`Go to ${nodeData.href}`}
961
+ >
971
962
  {children}
972
- </>
963
+ </div>
973
964
  );
974
965
  }
975
966
 
@@ -1030,7 +1021,7 @@ const EncoreLinkActionWrapper: React.FC<{
1030
1021
  if (!targetStateSetId) {
1031
1022
  console.warn(
1032
1023
  "⚠️ No stateSetId available for stateful compound component",
1033
- { id, name: nodeData.name }
1024
+ { id, name: nodeData.name },
1034
1025
  );
1035
1026
  } else {
1036
1027
  const currentVariant =
@@ -1055,24 +1046,45 @@ const EncoreLinkActionWrapper: React.FC<{
1055
1046
 
1056
1047
  if (actions && actions.length > 0) {
1057
1048
  const action = actions[0];
1058
- if (action.event !== "tap") {
1059
- return children;
1060
- }
1049
+ if (action.event === "tap") {
1050
+ const clickHandler = (e: React.MouseEvent) => {
1051
+ // e.stopPropagation(); // Allow bubbling if needed, but usually we want to stop here to prevent parent actions?
1052
+ // Actually, for a button, we usually stop propagation.
1053
+ e.stopPropagation();
1054
+ handleAction(action);
1055
+ };
1061
1056
 
1062
- return (
1063
- <>
1064
- {children}
1065
- <div
1066
- onClick={(e) => {
1067
- e.stopPropagation();
1068
- handleAction(action);
1069
- }}
1070
- onMouseDown={(e) => e.stopPropagation()}
1071
- onMouseUp={(e) => e.stopPropagation()}
1072
- style={{ ...style, cursor: "pointer", pointerEvents: "auto" }}
1073
- ></div>
1074
- </>
1075
- );
1057
+ const wrapperStyle: React.CSSProperties = {
1058
+ display: "contents",
1059
+ cursor: "pointer",
1060
+ // Ensure the wrapper doesn't block pointer events, but its children should capture them.
1061
+ // pointerEvents: 'auto' is default.
1062
+ };
1063
+
1064
+ if (action.action === "openurl" && action.params?.url) {
1065
+ return (
1066
+ <a
1067
+ href={action.params.url}
1068
+ target="_blank"
1069
+ rel="noopener noreferrer"
1070
+ onClick={(e) => {
1071
+ e.stopPropagation();
1072
+ // handleAction(action); // Optional side effect
1073
+ }}
1074
+ style={wrapperStyle}
1075
+ aria-label={nodeData.name || "Open Link"}
1076
+ >
1077
+ {children}
1078
+ </a>
1079
+ );
1080
+ }
1081
+
1082
+ return (
1083
+ <div onClick={clickHandler} style={wrapperStyle}>
1084
+ {children}
1085
+ </div>
1086
+ );
1087
+ }
1076
1088
  }
1077
1089
 
1078
1090
  return children;
@@ -1703,11 +1715,11 @@ const TextComponent: React.FC<ComponentProps> = ({ id, name, nodeData }) => {
1703
1715
  textContent && (textContent.includes("\n") || textContent.includes("\r"));
1704
1716
  let renderedContent;
1705
1717
  if (hasLineBreaks && style.flexDirection === "column") {
1706
- // Split by newlines and carriage returns, filter out empty strings from \r\n combinations
1707
- const lines = textContent.split(/\r?\n/).filter((line) => line.length > 0);
1718
+ // Split by newlines and carriage returns, preserve empty lines for "double spacing"
1719
+ const lines = textContent.split(/\r?\n/);
1708
1720
  renderedContent = lines.map((line, index) => (
1709
1721
  <div key={index} style={{ lineHeight: "inherit" }}>
1710
- {line}
1722
+ {line || "\u00A0"}
1711
1723
  </div>
1712
1724
  ));
1713
1725
  } else {
@@ -1915,7 +1927,7 @@ const ContainerComponent = ({
1915
1927
  if (id === "01KB929X1KK5TX0K8VBNP6ZDPG") {
1916
1928
  console.log(
1917
1929
  `[DEBUG] ContainerComponent ${id} (Hero) FULL nodeData:`,
1918
- JSON.stringify(nodeData, null, 2)
1930
+ JSON.stringify(nodeData, null, 2),
1919
1931
  );
1920
1932
  }
1921
1933
  const bindingContext = useContext(EncoreBindingContext);
@@ -1932,10 +1944,26 @@ const ContainerComponent = ({
1932
1944
  debug: false,
1933
1945
  });
1934
1946
 
1947
+ if (id === "01KF5PAFET1VY5E81B68GQ9BFQ") {
1948
+ console.log("[DEBUG] Embed Container Render", {
1949
+ id,
1950
+ style,
1951
+ computedStyle: style,
1952
+ layout: nodeData.style.layout,
1953
+ hasPositioning: !!nodeData.style.positioning,
1954
+ });
1955
+ // Force a simpler layout for debugging
1956
+ // style.position = 'relative';
1957
+ // style.zIndex = 1;
1958
+ }
1959
+
1935
1960
  const isFlex = !!nodeData.style.layout?.mode;
1936
1961
  // Determine flex direction for passing to children via context
1937
1962
  const flexDirection =
1938
- nodeData.style.layout?.mode === "HORIZONTAL" ? "row" : "column";
1963
+ nodeData.style.layout?.mode === "HORIZONTAL" ||
1964
+ nodeData.style.layout?.mode === "GRID"
1965
+ ? "row"
1966
+ : "column";
1939
1967
  // Only set default 100% if we don't have explicit dimensions from layout.size
1940
1968
  // Explicit dimensions are set by useEncoreStyle when layoutSizing is FIXED
1941
1969
  const hasExplicitWidth = style.width !== undefined;
@@ -2016,8 +2044,8 @@ const ContainerComponent = ({
2016
2044
  const finalStyle = shouldBeAbsolute
2017
2045
  ? { ...style, position: "absolute" }
2018
2046
  : style.position === "absolute"
2019
- ? style
2020
- : { ...style, position: style.position || "relative" };
2047
+ ? style
2048
+ : { ...style, position: style.position || "relative" };
2021
2049
 
2022
2050
  // For containers with explicit dimensions from layout.size, use the scaled dimensions
2023
2051
  // from style (which are calculated from layout.size * scaleFactor) for child context.
@@ -2152,7 +2180,7 @@ const ContainerComponent = ({
2152
2180
  ...(typeof actionData === "object" && actionData !== null
2153
2181
  ? actionData
2154
2182
  : {}),
2155
- })
2183
+ }),
2156
2184
  );
2157
2185
  }
2158
2186
  const hasActions = Array.isArray(actions) && actions.length > 0;
@@ -2262,7 +2290,7 @@ const SliderComponent = ({ id, name, nodeData, children }) => {
2262
2290
  if (id === "01KB929X1DQJN954WNGF5AH38B") {
2263
2291
  console.log(
2264
2292
  `[DEBUG] SliderComponent ${id} FULL nodeData:`,
2265
- JSON.stringify(nodeData, null, 2)
2293
+ JSON.stringify(nodeData, null, 2),
2266
2294
  );
2267
2295
  }
2268
2296
  // Log slider component data for inspection
@@ -2334,8 +2362,8 @@ const SliderComponent = ({ id, name, nodeData, children }) => {
2334
2362
  const animation = shouldBeVertical
2335
2363
  ? "vertical"
2336
2364
  : params.animation === "default"
2337
- ? "horizontal"
2338
- : params.animation || "horizontal";
2365
+ ? "horizontal"
2366
+ : params.animation || "horizontal";
2339
2367
 
2340
2368
  const style = useEncoreStyle(nodeData.style, {
2341
2369
  debug: false,
@@ -2391,7 +2419,7 @@ const SliderComponent = ({ id, name, nodeData, children }) => {
2391
2419
  // ContainerComponent will use this to know which item to render
2392
2420
  _arrayItemIndex: i,
2393
2421
  _arrayItemData: item,
2394
- })
2422
+ }),
2395
2423
  );
2396
2424
  });
2397
2425
  } else {
@@ -2545,7 +2573,7 @@ const SliderComponent = ({ id, name, nodeData, children }) => {
2545
2573
  });
2546
2574
  }
2547
2575
  },
2548
- [slideWidth, slideHeight, infinite, childList.length, isVertical]
2576
+ [slideWidth, slideHeight, infinite, childList.length, isVertical],
2549
2577
  );
2550
2578
 
2551
2579
  // Handle infinite scroll position adjustment
@@ -2741,7 +2769,7 @@ const SliderComponent = ({ id, name, nodeData, children }) => {
2741
2769
  }
2742
2770
  }
2743
2771
  },
2744
- [onAction, id] // Depend on onAction to ensure we call the latest version
2772
+ [onAction, id], // Depend on onAction to ensure we call the latest version
2745
2773
  );
2746
2774
 
2747
2775
  // Set up scroll event listeners
@@ -2825,7 +2853,7 @@ const SliderComponent = ({ id, name, nodeData, children }) => {
2825
2853
  ) {
2826
2854
  const targetIndex = Math.max(
2827
2855
  0,
2828
- Math.min(controlProps.currentIndex, childList.length - 1)
2856
+ Math.min(controlProps.currentIndex, childList.length - 1),
2829
2857
  );
2830
2858
  const currentIndex = calculateCurrentIndex();
2831
2859
  if (targetIndex !== currentIndex) {
@@ -3084,7 +3112,7 @@ const PageComponent = ({ id, name, nodeData, children }) => {
3084
3112
  {
3085
3113
  scrollableChildren: [],
3086
3114
  nonScrollableChildren: [],
3087
- }
3115
+ },
3088
3116
  );
3089
3117
 
3090
3118
  // Determine the layout direction from nodeData
@@ -3125,7 +3153,7 @@ const PageComponent = ({ id, name, nodeData, children }) => {
3125
3153
 
3126
3154
  if (shouldPatch) {
3127
3155
  console.log(
3128
- `[PATCH] Detected HORIZONTAL WRAP layout for page ${id} (Partial width children: ${partialWidthChildren}, Total width: ${totalWidth})`
3156
+ `[PATCH] Detected HORIZONTAL WRAP layout for page ${id} (Partial width children: ${partialWidthChildren}, Total width: ${totalWidth})`,
3129
3157
  );
3130
3158
  pageLayoutMode = "HORIZONTAL";
3131
3159
 
@@ -3145,7 +3173,7 @@ const PageComponent = ({ id, name, nodeData, children }) => {
3145
3173
  // Check if we have nonScrollableChildren that actually need flex positioning
3146
3174
  // BackgroundContainerComponent is absolutely positioned, so it doesn't need flex
3147
3175
  const needsFlexContainer = nonScrollableChildren.some(
3148
- (child) => child?.props?.nodeData?.type !== "container:background"
3176
+ (child) => child?.props?.nodeData?.type !== "container:background",
3149
3177
  );
3150
3178
 
3151
3179
  return (
@@ -3177,7 +3205,7 @@ const PageComponent = ({ id, name, nodeData, children }) => {
3177
3205
  {/* Render backgrounds directly - they're absolutely positioned */}
3178
3206
  {nonScrollableChildren
3179
3207
  .filter(
3180
- (child) => child?.props?.nodeData?.type === "container:background"
3208
+ (child) => child?.props?.nodeData?.type === "container:background",
3181
3209
  )
3182
3210
  .map((child, index) => (
3183
3211
  <EncoreContainerContext.Provider
@@ -3197,7 +3225,8 @@ const PageComponent = ({ id, name, nodeData, children }) => {
3197
3225
  {needsFlexContainer &&
3198
3226
  nonScrollableChildren
3199
3227
  .filter(
3200
- (child) => child?.props?.nodeData?.type !== "container:background"
3228
+ (child) =>
3229
+ child?.props?.nodeData?.type !== "container:background",
3201
3230
  )
3202
3231
  .map((child, index) => (
3203
3232
  <EncoreContainerContext.Provider
@@ -3266,6 +3295,7 @@ const EmbedComponent: React.FC<ComponentProps> = ({
3266
3295
  flexDirection: "column",
3267
3296
  // Reset positioning if provided by wrapper
3268
3297
  position: "absolute",
3298
+ zIndex: 10,
3269
3299
  }}
3270
3300
  data-type="EmbedComponent"
3271
3301
  data-id={id}
package/src/version.ts CHANGED
@@ -1 +1 @@
1
- export const PACKAGE_VERSION = "0.1.42";
1
+ export const PACKAGE_VERSION = "0.1.46";