@oxyhq/bloom 0.22.0 → 0.23.0

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 (25) hide show
  1. package/lib/commonjs/content-panel/index.js +12 -6
  2. package/lib/commonjs/content-panel/index.js.map +1 -1
  3. package/lib/commonjs/content-panel/index.web.js +31 -21
  4. package/lib/commonjs/content-panel/index.web.js.map +1 -1
  5. package/lib/commonjs/content-panel/nesting-context.js +39 -0
  6. package/lib/commonjs/content-panel/nesting-context.js.map +1 -0
  7. package/lib/module/content-panel/index.js +12 -6
  8. package/lib/module/content-panel/index.js.map +1 -1
  9. package/lib/module/content-panel/index.web.js +31 -21
  10. package/lib/module/content-panel/index.web.js.map +1 -1
  11. package/lib/module/content-panel/nesting-context.js +35 -0
  12. package/lib/module/content-panel/nesting-context.js.map +1 -0
  13. package/lib/typescript/commonjs/content-panel/index.d.ts.map +1 -1
  14. package/lib/typescript/commonjs/content-panel/index.web.d.ts.map +1 -1
  15. package/lib/typescript/commonjs/content-panel/nesting-context.d.ts +24 -0
  16. package/lib/typescript/commonjs/content-panel/nesting-context.d.ts.map +1 -0
  17. package/lib/typescript/module/content-panel/index.d.ts.map +1 -1
  18. package/lib/typescript/module/content-panel/index.web.d.ts.map +1 -1
  19. package/lib/typescript/module/content-panel/nesting-context.d.ts +24 -0
  20. package/lib/typescript/module/content-panel/nesting-context.d.ts.map +1 -0
  21. package/package.json +1 -1
  22. package/src/__tests__/ContentPanelNesting.test.tsx +52 -0
  23. package/src/content-panel/index.tsx +18 -8
  24. package/src/content-panel/index.web.tsx +40 -29
  25. package/src/content-panel/nesting-context.ts +35 -0
@@ -6,6 +6,7 @@ Object.defineProperty(exports, "__esModule", {
6
6
  exports.PANEL_TOP_INSET = exports.PANEL_BOTTOM_INSET = exports.GUTTER_MASK_SPREAD = exports.ContentPanel = void 0;
7
7
  var _react = _interopRequireWildcard(require("react"));
8
8
  var _reactNative = require("react-native");
9
+ var _nestingContext = require("./nesting-context.js");
9
10
  var _jsxRuntime = require("react/jsx-runtime");
10
11
  function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f; })(e, t); }
11
12
  /**
@@ -47,15 +48,20 @@ const ContentPanelComponent = ({
47
48
  contentClassName,
48
49
  contentStyle
49
50
  }) => {
51
+ // Dev-only invariant — a ContentPanel must never be nested inside another.
52
+ (0, _nestingContext.useContentPanelNestingGuard)();
50
53
  const surfaceClass = ['flex-1', 'rounded-radius-28', 'overflow-hidden border border-border', surfaceClassName ?? 'bg-card'].join(' ');
51
54
  const contentClass = ['flex-1', contentClassName].filter(Boolean).join(' ');
52
- return /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
53
- className: surfaceClass,
54
- style: surfaceStyle,
55
+ return /*#__PURE__*/(0, _jsxRuntime.jsx)(_nestingContext.ContentPanelNestingContext.Provider, {
56
+ value: true,
55
57
  children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
56
- className: contentClass,
57
- style: contentStyle,
58
- children: children
58
+ className: surfaceClass,
59
+ style: surfaceStyle,
60
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
61
+ className: contentClass,
62
+ style: contentStyle,
63
+ children: children
64
+ })
59
65
  })
60
66
  });
61
67
  };
@@ -1 +1 @@
1
- {"version":3,"names":["_react","_interopRequireWildcard","require","_reactNative","_jsxRuntime","e","t","WeakMap","r","n","__esModule","o","i","f","__proto__","default","has","get","set","hasOwnProperty","call","Object","defineProperty","getOwnPropertyDescriptor","GUTTER_MASK_SPREAD","exports","PANEL_TOP_INSET","PANEL_BOTTOM_INSET","ContentPanelComponent","children","surfaceClassName","surfaceStyle","contentClassName","contentStyle","surfaceClass","join","contentClass","filter","Boolean","jsx","View","className","style","ContentPanel","memo","displayName"],"sourceRoot":"../../../src","sources":["content-panel/index.tsx"],"mappings":";;;;;;AAsBA,IAAAA,MAAA,GAAAC,uBAAA,CAAAC,OAAA;AACA,IAAAC,YAAA,GAAAD,OAAA;AAAoE,IAAAE,WAAA,GAAAF,OAAA;AAAA,SAAAD,wBAAAI,CAAA,EAAAC,CAAA,6BAAAC,OAAA,MAAAC,CAAA,OAAAD,OAAA,IAAAE,CAAA,OAAAF,OAAA,YAAAN,uBAAA,YAAAA,CAAAI,CAAA,EAAAC,CAAA,SAAAA,CAAA,IAAAD,CAAA,IAAAA,CAAA,CAAAK,UAAA,SAAAL,CAAA,MAAAM,CAAA,EAAAC,CAAA,EAAAC,CAAA,KAAAC,SAAA,QAAAC,OAAA,EAAAV,CAAA,iBAAAA,CAAA,uBAAAA,CAAA,yBAAAA,CAAA,SAAAQ,CAAA,MAAAF,CAAA,GAAAL,CAAA,GAAAG,CAAA,GAAAD,CAAA,QAAAG,CAAA,CAAAK,GAAA,CAAAX,CAAA,UAAAM,CAAA,CAAAM,GAAA,CAAAZ,CAAA,GAAAM,CAAA,CAAAO,GAAA,CAAAb,CAAA,EAAAQ,CAAA,gBAAAP,CAAA,IAAAD,CAAA,gBAAAC,CAAA,OAAAa,cAAA,CAAAC,IAAA,CAAAf,CAAA,EAAAC,CAAA,OAAAM,CAAA,IAAAD,CAAA,GAAAU,MAAA,CAAAC,cAAA,KAAAD,MAAA,CAAAE,wBAAA,CAAAlB,CAAA,EAAAC,CAAA,OAAAM,CAAA,CAAAK,GAAA,IAAAL,CAAA,CAAAM,GAAA,IAAAP,CAAA,CAAAE,CAAA,EAAAP,CAAA,EAAAM,CAAA,IAAAC,CAAA,CAAAP,CAAA,IAAAD,CAAA,CAAAC,CAAA,WAAAO,CAAA,KAAAR,CAAA,EAAAC,CAAA;AAvBpE;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAIA;AACA;AACA;AACA;AACO,MAAMkB,kBAAkB,GAAAC,OAAA,CAAAD,kBAAA,GAAG,EAAE;AACpC;AACO,MAAME,eAAe,GAAAD,OAAA,CAAAC,eAAA,GAAG,CAAC;AAChC;AACO,MAAMC,kBAAkB,GAAAF,OAAA,CAAAE,kBAAA,GAAG,CAAC;AA2BnC,MAAMC,qBAAkD,GAAGA,CAAC;EAC1DC,QAAQ;EACRC,gBAAgB;EAChBC,YAAY;EACZC,gBAAgB;EAChBC;AACF,CAAC,KAAK;EACJ,MAAMC,YAAY,GAAG,CACnB,QAAQ,EACR,mBAAmB,EACnB,sCAAsC,EACtCJ,gBAAgB,IAAI,SAAS,CAC9B,CAACK,IAAI,CAAC,GAAG,CAAC;EACX,MAAMC,YAAY,GAAG,CAAC,QAAQ,EAAEJ,gBAAgB,CAAC,CAACK,MAAM,CAACC,OAAO,CAAC,CAACH,IAAI,CAAC,GAAG,CAAC;EAE3E,oBACE,IAAA/B,WAAA,CAAAmC,GAAA,EAACpC,YAAA,CAAAqC,IAAI;IACIC,SAAS,EAAEP,YAAY;IAC9BQ,KAAK,EAAEX,YAAa;IAAAF,QAAA,eAEpB,IAAAzB,WAAA,CAAAmC,GAAA,EAACpC,YAAA,CAAAqC,IAAI;MACIC,SAAS,EAAEL,YAAY;MAC9BM,KAAK,EAAET,YAAa;MAAAJ,QAAA,EAEnBA;IAAQ,CACL;EAAC,CACH,CAAC;AAEX,CAAC;AAEM,MAAMc,YAAY,GAAAlB,OAAA,CAAAkB,YAAA,gBAAG,IAAAC,WAAI,EAAChB,qBAAqB,CAAC;AACvDe,YAAY,CAACE,WAAW,GAAG,cAAc","ignoreList":[]}
1
+ {"version":3,"names":["_react","_interopRequireWildcard","require","_reactNative","_nestingContext","_jsxRuntime","e","t","WeakMap","r","n","__esModule","o","i","f","__proto__","default","has","get","set","hasOwnProperty","call","Object","defineProperty","getOwnPropertyDescriptor","GUTTER_MASK_SPREAD","exports","PANEL_TOP_INSET","PANEL_BOTTOM_INSET","ContentPanelComponent","children","surfaceClassName","surfaceStyle","contentClassName","contentStyle","useContentPanelNestingGuard","surfaceClass","join","contentClass","filter","Boolean","jsx","ContentPanelNestingContext","Provider","value","View","className","style","ContentPanel","memo","displayName"],"sourceRoot":"../../../src","sources":["content-panel/index.tsx"],"mappings":"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","ignoreList":[]}
@@ -7,6 +7,7 @@ exports.PANEL_TOP_INSET = exports.PANEL_BOTTOM_INSET = exports.GUTTER_MASK_SPREA
7
7
  var _react = _interopRequireWildcard(require("react"));
