@oxyhq/bloom 0.35.7 → 0.35.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/commonjs/content-panel/index.js +37 -9
- package/lib/commonjs/content-panel/index.js.map +1 -1
- package/lib/commonjs/content-panel/index.web.js +71 -15
- package/lib/commonjs/content-panel/index.web.js.map +1 -1
- package/lib/module/content-panel/index.js +37 -9
- package/lib/module/content-panel/index.js.map +1 -1
- package/lib/module/content-panel/index.web.js +71 -15
- package/lib/module/content-panel/index.web.js.map +1 -1
- package/lib/typescript/commonjs/content-panel/index.d.ts +24 -6
- package/lib/typescript/commonjs/content-panel/index.d.ts.map +1 -1
- package/lib/typescript/commonjs/content-panel/index.web.d.ts +33 -10
- package/lib/typescript/commonjs/content-panel/index.web.d.ts.map +1 -1
- package/lib/typescript/module/content-panel/index.d.ts +24 -6
- package/lib/typescript/module/content-panel/index.d.ts.map +1 -1
- package/lib/typescript/module/content-panel/index.web.d.ts +33 -10
- package/lib/typescript/module/content-panel/index.web.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/__tests__/ContentPanelNesting.test.tsx +74 -0
- package/src/content-panel/index.tsx +47 -11
- package/src/content-panel/index.web.tsx +85 -17
|
@@ -22,11 +22,12 @@ function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r
|
|
|
22
22
|
* scroll, no sticky positioning, and no bleed to mask, so the panel is simply a
|
|
23
23
|
* surface wrapping its content. The `framed` prop still drives whether that
|
|
24
24
|
* surface is rounded + bordered, tri-state and resolved with pure NativeWind
|
|
25
|
-
* (`
|
|
26
|
-
* hook needed):
|
|
25
|
+
* (the breakpoint's `min-*:` variant evaluates against the window width on
|
|
26
|
+
* native too — no JS breakpoint hook needed):
|
|
27
27
|
*
|
|
28
|
-
* - `undefined` (DEFAULT) → responsive: full-bleed below `
|
|
29
|
-
*
|
|
28
|
+
* - `undefined` (DEFAULT) → responsive: full-bleed below the `framedFrom`
|
|
29
|
+
* breakpoint (default `768` / Tailwind `md`), rounded + bordered at/above it
|
|
30
|
+
* (e.g. `md:rounded-radius-28 md:border md:border-border`).
|
|
30
31
|
* - `false` → never framed (plain full-bleed at every size).
|
|
31
32
|
* - `true` → always rounded + bordered.
|
|
32
33
|
*
|
|
@@ -50,9 +51,34 @@ const GUTTER_MASK_SPREAD = exports.GUTTER_MASK_SPREAD = 40;
|
|
|
50
51
|
const PANEL_TOP_INSET = exports.PANEL_TOP_INSET = 8;
|
|
51
52
|
/** Bottom sticky inset (px) of the framed panel chrome. */
|
|
52
53
|
const PANEL_BOTTOM_INSET = exports.PANEL_BOTTOM_INSET = 8;
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Viewport width (px) at which a RESPONSIVE `ContentPanel` (`framed` undefined)
|
|
57
|
+
* switches from full-bleed to framed. Each value maps to a PRE-SHIPPED literal
|
|
58
|
+
* Tailwind class bundle — `768` (default) uses the named `md:` screen
|
|
59
|
+
* (byte-identical to prior behavior), `640` uses `sm:`, `1024` uses `lg:`, and
|
|
60
|
+
* `500` uses the arbitrary `min-[500px]:` variant (there is no named screen at
|
|
61
|
+
* 500px). The breakpoint tokens are never built at runtime, so a consumer's
|
|
62
|
+
* Tailwind content-scan over `src/**`/`lib/**` resolves them verbatim.
|
|
63
|
+
*/
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Per-breakpoint literal Tailwind surface class bundle for the NATIVE responsive
|
|
67
|
+
* mode. Each value is a WHOLE literal string (never assembled from parts at
|
|
68
|
+
* runtime) so the Tailwind content-scan over `src/**`/`lib/**` picks up every
|
|
69
|
+
* `min-*`/`sm:`/`md:`/`lg:` token verbatim. Selecting a bundle by `framedFrom`
|
|
70
|
+
* value is the only runtime decision.
|
|
71
|
+
*/
|
|
72
|
+
const RESPONSIVE_SURFACE = {
|
|
73
|
+
500: 'min-[500px]:overflow-hidden min-[500px]:rounded-radius-28 min-[500px]:border min-[500px]:border-border',
|
|
74
|
+
640: 'sm:overflow-hidden sm:rounded-radius-28 sm:border sm:border-border',
|
|
75
|
+
768: 'md:overflow-hidden md:rounded-radius-28 md:border md:border-border',
|
|
76
|
+
1024: 'lg:overflow-hidden lg:rounded-radius-28 lg:border lg:border-border'
|
|
77
|
+
};
|
|
53
78
|
const ContentPanelComponent = ({
|
|
54
79
|
children,
|
|
55
80
|
framed,
|
|
81
|
+
framedFrom = 768,
|
|
56
82
|
surfaceClassName,
|
|
57
83
|
surfaceStyle,
|
|
58
84
|
contentClassName,
|
|
@@ -61,11 +87,13 @@ const ContentPanelComponent = ({
|
|
|
61
87
|
// Dev-only invariant — a ContentPanel must never be nested inside another.
|
|
62
88
|
(0, _nestingContext.useContentPanelNestingGuard)();
|
|
63
89
|
|
|
64
|
-
// Tri-state: `undefined` → responsive (
|
|
65
|
-
// `false` → never framed (plain full-bleed). Whole literal class
|
|
66
|
-
// selected per mode so the Tailwind content-scan over `src/**`
|
|
67
|
-
// `md:` / `rounded-radius-28` token verbatim
|
|
68
|
-
|
|
90
|
+
// Tri-state: `undefined` → responsive (breakpoint-gated), `true` → always
|
|
91
|
+
// framed, `false` → never framed (plain full-bleed). Whole literal class
|
|
92
|
+
// strings are selected per mode so the Tailwind content-scan over `src/**`
|
|
93
|
+
// picks up every `min-*`/`sm:`/`md:`/`lg:` / `rounded-radius-28` token verbatim
|
|
94
|
+
// (the breakpoint bundle lives whole in `RESPONSIVE_SURFACE`; `framed ===
|
|
95
|
+
// true`/`false` ignore `framedFrom` for fixed always/never framing).
|
|
96
|
+
const surfaceBase = framed === undefined ? `flex-1 ${RESPONSIVE_SURFACE[framedFrom]}` : framed ? 'flex-1 overflow-hidden rounded-radius-28 border border-border' : 'flex-1';
|
|
69
97
|
const surfaceClass = [surfaceBase, surfaceClassName ?? 'bg-card'].join(' ');
|
|
70
98
|
const contentClass = ['flex-1', contentClassName].filter(Boolean).join(' ');
|
|
71
99
|
return /*#__PURE__*/(0, _jsxRuntime.jsx)(_nestingContext.ContentPanelNestingContext.Provider, {
|
|
@@ -1 +1 @@
|
|
|
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","framed","surfaceClassName","surfaceStyle","contentClassName","contentStyle","useContentPanelNestingGuard","surfaceBase","undefined","surfaceClass","join","contentClass","filter","Boolean","jsx","ContentPanelNestingContext","Provider","value","View","className","style","ContentPanel","memo","displayName"],"sourceRoot":"../../../src","sources":["content-panel/index.tsx"],"mappings":";;;;;;
|
|
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","RESPONSIVE_SURFACE","ContentPanelComponent","children","framed","framedFrom","surfaceClassName","surfaceStyle","contentClassName","contentStyle","useContentPanelNestingGuard","surfaceBase","undefined","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":[]}
|
|
@@ -38,18 +38,22 @@ function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r
|
|
|
38
38
|
* Framing is tri-state via the `framed` prop:
|
|
39
39
|
*
|
|
40
40
|
* - `undefined` (DEFAULT) → RESPONSIVE, driven purely by NativeWind: full-bleed
|
|
41
|
-
* below `
|
|
42
|
-
*
|
|
43
|
-
*
|
|
41
|
+
* below the `framedFrom` breakpoint, framed at/above it. The breakpoint is
|
|
42
|
+
* configurable per consumer (`framedFrom`, default `768` / Tailwind `md`); the
|
|
43
|
+
* rounding is gated by that breakpoint's `min-*:` variant and both overlays
|
|
44
|
+
* are always rendered but carry the matching `max-*:hidden` literal
|
|
45
|
+
* (`display:none` below the breakpoint), so the breakpoint is decided entirely
|
|
46
|
+
* in CSS — no consumer JS breakpoint hook.
|
|
44
47
|
* - `false` → NEVER framed (full-bleed at every size): a single flat surface,
|
|
45
48
|
* no rounding, no overlays.
|
|
46
49
|
* - `true` → ALWAYS framed (rounded + overlays at every size).
|
|
47
50
|
*
|
|
48
|
-
* The overlays are gated by VISIBILITY (a `max
|
|
49
|
-
* by conditional mounting for the responsive case, so
|
|
50
|
-
* never remounts them. The content wrapper likewise
|
|
51
|
-
* `{children}` reconciles in place across the breakpoint
|
|
52
|
-
* (which would reset feed scroll / the virtualizer +
|
|
51
|
+
* The overlays are gated by VISIBILITY (a `max-*:hidden` literal for the chosen
|
|
52
|
+
* breakpoint) rather than by conditional mounting for the responsive case, so
|
|
53
|
+
* crossing the breakpoint never remounts them. The content wrapper likewise
|
|
54
|
+
* keeps a stable `key` so `{children}` reconciles in place across the breakpoint
|
|
55
|
+
* instead of remounting (which would reset feed scroll / the virtualizer +
|
|
56
|
+
* refetch).
|
|
53
57
|
*
|
|
54
58
|
* Native bundlers use `./index.tsx` (a plain rounded surface); web bundlers
|
|
55
59
|
* select this file via the `"browser"` export condition in `package.json`.
|
|
@@ -66,9 +70,58 @@ const GUTTER_MASK_SPREAD = exports.GUTTER_MASK_SPREAD = 40;
|
|
|
66
70
|
const PANEL_TOP_INSET = exports.PANEL_TOP_INSET = 8;
|
|
67
71
|
/** Bottom sticky inset (px) of the framed panel chrome. */
|
|
68
72
|
const PANEL_BOTTOM_INSET = exports.PANEL_BOTTOM_INSET = 8;
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Viewport width (px) at which a RESPONSIVE `ContentPanel` (`framed` undefined)
|
|
76
|
+
* switches from full-bleed to framed. Each value maps to a PRE-SHIPPED literal
|
|
77
|
+
* Tailwind class bundle — `768` (default) uses the named `md:`/`max-md:` screens
|
|
78
|
+
* (byte-identical to prior behavior), `640` uses `sm:`/`max-sm:`, `1024` uses
|
|
79
|
+
* `lg:`/`max-lg:`, and `500` uses the arbitrary `min-[500px]:`/`max-[500px]:`
|
|
80
|
+
* variants (there is no named screen at 500px). The breakpoint tokens are never
|
|
81
|
+
* built at runtime, so a consumer's Tailwind content-scan over `lib/**` resolves
|
|
82
|
+
* them verbatim.
|
|
83
|
+
*/
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Per-breakpoint literal Tailwind variant tokens for the WEB responsive mode.
|
|
87
|
+
* Each field is a WHOLE literal class string (never assembled from parts at
|
|
88
|
+
* runtime) so the consumer's content-scan over `lib/**` picks up every
|
|
89
|
+
* arbitrary/`min-*`/`max-*`/`web:` token verbatim. Selecting a bundle by
|
|
90
|
+
* `framedFrom` value is the only runtime decision:
|
|
91
|
+
* - `surface` → framing rounding on the surface at/above the breakpoint.
|
|
92
|
+
* - `content` → the same rounding + a web-only horizontal clip on the
|
|
93
|
+
* inner content wrapper (`web:<bp>:overflow-x-clip`, platform-first-then-
|
|
94
|
+
* breakpoint — matching the consumer convention `web:sm:flex` in `@mercaria/ui`).
|
|
95
|
+
* - `overlayHidden` → `display:none` for the sticky overlays BELOW the breakpoint.
|
|
96
|
+
* `min-[500px]:` (≥500) and `max-[500px]:` (<500) are exactly complementary
|
|
97
|
+
* (meet at 500, no overlap/gap), mirroring the named `md:`/`max-md:` pair.
|
|
98
|
+
*/
|
|
99
|
+
const RESPONSIVE_WEB = {
|
|
100
|
+
500: {
|
|
101
|
+
surface: 'min-[500px]:rounded-radius-28',
|
|
102
|
+
content: 'min-[500px]:rounded-radius-28 web:min-[500px]:overflow-x-clip',
|
|
103
|
+
overlayHidden: 'max-[500px]:hidden'
|
|
104
|
+
},
|
|
105
|
+
640: {
|
|
106
|
+
surface: 'sm:rounded-radius-28',
|
|
107
|
+
content: 'sm:rounded-radius-28 web:sm:overflow-x-clip',
|
|
108
|
+
overlayHidden: 'max-sm:hidden'
|
|
109
|
+
},
|
|
110
|
+
768: {
|
|
111
|
+
surface: 'md:rounded-radius-28',
|
|
112
|
+
content: 'md:rounded-radius-28 web:md:overflow-x-clip',
|
|
113
|
+
overlayHidden: 'max-md:hidden'
|
|
114
|
+
},
|
|
115
|
+
1024: {
|
|
116
|
+
surface: 'lg:rounded-radius-28',
|
|
117
|
+
content: 'lg:rounded-radius-28 web:lg:overflow-x-clip',
|
|
118
|
+
overlayHidden: 'max-lg:hidden'
|
|
119
|
+
}
|
|
120
|
+
};
|
|
69
121
|
const ContentPanelComponent = ({
|
|
70
122
|
children,
|
|
71
123
|
framed,
|
|
124
|
+
framedFrom = 768,
|
|
72
125
|
surfaceClassName,
|
|
73
126
|
surfaceStyle,
|
|
74
127
|
contentClassName,
|
|
@@ -87,13 +140,16 @@ const ContentPanelComponent = ({
|
|
|
87
140
|
// `false` → never framed (full-bleed).
|
|
88
141
|
const responsive = framed === undefined;
|
|
89
142
|
const showOverlays = framed !== false;
|
|
143
|
+
// Responsive mode selects a pre-shipped literal breakpoint bundle by value;
|
|
144
|
+
// `framed === true`/`false` ignore `framedFrom` (fixed always/never framing).
|
|
145
|
+
const bp = RESPONSIVE_WEB[framedFrom];
|
|
90
146
|
|
|
91
147
|
// Whole literal class strings selected per mode (the Tailwind content-scan
|
|
92
|
-
// over `lib/**` requires each arbitrary/`
|
|
93
|
-
// `
|
|
94
|
-
//
|
|
95
|
-
const surfaceBase = responsive ?
|
|
96
|
-
const contentBase = responsive ?
|
|
148
|
+
// over `lib/**` requires each arbitrary/`min-*`/`max-*`/`web:` token stay
|
|
149
|
+
// literal — the breakpoint tokens live whole in `RESPONSIVE_WEB` above and are
|
|
150
|
+
// only spliced in, never assembled from parts).
|
|
151
|
+
const surfaceBase = responsive ? `flex-1 ${bp.surface}` : framed ? 'flex-1 rounded-radius-28' : 'flex-1';
|
|
152
|
+
const contentBase = responsive ? `flex-1 ${bp.content}` : framed ? 'flex-1 rounded-radius-28 web:overflow-x-clip' : 'flex-1';
|
|
97
153
|
const surfaceClass = [surfaceBase, surfaceClassName ?? 'bg-card'].join(' ');
|
|
98
154
|
const contentClass = [contentBase, contentClassName].filter(Boolean).join(' ');
|
|
99
155
|
return /*#__PURE__*/(0, _jsxRuntime.jsx)(_nestingContext.ContentPanelNestingContext.Provider, {
|
|
@@ -103,13 +159,13 @@ const ContentPanelComponent = ({
|
|
|
103
159
|
style: surfaceStyle,
|
|
104
160
|
children: [showOverlays && /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
|
|
105
161
|
pointerEvents: "none",
|
|
106
|
-
className: responsive ?
|
|
162
|
+
className: responsive ? `web:sticky web:top-2 z-30 h-[calc(100dvh-16px)] w-full rounded-radius-28 ${bp.overlayHidden} web:[margin-bottom:calc(-100dvh+16px)] web:[clip-path:inset(-12px)]` : '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)]',
|
|
107
163
|
style: {
|
|
108
164
|
boxShadow: `0 0 0 ${GUTTER_MASK_SPREAD}px ${maskColor ?? colors.background}`
|
|
109
165
|
}
|
|
110
166
|
}, "bleed-mask"), showOverlays && showStickyFrame !== false && /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
|
|
111
167
|
pointerEvents: "none",
|
|
112
|
-
className: responsive ?
|
|
168
|
+
className: responsive ? `web:sticky web:top-2 z-[120] h-[calc(100dvh-16px)] w-full rounded-radius-28 border border-border ${bp.overlayHidden} web:[margin-bottom:calc(-100dvh+16px)]` : '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)]'
|
|
113
169
|
}, "border-frame"), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
|
|
114
170
|
className: contentClass,
|
|
115
171
|
style: contentStyle,
|
|
@@ -1 +1 @@
|
|
|
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","responsive","undefined","showOverlays","surfaceBase","contentBase","surfaceClass","join","contentClass","filter","Boolean","jsx","ContentPanelNestingContext","Provider","value","jsxs","View","className","style","pointerEvents","boxShadow","background","ContentPanel","memo","displayName"],"sourceRoot":"../../../src","sources":["content-panel/index.web.tsx"],"mappings":";;;;;;
|
|
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","RESPONSIVE_WEB","surface","content","overlayHidden","ContentPanelComponent","children","framed","framedFrom","surfaceClassName","surfaceStyle","contentClassName","contentStyle","showStickyFrame","maskColor","useContentPanelNestingGuard","colors","useTheme","responsive","undefined","showOverlays","bp","surfaceBase","contentBase","surfaceClass","join","contentClass","filter","Boolean","jsx","ContentPanelNestingContext","Provider","value","jsxs","View","className","style","pointerEvents","boxShadow","background","ContentPanel","memo","displayName"],"sourceRoot":"../../../src","sources":["content-panel/index.web.tsx"],"mappings":";;;;;;AAqDA,IAAAA,MAAA,GAAAC,uBAAA,CAAAC,OAAA;AACA,IAAAC,YAAA,GAAAD,OAAA;AAEA,IAAAE,SAAA,GAAAF,OAAA;AACA,IAAAG,eAAA,GAAAH,OAAA;AAG2B,IAAAI,WAAA,GAAAJ,OAAA;AAAA,SAAAD,wBAAAM,CAAA,EAAAC,CAAA,6BAAAC,OAAA,MAAAC,CAAA,OAAAD,OAAA,IAAAE,CAAA,OAAAF,OAAA,YAAAR,uBAAA,YAAAA,CAAAM,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;AA5D3B;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAUA;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;;AAEnC;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAyCA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,MAAMC,cAGL,GAAG;EACF,GAAG,EAAE;IACHC,OAAO,EAAE,+BAA+B;IACxCC,OAAO,EAAE,+DAA+D;IACxEC,aAAa,EAAE;EACjB,CAAC;EACD,GAAG,EAAE;IACHF,OAAO,EAAE,sBAAsB;IAC/BC,OAAO,EAAE,6CAA6C;IACtDC,aAAa,EAAE;EACjB,CAAC;EACD,GAAG,EAAE;IACHF,OAAO,EAAE,sBAAsB;IAC/BC,OAAO,EAAE,6CAA6C;IACtDC,aAAa,EAAE;EACjB,CAAC;EACD,IAAI,EAAE;IACJF,OAAO,EAAE,sBAAsB;IAC/BC,OAAO,EAAE,6CAA6C;IACtDC,aAAa,EAAE;EACjB;AACF,CAAC;AAED,MAAMC,qBAAkD,GAAGA,CAAC;EAC1DC,QAAQ;EACRC,MAAM;EACNC,UAAU,GAAG,GAAG;EAChBC,gBAAgB;EAChBC,YAAY;EACZC,gBAAgB;EAChBC,YAAY;EACZC,eAAe;EACfC;AACF,CAAC,KAAK;EACJ;EACA;EACA,IAAAC,2CAA2B,EAAC,CAAC;EAC7B,MAAM;IAAEC;EAAO,CAAC,GAAG,IAAAC,kBAAQ,EAAC,CAAC;;EAE7B;EACA;EACA,MAAMC,UAAU,GAAGX,MAAM,KAAKY,SAAS;EACvC,MAAMC,YAAY,GAAGb,MAAM,KAAK,KAAK;EACrC;EACA;EACA,MAAMc,EAAE,GAAGpB,cAAc,CAACO,UAAU,CAAC;;EAErC;EACA;EACA;EACA;EACA,MAAMc,WAAW,GAAGJ,UAAU,GAC1B,UAAUG,EAAE,CAACnB,OAAO,EAAE,GACtBK,MAAM,GACJ,0BAA0B,GAC1B,QAAQ;EACd,MAAMgB,WAAW,GAAGL,UAAU,GAC1B,UAAUG,EAAE,CAAClB,OAAO,EAAE,GACtBI,MAAM,GACJ,8CAA8C,GAC9C,QAAQ;EACd,MAAMiB,YAAY,GAAG,CAACF,WAAW,EAAEb,gBAAgB,IAAI,SAAS,CAAC,CAACgB,IAAI,CAAC,GAAG,CAAC;EAC3E,MAAMC,YAAY,GAAG,CAACH,WAAW,EAAEZ,gBAAgB,CAAC,CAACgB,MAAM,CAACC,OAAO,CAAC,CAACH,IAAI,CAAC,GAAG,CAAC;EAE9E,oBACE,IAAAhD,WAAA,CAAAoD,GAAA,EAACrD,eAAA,CAAAsD,0BAA0B,CAACC,QAAQ;IAACC,KAAK,EAAE,IAAK;IAAA1B,QAAA,eAC/C,IAAA7B,WAAA,CAAAwD,IAAA,EAAC3D,YAAA,CAAA4D,IAAI;MACIC,SAAS,EAAEX,YAAY;MAC9BY,KAAK,EAAE1B,YAAa;MAAAJ,QAAA,GAKnBc,YAAY,iBACX,IAAA3C,WAAA,CAAAoD,GAAA,EAACvD,YAAA,CAAA4D,IAAI;QAEHG,aAAa,EAAC,MAAM;QAElBF,SAAS,EAAEjB,UAAU,GACjB,4EAA4EG,EAAE,CAACjB,aAAa,sEAAsE,GAClK,8IAA8I;QAEpJgC,KAAK,EAAE;UAAEE,SAAS,EAAE,SAASzC,kBAAkB,MAAMiB,SAAS,IAAIE,MAAM,CAACuB,UAAU;QAAG;MAAE,GAPpF,YAQL,CACF,EAGAnB,YAAY,IAAIP,eAAe,KAAK,KAAK,iBACxC,IAAApC,WAAA,CAAAoD,GAAA,EAACvD,YAAA,CAAA4D,IAAI;QAEHG,aAAa,EAAC,MAAM;QAElBF,SAAS,EAAEjB,UAAU,GACjB,oGAAoGG,EAAE,CAACjB,aAAa,yCAAyC,GAC7J;MAAyI,GAL3I,cAOL,CACF,eAKD,IAAA3B,WAAA,CAAAoD,GAAA,EAACvD,YAAA,CAAA4D,IAAI;QAEIC,SAAS,EAAET,YAAY;QAC9BU,KAAK,EAAExB,YAAa;QAAAN,QAAA,EAEnBA;MAAQ,GAJL,SAKA,CAAC;IAAA,CACH;EAAC,CAC4B,CAAC;AAE1C,CAAC;AAEM,MAAMkC,YAAY,GAAA1C,OAAA,CAAA0C,YAAA,gBAAG,IAAAC,WAAI,EAACpC,qBAAqB,CAAC;AACvDmC,YAAY,CAACE,WAAW,GAAG,cAAc","ignoreList":[]}
|
|
@@ -13,11 +13,12 @@
|
|
|
13
13
|
* scroll, no sticky positioning, and no bleed to mask, so the panel is simply a
|
|
14
14
|
* surface wrapping its content. The `framed` prop still drives whether that
|
|
15
15
|
* surface is rounded + bordered, tri-state and resolved with pure NativeWind
|
|
16
|
-
* (`
|
|
17
|
-
* hook needed):
|
|
16
|
+
* (the breakpoint's `min-*:` variant evaluates against the window width on
|
|
17
|
+
* native too — no JS breakpoint hook needed):
|
|
18
18
|
*
|
|
19
|
-
* - `undefined` (DEFAULT) → responsive: full-bleed below `
|
|
20
|
-
*
|
|
19
|
+
* - `undefined` (DEFAULT) → responsive: full-bleed below the `framedFrom`
|
|
20
|
+
* breakpoint (default `768` / Tailwind `md`), rounded + bordered at/above it
|
|
21
|
+
* (e.g. `md:rounded-radius-28 md:border md:border-border`).
|
|
21
22
|
* - `false` → never framed (plain full-bleed at every size).
|
|
22
23
|
* - `true` → always rounded + bordered.
|
|
23
24
|
*
|
|
@@ -45,9 +46,34 @@ export const GUTTER_MASK_SPREAD = 40;
|
|
|
45
46
|
export const PANEL_TOP_INSET = 8;
|
|
46
47
|
/** Bottom sticky inset (px) of the framed panel chrome. */
|
|
47
48
|
export const PANEL_BOTTOM_INSET = 8;
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Viewport width (px) at which a RESPONSIVE `ContentPanel` (`framed` undefined)
|
|
52
|
+
* switches from full-bleed to framed. Each value maps to a PRE-SHIPPED literal
|
|
53
|
+
* Tailwind class bundle — `768` (default) uses the named `md:` screen
|
|
54
|
+
* (byte-identical to prior behavior), `640` uses `sm:`, `1024` uses `lg:`, and
|
|
55
|
+
* `500` uses the arbitrary `min-[500px]:` variant (there is no named screen at
|
|
56
|
+
* 500px). The breakpoint tokens are never built at runtime, so a consumer's
|
|
57
|
+
* Tailwind content-scan over `src/**`/`lib/**` resolves them verbatim.
|
|
58
|
+
*/
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Per-breakpoint literal Tailwind surface class bundle for the NATIVE responsive
|
|
62
|
+
* mode. Each value is a WHOLE literal string (never assembled from parts at
|
|
63
|
+
* runtime) so the Tailwind content-scan over `src/**`/`lib/**` picks up every
|
|
64
|
+
* `min-*`/`sm:`/`md:`/`lg:` token verbatim. Selecting a bundle by `framedFrom`
|
|
65
|
+
* value is the only runtime decision.
|
|
66
|
+
*/
|
|
67
|
+
const RESPONSIVE_SURFACE = {
|
|
68
|
+
500: 'min-[500px]:overflow-hidden min-[500px]:rounded-radius-28 min-[500px]:border min-[500px]:border-border',
|
|
69
|
+
640: 'sm:overflow-hidden sm:rounded-radius-28 sm:border sm:border-border',
|
|
70
|
+
768: 'md:overflow-hidden md:rounded-radius-28 md:border md:border-border',
|
|
71
|
+
1024: 'lg:overflow-hidden lg:rounded-radius-28 lg:border lg:border-border'
|
|
72
|
+
};
|
|
48
73
|
const ContentPanelComponent = ({
|
|
49
74
|
children,
|
|
50
75
|
framed,
|
|
76
|
+
framedFrom = 768,
|
|
51
77
|
surfaceClassName,
|
|
52
78
|
surfaceStyle,
|
|
53
79
|
contentClassName,
|
|
@@ -56,11 +82,13 @@ const ContentPanelComponent = ({
|
|
|
56
82
|
// Dev-only invariant — a ContentPanel must never be nested inside another.
|
|
57
83
|
useContentPanelNestingGuard();
|
|
58
84
|
|
|
59
|
-
// Tri-state: `undefined` → responsive (
|
|
60
|
-
// `false` → never framed (plain full-bleed). Whole literal class
|
|
61
|
-
// selected per mode so the Tailwind content-scan over `src/**`
|
|
62
|
-
// `md:` / `rounded-radius-28` token verbatim
|
|
63
|
-
|
|
85
|
+
// Tri-state: `undefined` → responsive (breakpoint-gated), `true` → always
|
|
86
|
+
// framed, `false` → never framed (plain full-bleed). Whole literal class
|
|
87
|
+
// strings are selected per mode so the Tailwind content-scan over `src/**`
|
|
88
|
+
// picks up every `min-*`/`sm:`/`md:`/`lg:` / `rounded-radius-28` token verbatim
|
|
89
|
+
// (the breakpoint bundle lives whole in `RESPONSIVE_SURFACE`; `framed ===
|
|
90
|
+
// true`/`false` ignore `framedFrom` for fixed always/never framing).
|
|
91
|
+
const surfaceBase = framed === undefined ? `flex-1 ${RESPONSIVE_SURFACE[framedFrom]}` : framed ? 'flex-1 overflow-hidden rounded-radius-28 border border-border' : 'flex-1';
|
|
64
92
|
const surfaceClass = [surfaceBase, surfaceClassName ?? 'bg-card'].join(' ');
|
|
65
93
|
const contentClass = ['flex-1', contentClassName].filter(Boolean).join(' ');
|
|
66
94
|
return /*#__PURE__*/_jsx(ContentPanelNestingContext.Provider, {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["React","memo","View","ContentPanelNestingContext","useContentPanelNestingGuard","jsx","_jsx","GUTTER_MASK_SPREAD","PANEL_TOP_INSET","PANEL_BOTTOM_INSET","ContentPanelComponent","children","framed","surfaceClassName","surfaceStyle","contentClassName","contentStyle","surfaceBase","undefined","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;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;
|
|
1
|
+
{"version":3,"names":["React","memo","View","ContentPanelNestingContext","useContentPanelNestingGuard","jsx","_jsx","GUTTER_MASK_SPREAD","PANEL_TOP_INSET","PANEL_BOTTOM_INSET","RESPONSIVE_SURFACE","ContentPanelComponent","children","framed","framedFrom","surfaceClassName","surfaceStyle","contentClassName","contentStyle","surfaceBase","undefined","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;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;;AAEnC;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAuCA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,MAAMC,kBAAgE,GAAG;EACvE,GAAG,EAAE,wGAAwG;EAC7G,GAAG,EAAE,oEAAoE;EACzE,GAAG,EAAE,oEAAoE;EACzE,IAAI,EAAE;AACR,CAAC;AAED,MAAMC,qBAAkD,GAAGA,CAAC;EAC1DC,QAAQ;EACRC,MAAM;EACNC,UAAU,GAAG,GAAG;EAChBC,gBAAgB;EAChBC,YAAY;EACZC,gBAAgB;EAChBC;AACF,CAAC,KAAK;EACJ;EACAd,2BAA2B,CAAC,CAAC;;EAE7B;EACA;EACA;EACA;EACA;EACA;EACA,MAAMe,WAAW,GACfN,MAAM,KAAKO,SAAS,GAChB,UAAUV,kBAAkB,CAACI,UAAU,CAAC,EAAE,GAC1CD,MAAM,GACJ,+DAA+D,GAC/D,QAAQ;EAChB,MAAMQ,YAAY,GAAG,CAACF,WAAW,EAAEJ,gBAAgB,IAAI,SAAS,CAAC,CAACO,IAAI,CAAC,GAAG,CAAC;EAC3E,MAAMC,YAAY,GAAG,CAAC,QAAQ,EAAEN,gBAAgB,CAAC,CAACO,MAAM,CAACC,OAAO,CAAC,CAACH,IAAI,CAAC,GAAG,CAAC;EAE3E,oBACEhB,IAAA,CAACH,0BAA0B,CAACuB,QAAQ;IAACC,KAAK,EAAE,IAAK;IAAAf,QAAA,eAC/CN,IAAA,CAACJ,IAAI;MACI0B,SAAS,EAAEP,YAAY;MAC9BQ,KAAK,EAAEb,YAAa;MAAAJ,QAAA,eAEpBN,IAAA,CAACJ,IAAI;QACI0B,SAAS,EAAEL,YAAY;QAC9BM,KAAK,EAAEX,YAAa;QAAAN,QAAA,EAEnBA;MAAQ,CACL;IAAC,CACH;EAAC,CAC4B,CAAC;AAE1C,CAAC;AAED,OAAO,MAAMkB,YAAY,gBAAG7B,IAAI,CAACU,qBAAqB,CAAC;AACvDmB,YAAY,CAACC,WAAW,GAAG,cAAc","ignoreList":[]}
|
|
@@ -28,18 +28,22 @@
|
|
|
28
28
|
* Framing is tri-state via the `framed` prop:
|
|
29
29
|
*
|
|
30
30
|
* - `undefined` (DEFAULT) → RESPONSIVE, driven purely by NativeWind: full-bleed
|
|
31
|
-
* below `
|
|
32
|
-
*
|
|
33
|
-
*
|
|
31
|
+
* below the `framedFrom` breakpoint, framed at/above it. The breakpoint is
|
|
32
|
+
* configurable per consumer (`framedFrom`, default `768` / Tailwind `md`); the
|
|
33
|
+
* rounding is gated by that breakpoint's `min-*:` variant and both overlays
|
|
34
|
+
* are always rendered but carry the matching `max-*:hidden` literal
|
|
35
|
+
* (`display:none` below the breakpoint), so the breakpoint is decided entirely
|
|
36
|
+
* in CSS — no consumer JS breakpoint hook.
|
|
34
37
|
* - `false` → NEVER framed (full-bleed at every size): a single flat surface,
|
|
35
38
|
* no rounding, no overlays.
|
|
36
39
|
* - `true` → ALWAYS framed (rounded + overlays at every size).
|
|
37
40
|
*
|
|
38
|
-
* The overlays are gated by VISIBILITY (a `max
|
|
39
|
-
* by conditional mounting for the responsive case, so
|
|
40
|
-
* never remounts them. The content wrapper likewise
|
|
41
|
-
* `{children}` reconciles in place across the breakpoint
|
|
42
|
-
* (which would reset feed scroll / the virtualizer +
|
|
41
|
+
* The overlays are gated by VISIBILITY (a `max-*:hidden` literal for the chosen
|
|
42
|
+
* breakpoint) rather than by conditional mounting for the responsive case, so
|
|
43
|
+
* crossing the breakpoint never remounts them. The content wrapper likewise
|
|
44
|
+
* keeps a stable `key` so `{children}` reconciles in place across the breakpoint
|
|
45
|
+
* instead of remounting (which would reset feed scroll / the virtualizer +
|
|
46
|
+
* refetch).
|
|
43
47
|
*
|
|
44
48
|
* Native bundlers use `./index.tsx` (a plain rounded surface); web bundlers
|
|
45
49
|
* select this file via the `"browser"` export condition in `package.json`.
|
|
@@ -61,9 +65,58 @@ export const GUTTER_MASK_SPREAD = 40;
|
|
|
61
65
|
export const PANEL_TOP_INSET = 8;
|
|
62
66
|
/** Bottom sticky inset (px) of the framed panel chrome. */
|
|
63
67
|
export const PANEL_BOTTOM_INSET = 8;
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Viewport width (px) at which a RESPONSIVE `ContentPanel` (`framed` undefined)
|
|
71
|
+
* switches from full-bleed to framed. Each value maps to a PRE-SHIPPED literal
|
|
72
|
+
* Tailwind class bundle — `768` (default) uses the named `md:`/`max-md:` screens
|
|
73
|
+
* (byte-identical to prior behavior), `640` uses `sm:`/`max-sm:`, `1024` uses
|
|
74
|
+
* `lg:`/`max-lg:`, and `500` uses the arbitrary `min-[500px]:`/`max-[500px]:`
|
|
75
|
+
* variants (there is no named screen at 500px). The breakpoint tokens are never
|
|
76
|
+
* built at runtime, so a consumer's Tailwind content-scan over `lib/**` resolves
|
|
77
|
+
* them verbatim.
|
|
78
|
+
*/
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Per-breakpoint literal Tailwind variant tokens for the WEB responsive mode.
|
|
82
|
+
* Each field is a WHOLE literal class string (never assembled from parts at
|
|
83
|
+
* runtime) so the consumer's content-scan over `lib/**` picks up every
|
|
84
|
+
* arbitrary/`min-*`/`max-*`/`web:` token verbatim. Selecting a bundle by
|
|
85
|
+
* `framedFrom` value is the only runtime decision:
|
|
86
|
+
* - `surface` → framing rounding on the surface at/above the breakpoint.
|
|
87
|
+
* - `content` → the same rounding + a web-only horizontal clip on the
|
|
88
|
+
* inner content wrapper (`web:<bp>:overflow-x-clip`, platform-first-then-
|
|
89
|
+
* breakpoint — matching the consumer convention `web:sm:flex` in `@mercaria/ui`).
|
|
90
|
+
* - `overlayHidden` → `display:none` for the sticky overlays BELOW the breakpoint.
|
|
91
|
+
* `min-[500px]:` (≥500) and `max-[500px]:` (<500) are exactly complementary
|
|
92
|
+
* (meet at 500, no overlap/gap), mirroring the named `md:`/`max-md:` pair.
|
|
93
|
+
*/
|
|
94
|
+
const RESPONSIVE_WEB = {
|
|
95
|
+
500: {
|
|
96
|
+
surface: 'min-[500px]:rounded-radius-28',
|
|
97
|
+
content: 'min-[500px]:rounded-radius-28 web:min-[500px]:overflow-x-clip',
|
|
98
|
+
overlayHidden: 'max-[500px]:hidden'
|
|
99
|
+
},
|
|
100
|
+
640: {
|
|
101
|
+
surface: 'sm:rounded-radius-28',
|
|
102
|
+
content: 'sm:rounded-radius-28 web:sm:overflow-x-clip',
|
|
103
|
+
overlayHidden: 'max-sm:hidden'
|
|
104
|
+
},
|
|
105
|
+
768: {
|
|
106
|
+
surface: 'md:rounded-radius-28',
|
|
107
|
+
content: 'md:rounded-radius-28 web:md:overflow-x-clip',
|
|
108
|
+
overlayHidden: 'max-md:hidden'
|
|
109
|
+
},
|
|
110
|
+
1024: {
|
|
111
|
+
surface: 'lg:rounded-radius-28',
|
|
112
|
+
content: 'lg:rounded-radius-28 web:lg:overflow-x-clip',
|
|
113
|
+
overlayHidden: 'max-lg:hidden'
|
|
114
|
+
}
|
|
115
|
+
};
|
|
64
116
|
const ContentPanelComponent = ({
|
|
65
117
|
children,
|
|
66
118
|
framed,
|
|
119
|
+
framedFrom = 768,
|
|
67
120
|
surfaceClassName,
|
|
68
121
|
surfaceStyle,
|
|
69
122
|
contentClassName,
|
|
@@ -82,13 +135,16 @@ const ContentPanelComponent = ({
|
|
|
82
135
|
// `false` → never framed (full-bleed).
|
|
83
136
|
const responsive = framed === undefined;
|
|
84
137
|
const showOverlays = framed !== false;
|
|
138
|
+
// Responsive mode selects a pre-shipped literal breakpoint bundle by value;
|
|
139
|
+
// `framed === true`/`false` ignore `framedFrom` (fixed always/never framing).
|
|
140
|
+
const bp = RESPONSIVE_WEB[framedFrom];
|
|
85
141
|
|
|
86
142
|
// Whole literal class strings selected per mode (the Tailwind content-scan
|
|
87
|
-
// over `lib/**` requires each arbitrary/`
|
|
88
|
-
// `
|
|
89
|
-
//
|
|
90
|
-
const surfaceBase = responsive ?
|
|
91
|
-
const contentBase = responsive ?
|
|
143
|
+
// over `lib/**` requires each arbitrary/`min-*`/`max-*`/`web:` token stay
|
|
144
|
+
// literal — the breakpoint tokens live whole in `RESPONSIVE_WEB` above and are
|
|
145
|
+
// only spliced in, never assembled from parts).
|
|
146
|
+
const surfaceBase = responsive ? `flex-1 ${bp.surface}` : framed ? 'flex-1 rounded-radius-28' : 'flex-1';
|
|
147
|
+
const contentBase = responsive ? `flex-1 ${bp.content}` : framed ? 'flex-1 rounded-radius-28 web:overflow-x-clip' : 'flex-1';
|
|
92
148
|
const surfaceClass = [surfaceBase, surfaceClassName ?? 'bg-card'].join(' ');
|
|
93
149
|
const contentClass = [contentBase, contentClassName].filter(Boolean).join(' ');
|
|
94
150
|
return /*#__PURE__*/_jsx(ContentPanelNestingContext.Provider, {
|
|
@@ -98,13 +154,13 @@ const ContentPanelComponent = ({
|
|
|
98
154
|
style: surfaceStyle,
|
|
99
155
|
children: [showOverlays && /*#__PURE__*/_jsx(View, {
|
|
100
156
|
pointerEvents: "none",
|
|
101
|
-
className: responsive ?
|
|
157
|
+
className: responsive ? `web:sticky web:top-2 z-30 h-[calc(100dvh-16px)] w-full rounded-radius-28 ${bp.overlayHidden} web:[margin-bottom:calc(-100dvh+16px)] web:[clip-path:inset(-12px)]` : '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)]',
|
|
102
158
|
style: {
|
|
103
159
|
boxShadow: `0 0 0 ${GUTTER_MASK_SPREAD}px ${maskColor ?? colors.background}`
|
|
104
160
|
}
|
|
105
161
|
}, "bleed-mask"), showOverlays && showStickyFrame !== false && /*#__PURE__*/_jsx(View, {
|
|
106
162
|
pointerEvents: "none",
|
|
107
|
-
className: responsive ?
|
|
163
|
+
className: responsive ? `web:sticky web:top-2 z-[120] h-[calc(100dvh-16px)] w-full rounded-radius-28 border border-border ${bp.overlayHidden} web:[margin-bottom:calc(-100dvh+16px)]` : '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)]'
|
|
108
164
|
}, "border-frame"), /*#__PURE__*/_jsx(View, {
|
|
109
165
|
className: contentClass,
|
|
110
166
|
style: contentStyle,
|
|
@@ -1 +1 @@
|
|
|
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","responsive","undefined","showOverlays","surfaceBase","contentBase","surfaceClass","join","contentClass","filter","Boolean","Provider","value","className","style","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;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;
|
|
1
|
+
{"version":3,"names":["React","memo","View","useTheme","ContentPanelNestingContext","useContentPanelNestingGuard","jsx","_jsx","jsxs","_jsxs","GUTTER_MASK_SPREAD","PANEL_TOP_INSET","PANEL_BOTTOM_INSET","RESPONSIVE_WEB","surface","content","overlayHidden","ContentPanelComponent","children","framed","framedFrom","surfaceClassName","surfaceStyle","contentClassName","contentStyle","showStickyFrame","maskColor","colors","responsive","undefined","showOverlays","bp","surfaceBase","contentBase","surfaceClass","join","contentClass","filter","Boolean","Provider","value","className","style","pointerEvents","boxShadow","background","ContentPanel","displayName"],"sourceRoot":"../../../src","sources":["content-panel/index.web.tsx"],"mappings":"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","ignoreList":[]}
|
|
@@ -11,11 +11,12 @@
|
|
|
11
11
|
* scroll, no sticky positioning, and no bleed to mask, so the panel is simply a
|
|
12
12
|
* surface wrapping its content. The `framed` prop still drives whether that
|
|
13
13
|
* surface is rounded + bordered, tri-state and resolved with pure NativeWind
|
|
14
|
-
* (`
|
|
15
|
-
* hook needed):
|
|
14
|
+
* (the breakpoint's `min-*:` variant evaluates against the window width on
|
|
15
|
+
* native too — no JS breakpoint hook needed):
|
|
16
16
|
*
|
|
17
|
-
* - `undefined` (DEFAULT) → responsive: full-bleed below `
|
|
18
|
-
*
|
|
17
|
+
* - `undefined` (DEFAULT) → responsive: full-bleed below the `framedFrom`
|
|
18
|
+
* breakpoint (default `768` / Tailwind `md`), rounded + bordered at/above it
|
|
19
|
+
* (e.g. `md:rounded-radius-28 md:border md:border-border`).
|
|
19
20
|
* - `false` → never framed (plain full-bleed at every size).
|
|
20
21
|
* - `true` → always rounded + bordered.
|
|
21
22
|
*
|
|
@@ -40,17 +41,34 @@ export declare const GUTTER_MASK_SPREAD = 40;
|
|
|
40
41
|
export declare const PANEL_TOP_INSET = 8;
|
|
41
42
|
/** Bottom sticky inset (px) of the framed panel chrome. */
|
|
42
43
|
export declare const PANEL_BOTTOM_INSET = 8;
|
|
44
|
+
/**
|
|
45
|
+
* Viewport width (px) at which a RESPONSIVE `ContentPanel` (`framed` undefined)
|
|
46
|
+
* switches from full-bleed to framed. Each value maps to a PRE-SHIPPED literal
|
|
47
|
+
* Tailwind class bundle — `768` (default) uses the named `md:` screen
|
|
48
|
+
* (byte-identical to prior behavior), `640` uses `sm:`, `1024` uses `lg:`, and
|
|
49
|
+
* `500` uses the arbitrary `min-[500px]:` variant (there is no named screen at
|
|
50
|
+
* 500px). The breakpoint tokens are never built at runtime, so a consumer's
|
|
51
|
+
* Tailwind content-scan over `src/**`/`lib/**` resolves them verbatim.
|
|
52
|
+
*/
|
|
53
|
+
export type ContentPanelFramedBreakpoint = 500 | 640 | 768 | 1024;
|
|
43
54
|
export interface ContentPanelProps {
|
|
44
55
|
children: React.ReactNode;
|
|
45
56
|
/**
|
|
46
57
|
* Framing mode. Tri-state, resolved purely with NativeWind — no consumer
|
|
47
58
|
* breakpoint hook needed:
|
|
48
|
-
* - `undefined` (DEFAULT) → responsive: full-bleed below `
|
|
49
|
-
* bordered at
|
|
59
|
+
* - `undefined` (DEFAULT) → responsive: full-bleed below the `framedFrom`
|
|
60
|
+
* breakpoint, rounded + bordered at/above it.
|
|
50
61
|
* - `false` → never framed (plain full-bleed at every size).
|
|
51
62
|
* - `true` → always rounded + bordered.
|
|
52
63
|
*/
|
|
53
64
|
framed?: boolean;
|
|
65
|
+
/**
|
|
66
|
+
* Viewport width at which the RESPONSIVE panel switches from full-bleed to
|
|
67
|
+
* framed. Only meaningful when `framed` is `undefined` (responsive) — ignored
|
|
68
|
+
* when `framed` is `true` (always framed) or `false` (never framed). Defaults
|
|
69
|
+
* to `768` (Tailwind `md`), reproducing the prior fixed behavior exactly.
|
|
70
|
+
*/
|
|
71
|
+
framedFrom?: ContentPanelFramedBreakpoint;
|
|
54
72
|
/** Override the surface background utility (defaults to `bg-card`). */
|
|
55
73
|
surfaceClassName?: string;
|
|
56
74
|
surfaceStyle?: StyleProp<ViewStyle>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/content-panel/index.tsx"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/content-panel/index.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;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;;;;;;;;GAQG;AACH,MAAM,MAAM,4BAA4B,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,IAAI,CAAC;AAElE,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;IAC1B;;;;;;;OAOG;IACH,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB;;;;;OAKG;IACH,UAAU,CAAC,EAAE,4BAA4B,CAAC;IAC1C,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;AA4DD,eAAO,MAAM,YAAY,+CAA8B,CAAC"}
|
|
@@ -26,18 +26,22 @@
|
|
|
26
26
|
* Framing is tri-state via the `framed` prop:
|
|
27
27
|
*
|
|
28
28
|
* - `undefined` (DEFAULT) → RESPONSIVE, driven purely by NativeWind: full-bleed
|
|
29
|
-
* below `
|
|
30
|
-
*
|
|
31
|
-
*
|
|
29
|
+
* below the `framedFrom` breakpoint, framed at/above it. The breakpoint is
|
|
30
|
+
* configurable per consumer (`framedFrom`, default `768` / Tailwind `md`); the
|
|
31
|
+
* rounding is gated by that breakpoint's `min-*:` variant and both overlays
|
|
32
|
+
* are always rendered but carry the matching `max-*:hidden` literal
|
|
33
|
+
* (`display:none` below the breakpoint), so the breakpoint is decided entirely
|
|
34
|
+
* in CSS — no consumer JS breakpoint hook.
|
|
32
35
|
* - `false` → NEVER framed (full-bleed at every size): a single flat surface,
|
|
33
36
|
* no rounding, no overlays.
|
|
34
37
|
* - `true` → ALWAYS framed (rounded + overlays at every size).
|
|
35
38
|
*
|
|
36
|
-
* The overlays are gated by VISIBILITY (a `max
|
|
37
|
-
* by conditional mounting for the responsive case, so
|
|
38
|
-
* never remounts them. The content wrapper likewise
|
|
39
|
-
* `{children}` reconciles in place across the breakpoint
|
|
40
|
-
* (which would reset feed scroll / the virtualizer +
|
|
39
|
+
* The overlays are gated by VISIBILITY (a `max-*:hidden` literal for the chosen
|
|
40
|
+
* breakpoint) rather than by conditional mounting for the responsive case, so
|
|
41
|
+
* crossing the breakpoint never remounts them. The content wrapper likewise
|
|
42
|
+
* keeps a stable `key` so `{children}` reconciles in place across the breakpoint
|
|
43
|
+
* instead of remounting (which would reset feed scroll / the virtualizer +
|
|
44
|
+
* refetch).
|
|
41
45
|
*
|
|
42
46
|
* Native bundlers use `./index.tsx` (a plain rounded surface); web bundlers
|
|
43
47
|
* select this file via the `"browser"` export condition in `package.json`.
|
|
@@ -55,17 +59,36 @@ export declare const GUTTER_MASK_SPREAD = 40;
|
|
|
55
59
|
export declare const PANEL_TOP_INSET = 8;
|
|
56
60
|
/** Bottom sticky inset (px) of the framed panel chrome. */
|
|
57
61
|
export declare const PANEL_BOTTOM_INSET = 8;
|
|
62
|
+
/**
|
|
63
|
+
* Viewport width (px) at which a RESPONSIVE `ContentPanel` (`framed` undefined)
|
|
64
|
+
* switches from full-bleed to framed. Each value maps to a PRE-SHIPPED literal
|
|
65
|
+
* Tailwind class bundle — `768` (default) uses the named `md:`/`max-md:` screens
|
|
66
|
+
* (byte-identical to prior behavior), `640` uses `sm:`/`max-sm:`, `1024` uses
|
|
67
|
+
* `lg:`/`max-lg:`, and `500` uses the arbitrary `min-[500px]:`/`max-[500px]:`
|
|
68
|
+
* variants (there is no named screen at 500px). The breakpoint tokens are never
|
|
69
|
+
* built at runtime, so a consumer's Tailwind content-scan over `lib/**` resolves
|
|
70
|
+
* them verbatim.
|
|
71
|
+
*/
|
|
72
|
+
export type ContentPanelFramedBreakpoint = 500 | 640 | 768 | 1024;
|
|
58
73
|
export interface ContentPanelProps {
|
|
59
74
|
children: React.ReactNode;
|
|
60
75
|
/**
|
|
61
76
|
* Framing mode. Tri-state, resolved purely with NativeWind — no consumer
|
|
62
77
|
* breakpoint hook needed:
|
|
63
|
-
* - `undefined` (DEFAULT) → responsive: full-bleed below `
|
|
64
|
-
*
|
|
78
|
+
* - `undefined` (DEFAULT) → responsive: full-bleed below the `framedFrom`
|
|
79
|
+
* breakpoint, framed at/above it (rounded corners + sticky bleed-mask/border
|
|
80
|
+
* overlays).
|
|
65
81
|
* - `false` → never framed (full-bleed at every size).
|
|
66
82
|
* - `true` → always framed (rounded + overlays at every size).
|
|
67
83
|
*/
|
|
68
84
|
framed?: boolean;
|
|
85
|
+
/**
|
|
86
|
+
* Viewport width at which the RESPONSIVE panel switches from full-bleed to
|
|
87
|
+
* framed. Only meaningful when `framed` is `undefined` (responsive) — ignored
|
|
88
|
+
* when `framed` is `true` (always framed) or `false` (never framed). Defaults
|
|
89
|
+
* to `768` (Tailwind `md`), reproducing the prior fixed behavior exactly.
|
|
90
|
+
*/
|
|
91
|
+
framedFrom?: ContentPanelFramedBreakpoint;
|
|
69
92
|
/** Override the surface background utility (defaults to `bg-card`). */
|
|
70
93
|
surfaceClassName?: string;
|
|
71
94
|
surfaceStyle?: StyleProp<ViewStyle>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.web.d.ts","sourceRoot":"","sources":["../../../../src/content-panel/index.web.tsx"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"index.web.d.ts","sourceRoot":"","sources":["../../../../src/content-panel/index.web.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoDG;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;;;;;;;;;GASG;AACH,MAAM,MAAM,4BAA4B,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,IAAI,CAAC;AAElE,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;IAC1B;;;;;;;;OAQG;IACH,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB;;;;;OAKG;IACH,UAAU,CAAC,EAAE,4BAA4B,CAAC;IAC1C,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;AAqID,eAAO,MAAM,YAAY,+CAA8B,CAAC"}
|
|
@@ -11,11 +11,12 @@
|
|
|
11
11
|
* scroll, no sticky positioning, and no bleed to mask, so the panel is simply a
|
|
12
12
|
* surface wrapping its content. The `framed` prop still drives whether that
|
|
13
13
|
* surface is rounded + bordered, tri-state and resolved with pure NativeWind
|
|
14
|
-
* (`
|
|
15
|
-
* hook needed):
|
|
14
|
+
* (the breakpoint's `min-*:` variant evaluates against the window width on
|
|
15
|
+
* native too — no JS breakpoint hook needed):
|
|
16
16
|
*
|
|
17
|
-
* - `undefined` (DEFAULT) → responsive: full-bleed below `
|
|
18
|
-
*
|
|
17
|
+
* - `undefined` (DEFAULT) → responsive: full-bleed below the `framedFrom`
|
|
18
|
+
* breakpoint (default `768` / Tailwind `md`), rounded + bordered at/above it
|
|
19
|
+
* (e.g. `md:rounded-radius-28 md:border md:border-border`).
|
|
19
20
|
* - `false` → never framed (plain full-bleed at every size).
|
|
20
21
|
* - `true` → always rounded + bordered.
|
|
21
22
|
*
|
|
@@ -40,17 +41,34 @@ export declare const GUTTER_MASK_SPREAD = 40;
|
|
|
40
41
|
export declare const PANEL_TOP_INSET = 8;
|
|
41
42
|
/** Bottom sticky inset (px) of the framed panel chrome. */
|
|
42
43
|
export declare const PANEL_BOTTOM_INSET = 8;
|
|
44
|
+
/**
|
|
45
|
+
* Viewport width (px) at which a RESPONSIVE `ContentPanel` (`framed` undefined)
|
|
46
|
+
* switches from full-bleed to framed. Each value maps to a PRE-SHIPPED literal
|
|
47
|
+
* Tailwind class bundle — `768` (default) uses the named `md:` screen
|
|
48
|
+
* (byte-identical to prior behavior), `640` uses `sm:`, `1024` uses `lg:`, and
|
|
49
|
+
* `500` uses the arbitrary `min-[500px]:` variant (there is no named screen at
|
|
50
|
+
* 500px). The breakpoint tokens are never built at runtime, so a consumer's
|
|
51
|
+
* Tailwind content-scan over `src/**`/`lib/**` resolves them verbatim.
|
|
52
|
+
*/
|
|
53
|
+
export type ContentPanelFramedBreakpoint = 500 | 640 | 768 | 1024;
|
|
43
54
|
export interface ContentPanelProps {
|
|
44
55
|
children: React.ReactNode;
|
|
45
56
|
/**
|
|
46
57
|
* Framing mode. Tri-state, resolved purely with NativeWind — no consumer
|
|
47
58
|
* breakpoint hook needed:
|
|
48
|
-
* - `undefined` (DEFAULT) → responsive: full-bleed below `
|
|
49
|
-
* bordered at
|
|
59
|
+
* - `undefined` (DEFAULT) → responsive: full-bleed below the `framedFrom`
|
|
60
|
+
* breakpoint, rounded + bordered at/above it.
|
|
50
61
|
* - `false` → never framed (plain full-bleed at every size).
|
|
51
62
|
* - `true` → always rounded + bordered.
|
|
52
63
|
*/
|
|
53
64
|
framed?: boolean;
|
|
65
|
+
/**
|
|
66
|
+
* Viewport width at which the RESPONSIVE panel switches from full-bleed to
|
|
67
|
+
* framed. Only meaningful when `framed` is `undefined` (responsive) — ignored
|
|
68
|
+
* when `framed` is `true` (always framed) or `false` (never framed). Defaults
|
|
69
|
+
* to `768` (Tailwind `md`), reproducing the prior fixed behavior exactly.
|
|
70
|
+
*/
|
|
71
|
+
framedFrom?: ContentPanelFramedBreakpoint;
|
|
54
72
|
/** Override the surface background utility (defaults to `bg-card`). */
|
|
55
73
|
surfaceClassName?: string;
|
|
56
74
|
surfaceStyle?: StyleProp<ViewStyle>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/content-panel/index.tsx"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/content-panel/index.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;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;;;;;;;;GAQG;AACH,MAAM,MAAM,4BAA4B,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,IAAI,CAAC;AAElE,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;IAC1B;;;;;;;OAOG;IACH,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB;;;;;OAKG;IACH,UAAU,CAAC,EAAE,4BAA4B,CAAC;IAC1C,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;AA4DD,eAAO,MAAM,YAAY,+CAA8B,CAAC"}
|
|
@@ -26,18 +26,22 @@
|
|
|
26
26
|
* Framing is tri-state via the `framed` prop:
|
|
27
27
|
*
|
|
28
28
|
* - `undefined` (DEFAULT) → RESPONSIVE, driven purely by NativeWind: full-bleed
|
|
29
|
-
* below `
|
|
30
|
-
*
|
|
31
|
-
*
|
|
29
|
+
* below the `framedFrom` breakpoint, framed at/above it. The breakpoint is
|
|
30
|
+
* configurable per consumer (`framedFrom`, default `768` / Tailwind `md`); the
|
|
31
|
+
* rounding is gated by that breakpoint's `min-*:` variant and both overlays
|
|
32
|
+
* are always rendered but carry the matching `max-*:hidden` literal
|
|
33
|
+
* (`display:none` below the breakpoint), so the breakpoint is decided entirely
|
|
34
|
+
* in CSS — no consumer JS breakpoint hook.
|
|
32
35
|
* - `false` → NEVER framed (full-bleed at every size): a single flat surface,
|
|
33
36
|
* no rounding, no overlays.
|
|
34
37
|
* - `true` → ALWAYS framed (rounded + overlays at every size).
|
|
35
38
|
*
|
|
36
|
-
* The overlays are gated by VISIBILITY (a `max
|
|
37
|
-
* by conditional mounting for the responsive case, so
|
|
38
|
-
* never remounts them. The content wrapper likewise
|
|
39
|
-
* `{children}` reconciles in place across the breakpoint
|
|
40
|
-
* (which would reset feed scroll / the virtualizer +
|
|
39
|
+
* The overlays are gated by VISIBILITY (a `max-*:hidden` literal for the chosen
|
|
40
|
+
* breakpoint) rather than by conditional mounting for the responsive case, so
|
|
41
|
+
* crossing the breakpoint never remounts them. The content wrapper likewise
|
|
42
|
+
* keeps a stable `key` so `{children}` reconciles in place across the breakpoint
|
|
43
|
+
* instead of remounting (which would reset feed scroll / the virtualizer +
|
|
44
|
+
* refetch).
|
|
41
45
|
*
|
|
42
46
|
* Native bundlers use `./index.tsx` (a plain rounded surface); web bundlers
|
|
43
47
|
* select this file via the `"browser"` export condition in `package.json`.
|
|
@@ -55,17 +59,36 @@ export declare const GUTTER_MASK_SPREAD = 40;
|
|
|
55
59
|
export declare const PANEL_TOP_INSET = 8;
|
|
56
60
|
/** Bottom sticky inset (px) of the framed panel chrome. */
|
|
57
61
|
export declare const PANEL_BOTTOM_INSET = 8;
|
|
62
|
+
/**
|
|
63
|
+
* Viewport width (px) at which a RESPONSIVE `ContentPanel` (`framed` undefined)
|
|
64
|
+
* switches from full-bleed to framed. Each value maps to a PRE-SHIPPED literal
|
|
65
|
+
* Tailwind class bundle — `768` (default) uses the named `md:`/`max-md:` screens
|
|
66
|
+
* (byte-identical to prior behavior), `640` uses `sm:`/`max-sm:`, `1024` uses
|
|
67
|
+
* `lg:`/`max-lg:`, and `500` uses the arbitrary `min-[500px]:`/`max-[500px]:`
|
|
68
|
+
* variants (there is no named screen at 500px). The breakpoint tokens are never
|
|
69
|
+
* built at runtime, so a consumer's Tailwind content-scan over `lib/**` resolves
|
|
70
|
+
* them verbatim.
|
|
71
|
+
*/
|
|
72
|
+
export type ContentPanelFramedBreakpoint = 500 | 640 | 768 | 1024;
|
|
58
73
|
export interface ContentPanelProps {
|
|
59
74
|
children: React.ReactNode;
|
|
60
75
|
/**
|
|
61
76
|
* Framing mode. Tri-state, resolved purely with NativeWind — no consumer
|
|
62
77
|
* breakpoint hook needed:
|
|
63
|
-
* - `undefined` (DEFAULT) → responsive: full-bleed below `
|
|
64
|
-
*
|
|
78
|
+
* - `undefined` (DEFAULT) → responsive: full-bleed below the `framedFrom`
|
|
79
|
+
* breakpoint, framed at/above it (rounded corners + sticky bleed-mask/border
|
|
80
|
+
* overlays).
|
|
65
81
|
* - `false` → never framed (full-bleed at every size).
|
|
66
82
|
* - `true` → always framed (rounded + overlays at every size).
|
|
67
83
|
*/
|
|
68
84
|
framed?: boolean;
|
|
85
|
+
/**
|
|
86
|
+
* Viewport width at which the RESPONSIVE panel switches from full-bleed to
|
|
87
|
+
* framed. Only meaningful when `framed` is `undefined` (responsive) — ignored
|
|
88
|
+
* when `framed` is `true` (always framed) or `false` (never framed). Defaults
|
|
89
|
+
* to `768` (Tailwind `md`), reproducing the prior fixed behavior exactly.
|
|
90
|
+
*/
|
|
91
|
+
framedFrom?: ContentPanelFramedBreakpoint;
|
|
69
92
|
/** Override the surface background utility (defaults to `bg-card`). */
|
|
70
93
|
surfaceClassName?: string;
|
|
71
94
|
surfaceStyle?: StyleProp<ViewStyle>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.web.d.ts","sourceRoot":"","sources":["../../../../src/content-panel/index.web.tsx"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"index.web.d.ts","sourceRoot":"","sources":["../../../../src/content-panel/index.web.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoDG;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;;;;;;;;;GASG;AACH,MAAM,MAAM,4BAA4B,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,IAAI,CAAC;AAElE,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;IAC1B;;;;;;;;OAQG;IACH,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB;;;;;OAKG;IACH,UAAU,CAAC,EAAE,4BAA4B,CAAC;IAC1C,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;AAqID,eAAO,MAAM,YAAY,+CAA8B,CAAC"}
|
package/package.json
CHANGED
|
@@ -4,6 +4,14 @@ import { render } from '@testing-library/react-native';
|
|
|
4
4
|
|
|
5
5
|
import { ContentPanel } from '../content-panel';
|
|
6
6
|
|
|
7
|
+
/** Read the outer surface View's `className` from the rendered native tree. */
|
|
8
|
+
function surfaceClassName(node: ReturnType<typeof render>): string {
|
|
9
|
+
const json = node.toJSON();
|
|
10
|
+
const el = Array.isArray(json) ? json[0] : json;
|
|
11
|
+
const className = el?.props?.className;
|
|
12
|
+
return typeof className === 'string' ? className : '';
|
|
13
|
+
}
|
|
14
|
+
|
|
7
15
|
// Jest resolves `../content-panel` to the native variant (`index.tsx`), which
|
|
8
16
|
// does not read the theme — so these render without a BloomThemeProvider. The
|
|
9
17
|
// nesting guard lives in the shared `nesting-context` module exercised by both
|
|
@@ -50,3 +58,69 @@ describe('ContentPanel nesting guard', () => {
|
|
|
50
58
|
consoleError.mockRestore();
|
|
51
59
|
});
|
|
52
60
|
});
|
|
61
|
+
|
|
62
|
+
// The responsive framing breakpoint is configurable via `framedFrom` and is
|
|
63
|
+
// resolved entirely with literal NativeWind classes. These assert the native
|
|
64
|
+
// surface picks up the pre-shipped literal bundle for each breakpoint value.
|
|
65
|
+
describe('ContentPanel framedFrom breakpoint', () => {
|
|
66
|
+
it('defaults to the md (768) named-screen bundle when omitted', () => {
|
|
67
|
+
const className = surfaceClassName(
|
|
68
|
+
render(
|
|
69
|
+
<ContentPanel>
|
|
70
|
+
<Text>content</Text>
|
|
71
|
+
</ContentPanel>,
|
|
72
|
+
),
|
|
73
|
+
);
|
|
74
|
+
expect(className).toContain('md:rounded-radius-28');
|
|
75
|
+
expect(className).toContain('md:border');
|
|
76
|
+
expect(className).not.toContain('min-[500px]:');
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
it('uses the arbitrary min-[500px] bundle for framedFrom={500}', () => {
|
|
80
|
+
const className = surfaceClassName(
|
|
81
|
+
render(
|
|
82
|
+
<ContentPanel framedFrom={500}>
|
|
83
|
+
<Text>content</Text>
|
|
84
|
+
</ContentPanel>,
|
|
85
|
+
),
|
|
86
|
+
);
|
|
87
|
+
expect(className).toContain('min-[500px]:rounded-radius-28');
|
|
88
|
+
expect(className).toContain('min-[500px]:border');
|
|
89
|
+
expect(className).not.toContain('md:');
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
it('uses named sm (640) and lg (1024) bundles', () => {
|
|
93
|
+
expect(
|
|
94
|
+
surfaceClassName(
|
|
95
|
+
render(
|
|
96
|
+
<ContentPanel framedFrom={640}>
|
|
97
|
+
<Text>content</Text>
|
|
98
|
+
</ContentPanel>,
|
|
99
|
+
),
|
|
100
|
+
),
|
|
101
|
+
).toContain('sm:rounded-radius-28');
|
|
102
|
+
expect(
|
|
103
|
+
surfaceClassName(
|
|
104
|
+
render(
|
|
105
|
+
<ContentPanel framedFrom={1024}>
|
|
106
|
+
<Text>content</Text>
|
|
107
|
+
</ContentPanel>,
|
|
108
|
+
),
|
|
109
|
+
),
|
|
110
|
+
).toContain('lg:rounded-radius-28');
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
it('ignores framedFrom when framed is explicitly true (always framed)', () => {
|
|
114
|
+
const className = surfaceClassName(
|
|
115
|
+
render(
|
|
116
|
+
<ContentPanel framed framedFrom={500}>
|
|
117
|
+
<Text>content</Text>
|
|
118
|
+
</ContentPanel>,
|
|
119
|
+
),
|
|
120
|
+
);
|
|
121
|
+
// Always-framed uses unconditional (non-breakpoint) rounding + border.
|
|
122
|
+
expect(className).toContain('rounded-radius-28');
|
|
123
|
+
expect(className).not.toContain('min-[500px]:');
|
|
124
|
+
expect(className).not.toContain('md:');
|
|
125
|
+
});
|
|
126
|
+
});
|
|
@@ -11,11 +11,12 @@
|
|
|
11
11
|
* scroll, no sticky positioning, and no bleed to mask, so the panel is simply a
|
|
12
12
|
* surface wrapping its content. The `framed` prop still drives whether that
|
|
13
13
|
* surface is rounded + bordered, tri-state and resolved with pure NativeWind
|
|
14
|
-
* (`
|
|
15
|
-
* hook needed):
|
|
14
|
+
* (the breakpoint's `min-*:` variant evaluates against the window width on
|
|
15
|
+
* native too — no JS breakpoint hook needed):
|
|
16
16
|
*
|
|
17
|
-
* - `undefined` (DEFAULT) → responsive: full-bleed below `
|
|
18
|
-
*
|
|
17
|
+
* - `undefined` (DEFAULT) → responsive: full-bleed below the `framedFrom`
|
|
18
|
+
* breakpoint (default `768` / Tailwind `md`), rounded + bordered at/above it
|
|
19
|
+
* (e.g. `md:rounded-radius-28 md:border md:border-border`).
|
|
19
20
|
* - `false` → never framed (plain full-bleed at every size).
|
|
20
21
|
* - `true` → always rounded + bordered.
|
|
21
22
|
*
|
|
@@ -47,17 +48,35 @@ export const PANEL_TOP_INSET = 8;
|
|
|
47
48
|
/** Bottom sticky inset (px) of the framed panel chrome. */
|
|
48
49
|
export const PANEL_BOTTOM_INSET = 8;
|
|
49
50
|
|
|
51
|
+
/**
|
|
52
|
+
* Viewport width (px) at which a RESPONSIVE `ContentPanel` (`framed` undefined)
|
|
53
|
+
* switches from full-bleed to framed. Each value maps to a PRE-SHIPPED literal
|
|
54
|
+
* Tailwind class bundle — `768` (default) uses the named `md:` screen
|
|
55
|
+
* (byte-identical to prior behavior), `640` uses `sm:`, `1024` uses `lg:`, and
|
|
56
|
+
* `500` uses the arbitrary `min-[500px]:` variant (there is no named screen at
|
|
57
|
+
* 500px). The breakpoint tokens are never built at runtime, so a consumer's
|
|
58
|
+
* Tailwind content-scan over `src/**`/`lib/**` resolves them verbatim.
|
|
59
|
+
*/
|
|
60
|
+
export type ContentPanelFramedBreakpoint = 500 | 640 | 768 | 1024;
|
|
61
|
+
|
|
50
62
|
export interface ContentPanelProps {
|
|
51
63
|
children: React.ReactNode;
|
|
52
64
|
/**
|
|
53
65
|
* Framing mode. Tri-state, resolved purely with NativeWind — no consumer
|
|
54
66
|
* breakpoint hook needed:
|
|
55
|
-
* - `undefined` (DEFAULT) → responsive: full-bleed below `
|
|
56
|
-
* bordered at
|
|
67
|
+
* - `undefined` (DEFAULT) → responsive: full-bleed below the `framedFrom`
|
|
68
|
+
* breakpoint, rounded + bordered at/above it.
|
|
57
69
|
* - `false` → never framed (plain full-bleed at every size).
|
|
58
70
|
* - `true` → always rounded + bordered.
|
|
59
71
|
*/
|
|
60
72
|
framed?: boolean;
|
|
73
|
+
/**
|
|
74
|
+
* Viewport width at which the RESPONSIVE panel switches from full-bleed to
|
|
75
|
+
* framed. Only meaningful when `framed` is `undefined` (responsive) — ignored
|
|
76
|
+
* when `framed` is `true` (always framed) or `false` (never framed). Defaults
|
|
77
|
+
* to `768` (Tailwind `md`), reproducing the prior fixed behavior exactly.
|
|
78
|
+
*/
|
|
79
|
+
framedFrom?: ContentPanelFramedBreakpoint;
|
|
61
80
|
/** Override the surface background utility (defaults to `bg-card`). */
|
|
62
81
|
surfaceClassName?: string;
|
|
63
82
|
surfaceStyle?: StyleProp<ViewStyle>;
|
|
@@ -76,9 +95,24 @@ export interface ContentPanelProps {
|
|
|
76
95
|
maskColor?: string;
|
|
77
96
|
}
|
|
78
97
|
|
|
98
|
+
/**
|
|
99
|
+
* Per-breakpoint literal Tailwind surface class bundle for the NATIVE responsive
|
|
100
|
+
* mode. Each value is a WHOLE literal string (never assembled from parts at
|
|
101
|
+
* runtime) so the Tailwind content-scan over `src/**`/`lib/**` picks up every
|
|
102
|
+
* `min-*`/`sm:`/`md:`/`lg:` token verbatim. Selecting a bundle by `framedFrom`
|
|
103
|
+
* value is the only runtime decision.
|
|
104
|
+
*/
|
|
105
|
+
const RESPONSIVE_SURFACE: Record<ContentPanelFramedBreakpoint, string> = {
|
|
106
|
+
500: 'min-[500px]:overflow-hidden min-[500px]:rounded-radius-28 min-[500px]:border min-[500px]:border-border',
|
|
107
|
+
640: 'sm:overflow-hidden sm:rounded-radius-28 sm:border sm:border-border',
|
|
108
|
+
768: 'md:overflow-hidden md:rounded-radius-28 md:border md:border-border',
|
|
109
|
+
1024: 'lg:overflow-hidden lg:rounded-radius-28 lg:border lg:border-border',
|
|
110
|
+
};
|
|
111
|
+
|
|
79
112
|
const ContentPanelComponent: React.FC<ContentPanelProps> = ({
|
|
80
113
|
children,
|
|
81
114
|
framed,
|
|
115
|
+
framedFrom = 768,
|
|
82
116
|
surfaceClassName,
|
|
83
117
|
surfaceStyle,
|
|
84
118
|
contentClassName,
|
|
@@ -87,13 +121,15 @@ const ContentPanelComponent: React.FC<ContentPanelProps> = ({
|
|
|
87
121
|
// Dev-only invariant — a ContentPanel must never be nested inside another.
|
|
88
122
|
useContentPanelNestingGuard();
|
|
89
123
|
|
|
90
|
-
// Tri-state: `undefined` → responsive (
|
|
91
|
-
// `false` → never framed (plain full-bleed). Whole literal class
|
|
92
|
-
// selected per mode so the Tailwind content-scan over `src/**`
|
|
93
|
-
// `md:` / `rounded-radius-28` token verbatim
|
|
124
|
+
// Tri-state: `undefined` → responsive (breakpoint-gated), `true` → always
|
|
125
|
+
// framed, `false` → never framed (plain full-bleed). Whole literal class
|
|
126
|
+
// strings are selected per mode so the Tailwind content-scan over `src/**`
|
|
127
|
+
// picks up every `min-*`/`sm:`/`md:`/`lg:` / `rounded-radius-28` token verbatim
|
|
128
|
+
// (the breakpoint bundle lives whole in `RESPONSIVE_SURFACE`; `framed ===
|
|
129
|
+
// true`/`false` ignore `framedFrom` for fixed always/never framing).
|
|
94
130
|
const surfaceBase =
|
|
95
131
|
framed === undefined
|
|
96
|
-
?
|
|
132
|
+
? `flex-1 ${RESPONSIVE_SURFACE[framedFrom]}`
|
|
97
133
|
: framed
|
|
98
134
|
? 'flex-1 overflow-hidden rounded-radius-28 border border-border'
|
|
99
135
|
: 'flex-1';
|
|
@@ -26,18 +26,22 @@
|
|
|
26
26
|
* Framing is tri-state via the `framed` prop:
|
|
27
27
|
*
|
|
28
28
|
* - `undefined` (DEFAULT) → RESPONSIVE, driven purely by NativeWind: full-bleed
|
|
29
|
-
* below `
|
|
30
|
-
*
|
|
31
|
-
*
|
|
29
|
+
* below the `framedFrom` breakpoint, framed at/above it. The breakpoint is
|
|
30
|
+
* configurable per consumer (`framedFrom`, default `768` / Tailwind `md`); the
|
|
31
|
+
* rounding is gated by that breakpoint's `min-*:` variant and both overlays
|
|
32
|
+
* are always rendered but carry the matching `max-*:hidden` literal
|
|
33
|
+
* (`display:none` below the breakpoint), so the breakpoint is decided entirely
|
|
34
|
+
* in CSS — no consumer JS breakpoint hook.
|
|
32
35
|
* - `false` → NEVER framed (full-bleed at every size): a single flat surface,
|
|
33
36
|
* no rounding, no overlays.
|
|
34
37
|
* - `true` → ALWAYS framed (rounded + overlays at every size).
|
|
35
38
|
*
|
|
36
|
-
* The overlays are gated by VISIBILITY (a `max
|
|
37
|
-
* by conditional mounting for the responsive case, so
|
|
38
|
-
* never remounts them. The content wrapper likewise
|
|
39
|
-
* `{children}` reconciles in place across the breakpoint
|
|
40
|
-
* (which would reset feed scroll / the virtualizer +
|
|
39
|
+
* The overlays are gated by VISIBILITY (a `max-*:hidden` literal for the chosen
|
|
40
|
+
* breakpoint) rather than by conditional mounting for the responsive case, so
|
|
41
|
+
* crossing the breakpoint never remounts them. The content wrapper likewise
|
|
42
|
+
* keeps a stable `key` so `{children}` reconciles in place across the breakpoint
|
|
43
|
+
* instead of remounting (which would reset feed scroll / the virtualizer +
|
|
44
|
+
* refetch).
|
|
41
45
|
*
|
|
42
46
|
* Native bundlers use `./index.tsx` (a plain rounded surface); web bundlers
|
|
43
47
|
* select this file via the `"browser"` export condition in `package.json`.
|
|
@@ -63,17 +67,37 @@ export const PANEL_TOP_INSET = 8;
|
|
|
63
67
|
/** Bottom sticky inset (px) of the framed panel chrome. */
|
|
64
68
|
export const PANEL_BOTTOM_INSET = 8;
|
|
65
69
|
|
|
70
|
+
/**
|
|
71
|
+
* Viewport width (px) at which a RESPONSIVE `ContentPanel` (`framed` undefined)
|
|
72
|
+
* switches from full-bleed to framed. Each value maps to a PRE-SHIPPED literal
|
|
73
|
+
* Tailwind class bundle — `768` (default) uses the named `md:`/`max-md:` screens
|
|
74
|
+
* (byte-identical to prior behavior), `640` uses `sm:`/`max-sm:`, `1024` uses
|
|
75
|
+
* `lg:`/`max-lg:`, and `500` uses the arbitrary `min-[500px]:`/`max-[500px]:`
|
|
76
|
+
* variants (there is no named screen at 500px). The breakpoint tokens are never
|
|
77
|
+
* built at runtime, so a consumer's Tailwind content-scan over `lib/**` resolves
|
|
78
|
+
* them verbatim.
|
|
79
|
+
*/
|
|
80
|
+
export type ContentPanelFramedBreakpoint = 500 | 640 | 768 | 1024;
|
|
81
|
+
|
|
66
82
|
export interface ContentPanelProps {
|
|
67
83
|
children: React.ReactNode;
|
|
68
84
|
/**
|
|
69
85
|
* Framing mode. Tri-state, resolved purely with NativeWind — no consumer
|
|
70
86
|
* breakpoint hook needed:
|
|
71
|
-
* - `undefined` (DEFAULT) → responsive: full-bleed below `
|
|
72
|
-
*
|
|
87
|
+
* - `undefined` (DEFAULT) → responsive: full-bleed below the `framedFrom`
|
|
88
|
+
* breakpoint, framed at/above it (rounded corners + sticky bleed-mask/border
|
|
89
|
+
* overlays).
|
|
73
90
|
* - `false` → never framed (full-bleed at every size).
|
|
74
91
|
* - `true` → always framed (rounded + overlays at every size).
|
|
75
92
|
*/
|
|
76
93
|
framed?: boolean;
|
|
94
|
+
/**
|
|
95
|
+
* Viewport width at which the RESPONSIVE panel switches from full-bleed to
|
|
96
|
+
* framed. Only meaningful when `framed` is `undefined` (responsive) — ignored
|
|
97
|
+
* when `framed` is `true` (always framed) or `false` (never framed). Defaults
|
|
98
|
+
* to `768` (Tailwind `md`), reproducing the prior fixed behavior exactly.
|
|
99
|
+
*/
|
|
100
|
+
framedFrom?: ContentPanelFramedBreakpoint;
|
|
77
101
|
/** Override the surface background utility (defaults to `bg-card`). */
|
|
78
102
|
surfaceClassName?: string;
|
|
79
103
|
surfaceStyle?: StyleProp<ViewStyle>;
|
|
@@ -93,9 +117,50 @@ export interface ContentPanelProps {
|
|
|
93
117
|
maskColor?: string;
|
|
94
118
|
}
|
|
95
119
|
|
|
120
|
+
/**
|
|
121
|
+
* Per-breakpoint literal Tailwind variant tokens for the WEB responsive mode.
|
|
122
|
+
* Each field is a WHOLE literal class string (never assembled from parts at
|
|
123
|
+
* runtime) so the consumer's content-scan over `lib/**` picks up every
|
|
124
|
+
* arbitrary/`min-*`/`max-*`/`web:` token verbatim. Selecting a bundle by
|
|
125
|
+
* `framedFrom` value is the only runtime decision:
|
|
126
|
+
* - `surface` → framing rounding on the surface at/above the breakpoint.
|
|
127
|
+
* - `content` → the same rounding + a web-only horizontal clip on the
|
|
128
|
+
* inner content wrapper (`web:<bp>:overflow-x-clip`, platform-first-then-
|
|
129
|
+
* breakpoint — matching the consumer convention `web:sm:flex` in `@mercaria/ui`).
|
|
130
|
+
* - `overlayHidden` → `display:none` for the sticky overlays BELOW the breakpoint.
|
|
131
|
+
* `min-[500px]:` (≥500) and `max-[500px]:` (<500) are exactly complementary
|
|
132
|
+
* (meet at 500, no overlap/gap), mirroring the named `md:`/`max-md:` pair.
|
|
133
|
+
*/
|
|
134
|
+
const RESPONSIVE_WEB: Record<
|
|
135
|
+
ContentPanelFramedBreakpoint,
|
|
136
|
+
{ surface: string; content: string; overlayHidden: string }
|
|
137
|
+
> = {
|
|
138
|
+
500: {
|
|
139
|
+
surface: 'min-[500px]:rounded-radius-28',
|
|
140
|
+
content: 'min-[500px]:rounded-radius-28 web:min-[500px]:overflow-x-clip',
|
|
141
|
+
overlayHidden: 'max-[500px]:hidden',
|
|
142
|
+
},
|
|
143
|
+
640: {
|
|
144
|
+
surface: 'sm:rounded-radius-28',
|
|
145
|
+
content: 'sm:rounded-radius-28 web:sm:overflow-x-clip',
|
|
146
|
+
overlayHidden: 'max-sm:hidden',
|
|
147
|
+
},
|
|
148
|
+
768: {
|
|
149
|
+
surface: 'md:rounded-radius-28',
|
|
150
|
+
content: 'md:rounded-radius-28 web:md:overflow-x-clip',
|
|
151
|
+
overlayHidden: 'max-md:hidden',
|
|
152
|
+
},
|
|
153
|
+
1024: {
|
|
154
|
+
surface: 'lg:rounded-radius-28',
|
|
155
|
+
content: 'lg:rounded-radius-28 web:lg:overflow-x-clip',
|
|
156
|
+
overlayHidden: 'max-lg:hidden',
|
|
157
|
+
},
|
|
158
|
+
};
|
|
159
|
+
|
|
96
160
|
const ContentPanelComponent: React.FC<ContentPanelProps> = ({
|
|
97
161
|
children,
|
|
98
162
|
framed,
|
|
163
|
+
framedFrom = 768,
|
|
99
164
|
surfaceClassName,
|
|
100
165
|
surfaceStyle,
|
|
101
166
|
contentClassName,
|
|
@@ -112,18 +177,21 @@ const ContentPanelComponent: React.FC<ContentPanelProps> = ({
|
|
|
112
177
|
// `false` → never framed (full-bleed).
|
|
113
178
|
const responsive = framed === undefined;
|
|
114
179
|
const showOverlays = framed !== false;
|
|
180
|
+
// Responsive mode selects a pre-shipped literal breakpoint bundle by value;
|
|
181
|
+
// `framed === true`/`false` ignore `framedFrom` (fixed always/never framing).
|
|
182
|
+
const bp = RESPONSIVE_WEB[framedFrom];
|
|
115
183
|
|
|
116
184
|
// Whole literal class strings selected per mode (the Tailwind content-scan
|
|
117
|
-
// over `lib/**` requires each arbitrary/`
|
|
118
|
-
// `
|
|
119
|
-
//
|
|
185
|
+
// over `lib/**` requires each arbitrary/`min-*`/`max-*`/`web:` token stay
|
|
186
|
+
// literal — the breakpoint tokens live whole in `RESPONSIVE_WEB` above and are
|
|
187
|
+
// only spliced in, never assembled from parts).
|
|
120
188
|
const surfaceBase = responsive
|
|
121
|
-
?
|
|
189
|
+
? `flex-1 ${bp.surface}`
|
|
122
190
|
: framed
|
|
123
191
|
? 'flex-1 rounded-radius-28'
|
|
124
192
|
: 'flex-1';
|
|
125
193
|
const contentBase = responsive
|
|
126
|
-
?
|
|
194
|
+
? `flex-1 ${bp.content}`
|
|
127
195
|
: framed
|
|
128
196
|
? 'flex-1 rounded-radius-28 web:overflow-x-clip'
|
|
129
197
|
: 'flex-1';
|
|
@@ -145,7 +213,7 @@ const ContentPanelComponent: React.FC<ContentPanelProps> = ({
|
|
|
145
213
|
pointerEvents="none"
|
|
146
214
|
{...({
|
|
147
215
|
className: responsive
|
|
148
|
-
?
|
|
216
|
+
? `web:sticky web:top-2 z-30 h-[calc(100dvh-16px)] w-full rounded-radius-28 ${bp.overlayHidden} web:[margin-bottom:calc(-100dvh+16px)] web:[clip-path:inset(-12px)]`
|
|
149
217
|
: '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)]',
|
|
150
218
|
} as Record<string, string>)}
|
|
151
219
|
style={{ boxShadow: `0 0 0 ${GUTTER_MASK_SPREAD}px ${maskColor ?? colors.background}` }}
|
|
@@ -159,7 +227,7 @@ const ContentPanelComponent: React.FC<ContentPanelProps> = ({
|
|
|
159
227
|
pointerEvents="none"
|
|
160
228
|
{...({
|
|
161
229
|
className: responsive
|
|
162
|
-
?
|
|
230
|
+
? `web:sticky web:top-2 z-[120] h-[calc(100dvh-16px)] w-full rounded-radius-28 border border-border ${bp.overlayHidden} web:[margin-bottom:calc(-100dvh+16px)]`
|
|
163
231
|
: '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)]',
|
|
164
232
|
} as Record<string, string>)}
|
|
165
233
|
/>
|