8
8
  var _reactNative = require("react-native");
9
9
  var _useTheme = require("../theme/use-theme.js");
10
+ var _nestingContext = require("./nesting-context.js");
10
11
  var _jsxRuntime = require("react/jsx-runtime");
11
12
  function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f; })(e, t); }
12
13
  /**
@@ -62,37 +63,46 @@ const ContentPanelComponent = ({
62
63
  showStickyFrame,
63
64
  maskColor
64
65
  }) => {
66
+ // Dev-only invariant — must run unconditionally before the early `!framed`
67
+ // return so the hook order stays stable across renders (rules of hooks).
68
+ (0, _nestingContext.useContentPanelNestingGuard)();
65
69
  const {
66
70
  colors
67
71
  } = (0, _useTheme.useTheme)();
68
72
  if (!framed) {
69
73
  // Full-bleed: a single flat surface. No rounding, no sticky overlays.
70
74
  const fullBleedClass = ['flex-1', surfaceClassName ?? 'bg-card', contentClassName].filter(Boolean).join(' ');
71
- return /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
72
- className: fullBleedClass,
73
- style: [surfaceStyle, contentStyle],
74
- children: children
75
+ return /*#__PURE__*/(0, _jsxRuntime.jsx)(_nestingContext.ContentPanelNestingContext.Provider, {
76
+ value: true,
77
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
78
+ className: fullBleedClass,
79
+ style: [surfaceStyle, contentStyle],
80
+ children: children
81
+ })
75
82
  });
76
83
  }
77
84
  const surfaceClass = ['flex-1', 'rounded-radius-28', surfaceClassName ?? 'bg-card'].join(' ');
78
85
  const contentClass = ['flex-1', 'rounded-radius-28 web:overflow-x-clip', contentClassName].filter(Boolean).join(' ');
79
- return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
80
- className: surfaceClass,
81
- style: surfaceStyle,
82
- children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
83
- pointerEvents: "none",
84
- className: 'web:sticky web:top-2 z-30 h-[calc(100dvh-16px)] w-full rounded-radius-28 web:[margin-bottom:calc(-100dvh+16px)] web:[clip-path:inset(-12px)]',
85
- style: {
86
- boxShadow: `0 0 0 ${GUTTER_MASK_SPREAD}px ${maskColor ?? colors.background}`
87
- }
88
- }), showStickyFrame !== false && /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
89
- pointerEvents: "none",
90
- className: 'web:sticky web:top-2 z-[120] h-[calc(100dvh-16px)] w-full rounded-radius-28 border border-border web:[margin-bottom:calc(-100dvh+16px)]'
91
- }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
92
- className: contentClass,
93
- style: contentStyle,
94
- children: children
95
- })]
86
+ return /*#__PURE__*/(0, _jsxRuntime.jsx)(_nestingContext.ContentPanelNestingContext.Provider, {
87
+ value: true,
88
+ children: /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
89
+ className: surfaceClass,
90
+ style: surfaceStyle,
91
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
92
+ pointerEvents: "none",
93
+ className: 'web:sticky web:top-2 z-30 h-[calc(100dvh-16px)] w-full rounded-radius-28 web:[margin-bottom:calc(-100dvh+16px)] web:[clip-path:inset(-12px)]',
94
+ style: {
95
+ boxShadow: `0 0 0 ${GUTTER_MASK_SPREAD}px ${maskColor ?? colors.background}`
96
+ }
97
+ }), showStickyFrame !== false && /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
98
+ pointerEvents: "none",
99
+ className: 'web:sticky web:top-2 z-[120] h-[calc(100dvh-16px)] w-full rounded-radius-28 border border-border web:[margin-bottom:calc(-100dvh+16px)]'
100
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
101
+ className: contentClass,
102
+ style: contentStyle,
103
+ children: children
104
+ })]
105
+ })
96
106
  });
97
107
  };
98
108
  const ContentPanel = exports.ContentPanel = /*#__PURE__*/(0, _react.memo)(ContentPanelComponent);
@@ -1 +1 @@
1
- {"version":3,"names":["_react","_interopRequireWildcard","require","_reactNative","_useTheme","_jsxRuntime","e","t","WeakMap","r","n","__esModule","o","i","f","__proto__","default","has","get","set","hasOwnProperty","call","Object","defineProperty","getOwnPropertyDescriptor","GUTTER_MASK_SPREAD","exports","PANEL_TOP_INSET","PANEL_BOTTOM_INSET","ContentPanelComponent","children","framed","surfaceClassName","surfaceStyle","contentClassName","contentStyle","showStickyFrame","maskColor","colors","useTheme","fullBleedClass","filter","Boolean","join","jsx","View","className","style","surfaceClass","contentClass","jsxs","pointerEvents","boxShadow","background","ContentPanel","memo","displayName"],"sourceRoot":"../../../src","sources":["content-panel/index.web.tsx"],"mappings":"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","ignoreList":[]}
1
+ {"version":3,"names":["_react","_interopRequireWildcard","require","_reactNative","_useTheme","_nestingContext","_jsxRuntime","e","t","WeakMap","r","n","__esModule","o","i","f","__proto__","default","has","get","set","hasOwnProperty","call","Object","defineProperty","getOwnPropertyDescriptor","GUTTER_MASK_SPREAD","exports","PANEL_TOP_INSET","PANEL_BOTTOM_INSET","ContentPanelComponent","children","framed","surfaceClassName","surfaceStyle","contentClassName","contentStyle","showStickyFrame","maskColor","useContentPanelNestingGuard","colors","useTheme","fullBleedClass","filter","Boolean","join","jsx","ContentPanelNestingContext","Provider","value","View","className","style","surfaceClass","contentClass","jsxs","pointerEvents","boxShadow","background","ContentPanel","memo","displayName"],"sourceRoot":"../../../src","sources":["content-panel/index.web.tsx"],"mappings":"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","ignoreList":[]}
@@ -0,0 +1,39 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.ContentPanelNestingContext = void 0;
7
+ exports.useContentPanelNestingGuard = useContentPanelNestingGuard;
8
+ var _react = require("react");
9
+ /**
10
+ * Tracks whether the current subtree is already inside a `ContentPanel`.
11
+ *
12
+ * Every `ContentPanel` variant (web + native) wraps its rendered tree in
13
+ * `<ContentPanelNestingContext.Provider value={true}>`, so any DESCENDANT
14
+ * `ContentPanel` reads `true` here and can reject the invalid nesting. Context
15
+ * only flows downward, so side-by-side sibling panels each read the default
16
+ * `false` at their own level — siblings are valid, only true parent→child
17
+ * nesting trips the guard.
18
+ */
19
+ const ContentPanelNestingContext = exports.ContentPanelNestingContext = /*#__PURE__*/(0, _react.createContext)(false);
20
+ ContentPanelNestingContext.displayName = 'BloomContentPanelNestingContext';
21
+
22
+ /**
23
+ * Dev-only invariant: a `ContentPanel` must NEVER be rendered inside another
24
+ * `ContentPanel`. Nesting two content surfaces is meaningless and breaks the
25
+ * sticky / 100dvh framed frame. Call this unconditionally at the top of every
26
+ * `ContentPanel` variant — before any early return — so the hook order stays
27
+ * stable (rules of hooks).
28
+ *
29
+ * The throw is guarded by `process.env.NODE_ENV !== 'production'`, so bundlers
30
+ * (Metro on RN, Vite/Rolldown on web) statically strip the check from
31
+ * production builds for zero runtime overhead.
32
+ */
33
+ function useContentPanelNestingGuard() {
34
+ const isNested = (0, _react.useContext)(ContentPanelNestingContext);
35
+ if (process.env.NODE_ENV !== 'production' && isNested) {
36
+ throw new Error('ContentPanel cannot be nested inside another ContentPanel. ' + 'Render panels as siblings (e.g. side-by-side columns), not nested.');
37
+ }
38
+ }
39
+ //# sourceMappingURL=nesting-context.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["_react","require","ContentPanelNestingContext","exports","createContext","displayName","useContentPanelNestingGuard","isNested","useContext","process","env","NODE_ENV","Error"],"sourceRoot":"../../../src","sources":["content-panel/nesting-context.ts"],"mappings":";;;;;;;AAAA,IAAAA,MAAA,GAAAC,OAAA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,MAAMC,0BAA0B,GAAAC,OAAA,CAAAD,0BAAA,gBAAG,IAAAE,oBAAa,EAAU,KAAK,CAAC;AACvEF,0BAA0B,CAACG,WAAW,GAAG,iCAAiC;;AAE1E;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,SAASC,2BAA2BA,CAAA,EAAS;EAClD,MAAMC,QAAQ,GAAG,IAAAC,iBAAU,EAACN,0BAA0B,CAAC;EACvD,IAAIO,OAAO,CAACC,GAAG,CAACC,QAAQ,KAAK,YAAY,IAAIJ,QAAQ,EAAE;IACrD,MAAM,IAAIK,KAAK,CACb,6DAA6D,GAC3D,oEACJ,CAAC;EACH;AACF","ignoreList":[]}
@@ -24,6 +24,7 @@
24
24
  */
25
25
  import React, { memo } from 'react';
26
26
  import { View } from 'react-native';
27
+ import { ContentPanelNestingContext, useContentPanelNestingGuard } from "./nesting-context.js";
27
28
 
28
29
  /**
29
30
  * Width (in px) of the WEB sticky gutter-mask box-shadow ring. Exported for
@@ -42,15 +43,20 @@ const ContentPanelComponent = ({
42
43
  contentClassName,
43
44
  contentStyle
44
45
  }) => {
46
+ // Dev-only invariant — a ContentPanel must never be nested inside another.
47
+ useContentPanelNestingGuard();
45
48
  const surfaceClass = ['flex-1', 'rounded-radius-28', 'overflow-hidden border border-border', surfaceClassName ?? 'bg-card'].join(' ');
46
49
  const contentClass = ['flex-1', contentClassName].filter(Boolean).join(' ');
47
- return /*#__PURE__*/_jsx(View, {
48
- className: surfaceClass,
49
- style: surfaceStyle,
50
+ return /*#__PURE__*/_jsx(ContentPanelNestingContext.Provider, {
51
+ value: true,
50
52
  children: /*#__PURE__*/_jsx(View, {
51
- className: contentClass,
52
- style: contentStyle,
53
- children: children
53
+ className: surfaceClass,
54
+ style: surfaceStyle,
55
+ children: /*#__PURE__*/_jsx(View, {
56
+ className: contentClass,
57
+ style: contentStyle,
58
+ children: children
59
+ })
54
60
  })
55
61
  });
56
62
  };
@@ -1 +1 @@
1
- {"version":3,"names":["React","memo","View","jsx","_jsx","GUTTER_MASK_SPREAD","PANEL_TOP_INSET","PANEL_BOTTOM_INSET","ContentPanelComponent","children","surfaceClassName","surfaceStyle","contentClassName","contentStyle","surfaceClass","join","contentClass","filter","Boolean","className","style","ContentPanel","displayName"],"sourceRoot":"../../../src","sources":["content-panel/index.tsx"],"mappings":";;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,OAAOA,KAAK,IAAIC,IAAI,QAAQ,OAAO;AACnC,SAASC,IAAI,QAAwC,cAAc;;AAEnE;AACA;AACA;AACA;AAHA,SAAAC,GAAA,IAAAC,IAAA;AAIA,OAAO,MAAMC,kBAAkB,GAAG,EAAE;AACpC;AACA,OAAO,MAAMC,eAAe,GAAG,CAAC;AAChC;AACA,OAAO,MAAMC,kBAAkB,GAAG,CAAC;AA2BnC,MAAMC,qBAAkD,GAAGA,CAAC;EAC1DC,QAAQ;EACRC,gBAAgB;EAChBC,YAAY;EACZC,gBAAgB;EAChBC;AACF,CAAC,KAAK;EACJ,MAAMC,YAAY,GAAG,CACnB,QAAQ,EACR,mBAAmB,EACnB,sCAAsC,EACtCJ,gBAAgB,IAAI,SAAS,CAC9B,CAACK,IAAI,CAAC,GAAG,CAAC;EACX,MAAMC,YAAY,GAAG,CAAC,QAAQ,EAAEJ,gBAAgB,CAAC,CAACK,MAAM,CAACC,OAAO,CAAC,CAACH,IAAI,CAAC,GAAG,CAAC;EAE3E,oBACEX,IAAA,CAACF,IAAI;IACIiB,SAAS,EAAEL,YAAY;IAC9BM,KAAK,EAAET,YAAa;IAAAF,QAAA,eAEpBL,IAAA,CAACF,IAAI;MACIiB,SAAS,EAAEH,YAAY;MAC9BI,KAAK,EAAEP,YAAa;MAAAJ,QAAA,EAEnBA;IAAQ,CACL;EAAC,CACH,CAAC;AAEX,CAAC;AAED,OAAO,MAAMY,YAAY,gBAAGpB,IAAI,CAACO,qBAAqB,CAAC;AACvDa,YAAY,CAACC,WAAW,GAAG,cAAc","ignoreList":[]}
1
+ {"version":3,"names":["React","memo","View","ContentPanelNestingContext","useContentPanelNestingGuard","jsx","_jsx","GUTTER_MASK_SPREAD","PANEL_TOP_INSET","PANEL_BOTTOM_INSET","ContentPanelComponent","children","surfaceClassName","surfaceStyle","contentClassName","contentStyle","surfaceClass","join","contentClass","filter","Boolean","Provider","value","className","style","ContentPanel","displayName"],"sourceRoot":"../../../src","sources":["content-panel/index.tsx"],"mappings":";;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,OAAOA,KAAK,IAAIC,IAAI,QAAQ,OAAO;AACnC,SAASC,IAAI,QAAwC,cAAc;AAEnE,SACEC,0BAA0B,EAC1BC,2BAA2B,QACtB,sBAAmB;;AAE1B;AACA;AACA;AACA;AAHA,SAAAC,GAAA,IAAAC,IAAA;AAIA,OAAO,MAAMC,kBAAkB,GAAG,EAAE;AACpC;AACA,OAAO,MAAMC,eAAe,GAAG,CAAC;AAChC;AACA,OAAO,MAAMC,kBAAkB,GAAG,CAAC;AA2BnC,MAAMC,qBAAkD,GAAGA,CAAC;EAC1DC,QAAQ;EACRC,gBAAgB;EAChBC,YAAY;EACZC,gBAAgB;EAChBC;AACF,CAAC,KAAK;EACJ;EACAX,2BAA2B,CAAC,CAAC;EAE7B,MAAMY,YAAY,GAAG,CACnB,QAAQ,EACR,mBAAmB,EACnB,sCAAsC,EACtCJ,gBAAgB,IAAI,SAAS,CAC9B,CAACK,IAAI,CAAC,GAAG,CAAC;EACX,MAAMC,YAAY,GAAG,CAAC,QAAQ,EAAEJ,gBAAgB,CAAC,CAACK,MAAM,CAACC,OAAO,CAAC,CAACH,IAAI,CAAC,GAAG,CAAC;EAE3E,oBACEX,IAAA,CAACH,0BAA0B,CAACkB,QAAQ;IAACC,KAAK,EAAE,IAAK;IAAAX,QAAA,eAC/CL,IAAA,CAACJ,IAAI;MACIqB,SAAS,EAAEP,YAAY;MAC9BQ,KAAK,EAAEX,YAAa;MAAAF,QAAA,eAEpBL,IAAA,CAACJ,IAAI;QACIqB,SAAS,EAAEL,YAAY;QAC9BM,KAAK,EAAET,YAAa;QAAAJ,QAAA,EAEnBA;MAAQ,CACL;IAAC,CACH;EAAC,CAC4B,CAAC;AAE1C,CAAC;AAED,OAAO,MAAMc,YAAY,gBAAGxB,IAAI,CAACS,qBAAqB,CAAC;AACvDe,YAAY,CAACC,WAAW,GAAG,cAAc","ignoreList":[]}
@@ -39,6 +39,7 @@
39
39
  import React, { memo } from 'react';
40
40
  import { View } from 'react-native';
41
41
  import { useTheme } from "../theme/use-theme.js";
42
+ import { ContentPanelNestingContext, useContentPanelNestingGuard } from "./nesting-context.js";
42
43
 
43
44
  /** Width (in px) of the sticky gutter-mask box-shadow ring. */
44
45
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
@@ -57,37 +58,46 @@ const ContentPanelComponent = ({
57
58
  showStickyFrame,
58
59
  maskColor
59
60
  }) => {
61
+ // Dev-only invariant — must run unconditionally before the early `!framed`
62
+ // return so the hook order stays stable across renders (rules of hooks).
63
+ useContentPanelNestingGuard();
60
64
  const {
61
65
  colors
62
66
  } = useTheme();
63
67
  if (!framed) {
64
68
  // Full-bleed: a single flat surface. No rounding, no sticky overlays.
65
69
  const fullBleedClass = ['flex-1', surfaceClassName ?? 'bg-card', contentClassName].filter(Boolean).join(' ');
66
- return /*#__PURE__*/_jsx(View, {
67
- className: fullBleedClass,
68
- style: [surfaceStyle, contentStyle],
69
- children: children
70
+ return /*#__PURE__*/_jsx(ContentPanelNestingContext.Provider, {
71
+ value: true,
72
+ children: /*#__PURE__*/_jsx(View, {
73
+ className: fullBleedClass,
74
+ style: [surfaceStyle, contentStyle],
75
+ children: children
76
+ })
70
77
  });
71
78
  }
72
79
  const surfaceClass = ['flex-1', 'rounded-radius-28', surfaceClassName ?? 'bg-card'].join(' ');
73
80
  const contentClass = ['flex-1', 'rounded-radius-28 web:overflow-x-clip', contentClassName].filter(Boolean).join(' ');
74
- return /*#__PURE__*/_jsxs(View, {
75
- className: surfaceClass,
76
- style: surfaceStyle,
77
- children: [/*#__PURE__*/_jsx(View, {
78
- pointerEvents: "none",
79
- className: 'web:sticky web:top-2 z-30 h-[calc(100dvh-16px)] w-full rounded-radius-28 web:[margin-bottom:calc(-100dvh+16px)] web:[clip-path:inset(-12px)]',
80
- style: {
81
- boxShadow: `0 0 0 ${GUTTER_MASK_SPREAD}px ${maskColor ?? colors.background}`
82
- }
83
- }), showStickyFrame !== false && /*#__PURE__*/_jsx(View, {
84
- pointerEvents: "none",
85
- className: 'web:sticky web:top-2 z-[120] h-[calc(100dvh-16px)] w-full rounded-radius-28 border border-border web:[margin-bottom:calc(-100dvh+16px)]'
86
- }), /*#__PURE__*/_jsx(View, {
87
- className: contentClass,
88
- style: contentStyle,
89
- children: children
90
- })]
81
+ return /*#__PURE__*/_jsx(ContentPanelNestingContext.Provider, {
82
+ value: true,
83
+ children: /*#__PURE__*/_jsxs(View, {
84
+ className: surfaceClass,
85
+ style: surfaceStyle,
86
+ children: [/*#__PURE__*/_jsx(View, {
87
+ pointerEvents: "none",
88
+ className: 'web:sticky web:top-2 z-30 h-[calc(100dvh-16px)] w-full rounded-radius-28 web:[margin-bottom:calc(-100dvh+16px)] web:[clip-path:inset(-12px)]',
89
+ style: {
90
+ boxShadow: `0 0 0 ${GUTTER_MASK_SPREAD}px ${maskColor ?? colors.background}`
91
+ }
92
+ }), showStickyFrame !== false && /*#__PURE__*/_jsx(View, {
93
+ pointerEvents: "none",
94
+ className: 'web:sticky web:top-2 z-[120] h-[calc(100dvh-16px)] w-full rounded-radius-28 border border-border web:[margin-bottom:calc(-100dvh+16px)]'
95
+ }), /*#__PURE__*/_jsx(View, {
96
+ className: contentClass,
97
+ style: contentStyle,
98
+ children: children
99
+ })]
100
+ })
91
101
  });
92
102
  };
93
103
  export const ContentPanel = /*#__PURE__*/memo(ContentPanelComponent);
@@ -1 +1 @@
1
- {"version":3,"names":["React","memo","View","useTheme","jsx","_jsx","jsxs","_jsxs","GUTTER_MASK_SPREAD","PANEL_TOP_INSET","PANEL_BOTTOM_INSET","ContentPanelComponent","children","framed","surfaceClassName","surfaceStyle","contentClassName","contentStyle","showStickyFrame","maskColor","colors","fullBleedClass","filter","Boolean","join","className","style","surfaceClass","contentClass","pointerEvents","boxShadow","background","ContentPanel","displayName"],"sourceRoot":"../../../src","sources":["content-panel/index.web.tsx"],"mappings":";;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,OAAOA,KAAK,IAAIC,IAAI,QAAQ,OAAO;AACnC,SAASC,IAAI,QAAwC,cAAc;AAEnE,SAASC,QAAQ,QAAQ,uBAAoB;;AAE7C;AAAA,SAAAC,GAAA,IAAAC,IAAA,EAAAC,IAAA,IAAAC,KAAA;AACA,OAAO,MAAMC,kBAAkB,GAAG,EAAE;AACpC;AACA,OAAO,MAAMC,eAAe,GAAG,CAAC;AAChC;AACA,OAAO,MAAMC,kBAAkB,GAAG,CAAC;AAyBnC,MAAMC,qBAAkD,GAAGA,CAAC;EAC1DC,QAAQ;EACRC,MAAM;EACNC,gBAAgB;EAChBC,YAAY;EACZC,gBAAgB;EAChBC,YAAY;EACZC,eAAe;EACfC;AACF,CAAC,KAAK;EACJ,MAAM;IAAEC;EAAO,CAAC,GAAGjB,QAAQ,CAAC,CAAC;EAE7B,IAAI,CAACU,MAAM,EAAE;IACX;IACA,MAAMQ,cAAc,GAAG,CACrB,QAAQ,EACRP,gBAAgB,IAAI,SAAS,EAC7BE,gBAAgB,CACjB,CACEM,MAAM,CAACC,OAAO,CAAC,CACfC,IAAI,CAAC,GAAG,CAAC;IACZ,oBACEnB,IAAA,CAACH,IAAI;MACIuB,SAAS,EAAEJ,cAAc;MAChCK,KAAK,EAAE,CAACX,YAAY,EAAEE,YAAY,CAAE;MAAAL,QAAA,EAEnCA;IAAQ,CACL,CAAC;EAEX;EAEA,MAAMe,YAAY,GAAG,CAAC,QAAQ,EAAE,mBAAmB,EAAEb,gBAAgB,IAAI,SAAS,CAAC,CAACU,IAAI,CAAC,GAAG,CAAC;EAC7F,MAAMI,YAAY,GAAG,CAAC,QAAQ,EAAE,uCAAuC,EAAEZ,gBAAgB,CAAC,CACvFM,MAAM,CAACC,OAAO,CAAC,CACfC,IAAI,CAAC,GAAG,CAAC;EAEZ,oBACEjB,KAAA,CAACL,IAAI;IACIuB,SAAS,EAAEE,YAAY;IAC9BD,KAAK,EAAEX,YAAa;IAAAH,QAAA,gBAGpBP,IAAA,CAACH,IAAI;MACH2B,aAAa,EAAC,MAAM;MAElBJ,SAAS,EACP,8IAA8I;MAElJC,KAAK,EAAE;QAAEI,SAAS,EAAE,SAAStB,kBAAkB,MAAMW,SAAS,IAAIC,MAAM,CAACW,UAAU;MAAG;IAAE,CACzF,CAAC,EAEDb,eAAe,KAAK,KAAK,iBACxBb,IAAA,CAACH,IAAI;MACH2B,aAAa,EAAC,MAAM;MAElBJ,SAAS,EACP;IAAyI,CAE9I,CACF,eAEDpB,IAAA,CAACH,IAAI;MACIuB,SAAS,EAAEG,YAAY;MAC9BF,KAAK,EAAET,YAAa;MAAAL,QAAA,EAEnBA;IAAQ,CACL,CAAC;EAAA,CACH,CAAC;AAEX,CAAC;AAED,OAAO,MAAMoB,YAAY,gBAAG/B,IAAI,CAACU,qBAAqB,CAAC;AACvDqB,YAAY,CAACC,WAAW,GAAG,cAAc","ignoreList":[]}
1
+ {"version":3,"names":["React","memo","View","useTheme","ContentPanelNestingContext","useContentPanelNestingGuard","jsx","_jsx","jsxs","_jsxs","GUTTER_MASK_SPREAD","PANEL_TOP_INSET","PANEL_BOTTOM_INSET","ContentPanelComponent","children","framed","surfaceClassName","surfaceStyle","contentClassName","contentStyle","showStickyFrame","maskColor","colors","fullBleedClass","filter","Boolean","join","Provider","value","className","style","surfaceClass","contentClass","pointerEvents","boxShadow","background","ContentPanel","displayName"],"sourceRoot":"../../../src","sources":["content-panel/index.web.tsx"],"mappings":";;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,OAAOA,KAAK,IAAIC,IAAI,QAAQ,OAAO;AACnC,SAASC,IAAI,QAAwC,cAAc;AAEnE,SAASC,QAAQ,QAAQ,uBAAoB;AAC7C,SACEC,0BAA0B,EAC1BC,2BAA2B,QACtB,sBAAmB;;AAE1B;AAAA,SAAAC,GAAA,IAAAC,IAAA,EAAAC,IAAA,IAAAC,KAAA;AACA,OAAO,MAAMC,kBAAkB,GAAG,EAAE;AACpC;AACA,OAAO,MAAMC,eAAe,GAAG,CAAC;AAChC;AACA,OAAO,MAAMC,kBAAkB,GAAG,CAAC;AAyBnC,MAAMC,qBAAkD,GAAGA,CAAC;EAC1DC,QAAQ;EACRC,MAAM;EACNC,gBAAgB;EAChBC,YAAY;EACZC,gBAAgB;EAChBC,YAAY;EACZC,eAAe;EACfC;AACF,CAAC,KAAK;EACJ;EACA;EACAhB,2BAA2B,CAAC,CAAC;EAC7B,MAAM;IAAEiB;EAAO,CAAC,GAAGnB,QAAQ,CAAC,CAAC;EAE7B,IAAI,CAACY,MAAM,EAAE;IACX;IACA,MAAMQ,cAAc,GAAG,CACrB,QAAQ,EACRP,gBAAgB,IAAI,SAAS,EAC7BE,gBAAgB,CACjB,CACEM,MAAM,CAACC,OAAO,CAAC,CACfC,IAAI,CAAC,GAAG,CAAC;IACZ,oBACEnB,IAAA,CAACH,0BAA0B,CAACuB,QAAQ;MAACC,KAAK,EAAE,IAAK;MAAAd,QAAA,eAC/CP,IAAA,CAACL,IAAI;QACI2B,SAAS,EAAEN,cAAc;QAChCO,KAAK,EAAE,CAACb,YAAY,EAAEE,YAAY,CAAE;QAAAL,QAAA,EAEnCA;MAAQ,CACL;IAAC,CAC4B,CAAC;EAE1C;EAEA,MAAMiB,YAAY,GAAG,CAAC,QAAQ,EAAE,mBAAmB,EAAEf,gBAAgB,IAAI,SAAS,CAAC,CAACU,IAAI,CAAC,GAAG,CAAC;EAC7F,MAAMM,YAAY,GAAG,CAAC,QAAQ,EAAE,uCAAuC,EAAEd,gBAAgB,CAAC,CACvFM,MAAM,CAACC,OAAO,CAAC,CACfC,IAAI,CAAC,GAAG,CAAC;EAEZ,oBACEnB,IAAA,CAACH,0BAA0B,CAACuB,QAAQ;IAACC,KAAK,EAAE,IAAK;IAAAd,QAAA,eAC/CL,KAAA,CAACP,IAAI;MACI2B,SAAS,EAAEE,YAAY;MAC9BD,KAAK,EAAEb,YAAa;MAAAH,QAAA,gBAGpBP,IAAA,CAACL,IAAI;QACH+B,aAAa,EAAC,MAAM;QAElBJ,SAAS,EACP,8IAA8I;QAElJC,KAAK,EAAE;UAAEI,SAAS,EAAE,SAASxB,kBAAkB,MAAMW,SAAS,IAAIC,MAAM,CAACa,UAAU;QAAG;MAAE,CACzF,CAAC,EAEDf,eAAe,KAAK,KAAK,iBACxBb,IAAA,CAACL,IAAI;QACH+B,aAAa,EAAC,MAAM;QAElBJ,SAAS,EACP;MAAyI,CAE9I,CACF,eAEDtB,IAAA,CAACL,IAAI;QACI2B,SAAS,EAAEG,YAAY;QAC9BF,KAAK,EAAEX,YAAa;QAAAL,QAAA,EAEnBA;MAAQ,CACL,CAAC;IAAA,CACH;EAAC,CAC4B,CAAC;AAE1C,CAAC;AAED,OAAO,MAAMsB,YAAY,gBAAGnC,IAAI,CAACY,qBAAqB,CAAC;AACvDuB,YAAY,CAACC,WAAW,GAAG,cAAc","ignoreList":[]}
@@ -0,0 +1,35 @@
1
+ "use strict";
2
+
3
+ import { createContext, useContext } from 'react';
4
+
5
+ /**
6
+ * Tracks whether the current subtree is already inside a `ContentPanel`.
7
+ *
8
+ * Every `ContentPanel` variant (web + native) wraps its rendered tree in
9
+ * `<ContentPanelNestingContext.Provider value={true}>`, so any DESCENDANT
10
+ * `ContentPanel` reads `true` here and can reject the invalid nesting. Context
11
+ * only flows downward, so side-by-side sibling panels each read the default
12
+ * `false` at their own level — siblings are valid, only true parent→child
13
+ * nesting trips the guard.
14
+ */
15
+ export const ContentPanelNestingContext = /*#__PURE__*/createContext(false);
16
+ ContentPanelNestingContext.displayName = 'BloomContentPanelNestingContext';
17
+
18
+ /**
19
+ * Dev-only invariant: a `ContentPanel` must NEVER be rendered inside another
20
+ * `ContentPanel`. Nesting two content surfaces is meaningless and breaks the
21
+ * sticky / 100dvh framed frame. Call this unconditionally at the top of every
22
+ * `ContentPanel` variant — before any early return — so the hook order stays
23
+ * stable (rules of hooks).
24
+ *
25
+ * The throw is guarded by `process.env.NODE_ENV !== 'production'`, so bundlers
26
+ * (Metro on RN, Vite/Rolldown on web) statically strip the check from
27
+ * production builds for zero runtime overhead.
28
+ */
29
+ export function useContentPanelNestingGuard() {
30
+ const isNested = useContext(ContentPanelNestingContext);
31
+ if (process.env.NODE_ENV !== 'production' && isNested) {
32
+ throw new Error('ContentPanel cannot be nested inside another ContentPanel. ' + 'Render panels as siblings (e.g. side-by-side columns), not nested.');
33
+ }
34
+ }
35
+ //# sourceMappingURL=nesting-context.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["createContext","useContext","ContentPanelNestingContext","displayName","useContentPanelNestingGuard","isNested","process","env","NODE_ENV","Error"],"sourceRoot":"../../../src","sources":["content-panel/nesting-context.ts"],"mappings":";;AAAA,SAASA,aAAa,EAAEC,UAAU,QAAQ,OAAO;;AAEjD;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,OAAO,MAAMC,0BAA0B,gBAAGF,aAAa,CAAU,KAAK,CAAC;AACvEE,0BAA0B,CAACC,WAAW,GAAG,iCAAiC;;AAE1E;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,OAAO,SAASC,2BAA2BA,CAAA,EAAS;EAClD,MAAMC,QAAQ,GAAGJ,UAAU,CAACC,0BAA0B,CAAC;EACvD,IAAII,OAAO,CAACC,GAAG,CAACC,QAAQ,KAAK,YAAY,IAAIH,QAAQ,EAAE;IACrD,MAAM,IAAII,KAAK,CACb,6DAA6D,GAC3D,oEACJ,CAAC;EACH;AACF","ignoreList":[]}
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/content-panel/index.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,OAAO,KAAe,MAAM,OAAO,CAAC;AACpC,OAAO,EAAQ,KAAK,SAAS,EAAE,KAAK,SAAS,EAAE,MAAM,cAAc,CAAC;AAEpE;;;GAGG;AACH,eAAO,MAAM,kBAAkB,KAAK,CAAC;AACrC,wDAAwD;AACxD,eAAO,MAAM,eAAe,IAAI,CAAC;AACjC,2DAA2D;AAC3D,eAAO,MAAM,kBAAkB,IAAI,CAAC;AAEpC,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;IAC1B;;;OAGG;IACH,MAAM,EAAE,OAAO,CAAC;IAChB,uEAAuE;IACvE,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,YAAY,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IACpC,qDAAqD;IACrD,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,YAAY,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IACpC;;;OAGG;IACH,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAgCD,eAAO,MAAM,YAAY,+CAA8B,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/content-panel/index.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,OAAO,KAAe,MAAM,OAAO,CAAC;AACpC,OAAO,EAAQ,KAAK,SAAS,EAAE,KAAK,SAAS,EAAE,MAAM,cAAc,CAAC;AAOpE;;;GAGG;AACH,eAAO,MAAM,kBAAkB,KAAK,CAAC;AACrC,wDAAwD;AACxD,eAAO,MAAM,eAAe,IAAI,CAAC;AACjC,2DAA2D;AAC3D,eAAO,MAAM,kBAAkB,IAAI,CAAC;AAEpC,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;IAC1B;;;OAGG;IACH,MAAM,EAAE,OAAO,CAAC;IAChB,uEAAuE;IACvE,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,YAAY,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IACpC,qDAAqD;IACrD,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,YAAY,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IACpC;;;OAGG;IACH,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAqCD,eAAO,MAAM,YAAY,+CAA8B,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.web.d.ts","sourceRoot":"","sources":["../../../../src/content-panel/index.web.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AACH,OAAO,KAAe,MAAM,OAAO,CAAC;AACpC,OAAO,EAAQ,KAAK,SAAS,EAAE,KAAK,SAAS,EAAE,MAAM,cAAc,CAAC;AAIpE,+DAA+D;AAC/D,eAAO,MAAM,kBAAkB,KAAK,CAAC;AACrC,6DAA6D;AAC7D,eAAO,MAAM,eAAe,IAAI,CAAC;AACjC,2DAA2D;AAC3D,eAAO,MAAM,kBAAkB,IAAI,CAAC;AAEpC,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;IAC1B,4DAA4D;IAC5D,MAAM,EAAE,OAAO,CAAC;IAChB,uEAAuE;IACvE,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,YAAY,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IACpC,qDAAqD;IACrD,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,YAAY,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IACpC,qEAAqE;IACrE,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B;;;;;;;OAOG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAyED,eAAO,MAAM,YAAY,+CAA8B,CAAC"}
1
+ {"version":3,"file":"index.web.d.ts","sourceRoot":"","sources":["../../../../src/content-panel/index.web.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AACH,OAAO,KAAe,MAAM,OAAO,CAAC;AACpC,OAAO,EAAQ,KAAK,SAAS,EAAE,KAAK,SAAS,EAAE,MAAM,cAAc,CAAC;AAQpE,+DAA+D;AAC/D,eAAO,MAAM,kBAAkB,KAAK,CAAC;AACrC,6DAA6D;AAC7D,eAAO,MAAM,eAAe,IAAI,CAAC;AACjC,2DAA2D;AAC3D,eAAO,MAAM,kBAAkB,IAAI,CAAC;AAEpC,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;IAC1B,4DAA4D;IAC5D,MAAM,EAAE,OAAO,CAAC;IAChB,uEAAuE;IACvE,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,YAAY,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IACpC,qDAAqD;IACrD,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,YAAY,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IACpC,qEAAqE;IACrE,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B;;;;;;;OAOG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAgFD,eAAO,MAAM,YAAY,+CAA8B,CAAC"}
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Tracks whether the current subtree is already inside a `ContentPanel`.
3
+ *
4
+ * Every `ContentPanel` variant (web + native) wraps its rendered tree in
5
+ * `<ContentPanelNestingContext.Provider value={true}>`, so any DESCENDANT
6
+ * `ContentPanel` reads `true` here and can reject the invalid nesting. Context
7
+ * only flows downward, so side-by-side sibling panels each read the default
8
+ * `false` at their own level — siblings are valid, only true parent→child
9
+ * nesting trips the guard.
10
+ */
11
+ export declare const ContentPanelNestingContext: import("react").Context<boolean>;
12
+ /**
13
+ * Dev-only invariant: a `ContentPanel` must NEVER be rendered inside another
14
+ * `ContentPanel`. Nesting two content surfaces is meaningless and breaks the
15
+ * sticky / 100dvh framed frame. Call this unconditionally at the top of every
16
+ * `ContentPanel` variant — before any early return — so the hook order stays
17
+ * stable (rules of hooks).
18
+ *
19
+ * The throw is guarded by `process.env.NODE_ENV !== 'production'`, so bundlers
20
+ * (Metro on RN, Vite/Rolldown on web) statically strip the check from
21
+ * production builds for zero runtime overhead.
22
+ */
23
+ export declare function useContentPanelNestingGuard(): void;
24
+ //# sourceMappingURL=nesting-context.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"nesting-context.d.ts","sourceRoot":"","sources":["../../../../src/content-panel/nesting-context.ts"],"names":[],"mappings":"AAEA;;;;;;;;;GASG;AACH,eAAO,MAAM,0BAA0B,kCAAgC,CAAC;AAGxE;;;;;;;;;;GAUG;AACH,wBAAgB,2BAA2B,IAAI,IAAI,CAQlD"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/content-panel/index.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,OAAO,KAAe,MAAM,OAAO,CAAC;AACpC,OAAO,EAAQ,KAAK,SAAS,EAAE,KAAK,SAAS,EAAE,MAAM,cAAc,CAAC;AAEpE;;;GAGG;AACH,eAAO,MAAM,kBAAkB,KAAK,CAAC;AACrC,wDAAwD;AACxD,eAAO,MAAM,eAAe,IAAI,CAAC;AACjC,2DAA2D;AAC3D,eAAO,MAAM,kBAAkB,IAAI,CAAC;AAEpC,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;IAC1B;;;OAGG;IACH,MAAM,EAAE,OAAO,CAAC;IAChB,uEAAuE;IACvE,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,YAAY,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IACpC,qDAAqD;IACrD,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,YAAY,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IACpC;;;OAGG;IACH,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAgCD,eAAO,MAAM,YAAY,+CAA8B,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/content-panel/index.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,OAAO,KAAe,MAAM,OAAO,CAAC;AACpC,OAAO,EAAQ,KAAK,SAAS,EAAE,KAAK,SAAS,EAAE,MAAM,cAAc,CAAC;AAOpE;;;GAGG;AACH,eAAO,MAAM,kBAAkB,KAAK,CAAC;AACrC,wDAAwD;AACxD,eAAO,MAAM,eAAe,IAAI,CAAC;AACjC,2DAA2D;AAC3D,eAAO,MAAM,kBAAkB,IAAI,CAAC;AAEpC,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;IAC1B;;;OAGG;IACH,MAAM,EAAE,OAAO,CAAC;IAChB,uEAAuE;IACvE,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,YAAY,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IACpC,qDAAqD;IACrD,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,YAAY,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IACpC;;;OAGG;IACH,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAqCD,eAAO,MAAM,YAAY,+CAA8B,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.web.d.ts","sourceRoot":"","sources":["../../../../src/content-panel/index.web.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AACH,OAAO,KAAe,MAAM,OAAO,CAAC;AACpC,OAAO,EAAQ,KAAK,SAAS,EAAE,KAAK,SAAS,EAAE,MAAM,cAAc,CAAC;AAIpE,+DAA+D;AAC/D,eAAO,MAAM,kBAAkB,KAAK,CAAC;AACrC,6DAA6D;AAC7D,eAAO,MAAM,eAAe,IAAI,CAAC;AACjC,2DAA2D;AAC3D,eAAO,MAAM,kBAAkB,IAAI,CAAC;AAEpC,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;IAC1B,4DAA4D;IAC5D,MAAM,EAAE,OAAO,CAAC;IAChB,uEAAuE;IACvE,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,YAAY,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IACpC,qDAAqD;IACrD,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,YAAY,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IACpC,qEAAqE;IACrE,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B;;;;;;;OAOG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAyED,eAAO,MAAM,YAAY,+CAA8B,CAAC"}
1
+ {"version":3,"file":"index.web.d.ts","sourceRoot":"","sources":["../../../../src/content-panel/index.web.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AACH,OAAO,KAAe,MAAM,OAAO,CAAC;AACpC,OAAO,EAAQ,KAAK,SAAS,EAAE,KAAK,SAAS,EAAE,MAAM,cAAc,CAAC;AAQpE,+DAA+D;AAC/D,eAAO,MAAM,kBAAkB,KAAK,CAAC;AACrC,6DAA6D;AAC7D,eAAO,MAAM,eAAe,IAAI,CAAC;AACjC,2DAA2D;AAC3D,eAAO,MAAM,kBAAkB,IAAI,CAAC;AAEpC,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;IAC1B,4DAA4D;IAC5D,MAAM,EAAE,OAAO,CAAC;IAChB,uEAAuE;IACvE,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,YAAY,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IACpC,qDAAqD;IACrD,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,YAAY,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IACpC,qEAAqE;IACrE,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B;;;;;;;OAOG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAgFD,eAAO,MAAM,YAAY,+CAA8B,CAAC"}
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Tracks whether the current subtree is already inside a `ContentPanel`.
3
+ *
4
+ * Every `ContentPanel` variant (web + native) wraps its rendered tree in
5
+ * `<ContentPanelNestingContext.Provider value={true}>`, so any DESCENDANT
6
+ * `ContentPanel` reads `true` here and can reject the invalid nesting. Context
7
+ * only flows downward, so side-by-side sibling panels each read the default
8
+ * `false` at their own level — siblings are valid, only true parent→child
9
+ * nesting trips the guard.
10
+ */
11
+ export declare const ContentPanelNestingContext: import("react").Context<boolean>;
12
+ /**
13
+ * Dev-only invariant: a `ContentPanel` must NEVER be rendered inside another
14
+ * `ContentPanel`. Nesting two content surfaces is meaningless and breaks the
15
+ * sticky / 100dvh framed frame. Call this unconditionally at the top of every
16
+ * `ContentPanel` variant — before any early return — so the hook order stays
17
+ * stable (rules of hooks).
18
+ *
19
+ * The throw is guarded by `process.env.NODE_ENV !== 'production'`, so bundlers
20
+ * (Metro on RN, Vite/Rolldown on web) statically strip the check from
21
+ * production builds for zero runtime overhead.
22
+ */
23
+ export declare function useContentPanelNestingGuard(): void;
24
+ //# sourceMappingURL=nesting-context.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"nesting-context.d.ts","sourceRoot":"","sources":["../../../../src/content-panel/nesting-context.ts"],"names":[],"mappings":"AAEA;;;;;;;;;GASG;AACH,eAAO,MAAM,0BAA0B,kCAAgC,CAAC;AAGxE;;;;;;;;;;GAUG;AACH,wBAAgB,2BAA2B,IAAI,IAAI,CAQlD"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@oxyhq/bloom",
3
- "version": "0.22.0",
3
+ "version": "0.23.0",
4
4
  "description": "Bloom UI — Oxy ecosystem component library for React Native + Expo + Web",
5
5
  "main": "lib/commonjs/index.js",
6
6
  "module": "lib/module/index.js",
@@ -0,0 +1,52 @@
1
+ import React from 'react';
2
+ import { Text } from 'react-native';
3
+ import { render } from '@testing-library/react-native';
4
+
5
+ import { ContentPanel } from '../content-panel';
6
+
7
+ // Jest resolves `../content-panel` to the native variant (`index.tsx`), which
8
+ // does not read the theme — so these render without a BloomThemeProvider. The
9
+ // nesting guard lives in the shared `nesting-context` module exercised by both
10
+ // variants.
11
+ describe('ContentPanel nesting guard', () => {
12
+ it('renders a single (non-nested) panel', () => {
13
+ const { getByText } = render(
14
+ <ContentPanel framed>
15
+ <Text>content</Text>
16
+ </ContentPanel>,
17
+ );
18
+ expect(getByText('content')).toBeTruthy();
19
+ });
20
+
21
+ it('renders side-by-side sibling panels without tripping the guard', () => {
22
+ const { getByText } = render(
23
+ <React.Fragment>
24
+ <ContentPanel framed>
25
+ <Text>left</Text>
26
+ </ContentPanel>
27
+ <ContentPanel framed>
28
+ <Text>right</Text>
29
+ </ContentPanel>
30
+ </React.Fragment>,
31
+ );
32
+ expect(getByText('left')).toBeTruthy();
33
+ expect(getByText('right')).toBeTruthy();
34
+ });
35
+
36
+ it('throws when a ContentPanel is nested inside another ContentPanel', () => {
37
+ const consoleError = jest.spyOn(console, 'error').mockImplementation(() => {});
38
+ expect(() =>
39
+ render(
40
+ <ContentPanel framed>
41
+ <ContentPanel framed>
42
+ <Text>nested</Text>
43
+ </ContentPanel>
44
+ </ContentPanel>,
45
+ ),
46
+ ).toThrow(
47
+ 'ContentPanel cannot be nested inside another ContentPanel. ' +
48
+ 'Render panels as siblings (e.g. side-by-side columns), not nested.',
49
+ );
50
+ consoleError.mockRestore();
51
+ });
52
+ });
@@ -23,6 +23,11 @@
23
23
  import React, { memo } from 'react';
24
24
  import { View, type StyleProp, type ViewStyle } from 'react-native';
25
25
 
26
+ import {
27
+ ContentPanelNestingContext,
28
+ useContentPanelNestingGuard,
29
+ } from './nesting-context';
30
+
26
31
  /**
27
32
  * Width (in px) of the WEB sticky gutter-mask box-shadow ring. Exported for
28
33
  * cross-platform API parity (it has no effect on native).
@@ -65,6 +70,9 @@ const ContentPanelComponent: React.FC<ContentPanelProps> = ({
65
70
  contentClassName,
66
71
  contentStyle,
67
72
  }) => {
73
+ // Dev-only invariant — a ContentPanel must never be nested inside another.
74
+ useContentPanelNestingGuard();
75
+
68
76
  const surfaceClass = [
69
77
  'flex-1',
70
78
  'rounded-radius-28',
@@ -74,17 +82,19 @@ const ContentPanelComponent: React.FC<ContentPanelProps> = ({
74
82
  const contentClass = ['flex-1', contentClassName].filter(Boolean).join(' ');
75
83
 
76
84
  return (
77
- <View
78
- {...({ className: surfaceClass } as Record<string, string>)}
79
- style={surfaceStyle}
80
- >
85
+ <ContentPanelNestingContext.Provider value={true}>
81
86
  <View
82
- {...({ className: contentClass } as Record<string, string>)}
83
- style={contentStyle}
87
+ {...({ className: surfaceClass } as Record<string, string>)}
88
+ style={surfaceStyle}
84
89
  >
85
- {children}
90
+ <View
91
+ {...({ className: contentClass } as Record<string, string>)}
92
+ style={contentStyle}
93
+ >
94
+ {children}
95
+ </View>
86
96
  </View>
87
- </View>
97
+ </ContentPanelNestingContext.Provider>
88
98
  );
89
99
  };
90
100
 
@@ -38,6 +38,10 @@ import React, { memo } from 'react';
38
38
  import { View, type StyleProp, type ViewStyle } from 'react-native';
39
39
 
40
40
  import { useTheme } from '../theme/use-theme';
41
+ import {
42
+ ContentPanelNestingContext,
43
+ useContentPanelNestingGuard,
44
+ } from './nesting-context';
41
45
 
42
46
  /** Width (in px) of the sticky gutter-mask box-shadow ring. */
43
47
  export const GUTTER_MASK_SPREAD = 40;
@@ -79,6 +83,9 @@ const ContentPanelComponent: React.FC<ContentPanelProps> = ({
79
83
  showStickyFrame,
80
84
  maskColor,
81
85
  }) => {
86
+ // Dev-only invariant — must run unconditionally before the early `!framed`
87
+ // return so the hook order stays stable across renders (rules of hooks).
88
+ useContentPanelNestingGuard();
82
89
  const { colors } = useTheme();
83
90
 
84
91
  if (!framed) {
@@ -91,12 +98,14 @@ const ContentPanelComponent: React.FC<ContentPanelProps> = ({
91
98
  .filter(Boolean)
92
99
  .join(' ');
93
100
  return (
94
- <View
95
- {...({ className: fullBleedClass } as Record<string, string>)}
96
- style={[surfaceStyle, contentStyle]}
97
- >
98
- {children}
99
- </View>
101
+ <ContentPanelNestingContext.Provider value={true}>
102
+ <View
103
+ {...({ className: fullBleedClass } as Record<string, string>)}
104
+ style={[surfaceStyle, contentStyle]}
105
+ >
106
+ {children}
107
+ </View>
108
+ </ContentPanelNestingContext.Provider>
100
109
  );
101
110
  }
102
111
 
@@ -106,37 +115,39 @@ const ContentPanelComponent: React.FC<ContentPanelProps> = ({
106
115
  .join(' ');
107
116
 
108
117
  return (
109
- <View
110
- {...({ className: surfaceClass } as Record<string, string>)}
111
- style={surfaceStyle}
112
- >
113
- {/* (1) Bleed-mask overlay — gutter box-shadow ring, below chrome. */}
118
+ <ContentPanelNestingContext.Provider value={true}>
114
119
  <View
115
- pointerEvents="none"
116
- {...({
117
- className:
118
- 'web:sticky web:top-2 z-30 h-[calc(100dvh-16px)] w-full rounded-radius-28 web:[margin-bottom:calc(-100dvh+16px)] web:[clip-path:inset(-12px)]',
119
- } as Record<string, string>)}
120
- style={{ boxShadow: `0 0 0 ${GUTTER_MASK_SPREAD}px ${maskColor ?? colors.background}` }}
121
- />
122
- {/* (2) Border-frame overlay — one continuous rounded border, above all. */}
123
- {showStickyFrame !== false && (
120
+ {...({ className: surfaceClass } as Record<string, string>)}
121
+ style={surfaceStyle}
122
+ >
123
+ {/* (1) Bleed-mask overlay — gutter box-shadow ring, below chrome. */}
124
124
  <View
125
125
  pointerEvents="none"
126
126
  {...({
127
127
  className:
128
- 'web:sticky web:top-2 z-[120] h-[calc(100dvh-16px)] w-full rounded-radius-28 border border-border web:[margin-bottom:calc(-100dvh+16px)]',
128
+ 'web:sticky web:top-2 z-30 h-[calc(100dvh-16px)] w-full rounded-radius-28 web:[margin-bottom:calc(-100dvh+16px)] web:[clip-path:inset(-12px)]',
129
129
  } as Record<string, string>)}
130
+ style={{ boxShadow: `0 0 0 ${GUTTER_MASK_SPREAD}px ${maskColor ?? colors.background}` }}
130
131
  />
131
- )}
132
- {/* Content wrapper clipped to the rounded panel shape on web. */}
133
- <View
134
- {...({ className: contentClass } as Record<string, string>)}
135
- style={contentStyle}
136
- >
137
- {children}
132
+ {/* (2) Border-frame overlay — one continuous rounded border, above all. */}
133
+ {showStickyFrame !== false && (
134
+ <View
135
+ pointerEvents="none"
136
+ {...({
137
+ className:
138
+ 'web:sticky web:top-2 z-[120] h-[calc(100dvh-16px)] w-full rounded-radius-28 border border-border web:[margin-bottom:calc(-100dvh+16px)]',
139
+ } as Record<string, string>)}
140
+ />
141
+ )}
142
+ {/* Content wrapper — clipped to the rounded panel shape on web. */}
143
+ <View
144
+ {...({ className: contentClass } as Record<string, string>)}
145
+ style={contentStyle}
146
+ >
147
+ {children}
148
+ </View>
138
149
  </View>
139
- </View>
150
+ </ContentPanelNestingContext.Provider>
140
151
  );
141
152
  };
142
153
 
@@ -0,0 +1,35 @@
1
+ import { createContext, useContext } from 'react';
2
+
3
+ /**
4
+ * Tracks whether the current subtree is already inside a `ContentPanel`.
5
+ *
6
+ * Every `ContentPanel` variant (web + native) wraps its rendered tree in
7
+ * `<ContentPanelNestingContext.Provider value={true}>`, so any DESCENDANT
8
+ * `ContentPanel` reads `true` here and can reject the invalid nesting. Context
9
+ * only flows downward, so side-by-side sibling panels each read the default
10
+ * `false` at their own level — siblings are valid, only true parent→child
11
+ * nesting trips the guard.
12
+ */
13
+ export const ContentPanelNestingContext = createContext<boolean>(false);
14
+ ContentPanelNestingContext.displayName = 'BloomContentPanelNestingContext';
15
+
16
+ /**
17
+ * Dev-only invariant: a `ContentPanel` must NEVER be rendered inside another
18
+ * `ContentPanel`. Nesting two content surfaces is meaningless and breaks the
19
+ * sticky / 100dvh framed frame. Call this unconditionally at the top of every
20
+ * `ContentPanel` variant — before any early return — so the hook order stays
21
+ * stable (rules of hooks).
22
+ *
23
+ * The throw is guarded by `process.env.NODE_ENV !== 'production'`, so bundlers
24
+ * (Metro on RN, Vite/Rolldown on web) statically strip the check from
25
+ * production builds for zero runtime overhead.
26
+ */
27
+ export function useContentPanelNestingGuard(): void {
28
+ const isNested = useContext(ContentPanelNestingContext);
29
+ if (process.env.NODE_ENV !== 'production' && isNested) {
30
+ throw new Error(
31
+ 'ContentPanel cannot be nested inside another ContentPanel. ' +
32
+ 'Render panels as siblings (e.g. side-by-side columns), not nested.',
33
+ );
34
+ }
35
+ }