@oxyhq/bloom 0.20.0 → 0.22.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.
- package/lib/commonjs/content-panel/index.js +64 -0
- package/lib/commonjs/content-panel/index.js.map +1 -0
- package/lib/commonjs/content-panel/index.web.js +100 -0
- package/lib/commonjs/content-panel/index.web.js.map +1 -0
- package/lib/module/content-panel/index.js +59 -0
- package/lib/module/content-panel/index.js.map +1 -0
- package/lib/module/content-panel/index.web.js +95 -0
- package/lib/module/content-panel/index.web.js.map +1 -0
- package/lib/typescript/commonjs/content-panel/index.d.ts +59 -0
- package/lib/typescript/commonjs/content-panel/index.d.ts.map +1 -0
- package/lib/typescript/commonjs/content-panel/index.web.d.ts +68 -0
- package/lib/typescript/commonjs/content-panel/index.web.d.ts.map +1 -0
- package/lib/typescript/module/content-panel/index.d.ts +59 -0
- package/lib/typescript/module/content-panel/index.d.ts.map +1 -0
- package/lib/typescript/module/content-panel/index.web.d.ts +68 -0
- package/lib/typescript/module/content-panel/index.web.d.ts.map +1 -0
- package/package.json +17 -1
- package/src/content-panel/index.tsx +92 -0
- package/src/content-panel/index.web.tsx +144 -0
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.PANEL_TOP_INSET = exports.PANEL_BOTTOM_INSET = exports.GUTTER_MASK_SPREAD = exports.ContentPanel = void 0;
|
|
7
|
+
var _react = _interopRequireWildcard(require("react"));
|
|
8
|
+
var _reactNative = require("react-native");
|
|
9
|
+
var _jsxRuntime = require("react/jsx-runtime");
|
|
10
|
+
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
|
+
* Native variant of `ContentPanel` — the framed app-content surface.
|
|
13
|
+
*
|
|
14
|
+
* `ContentPanel` is the rounded, bordered panel that holds an app's routed
|
|
15
|
+
* content. On WEB it adds two sticky overlays (a gutter "bleed-mask" box-shadow
|
|
16
|
+
* ring and a continuous rounded border frame) so feed content that bleeds into
|
|
17
|
+
* the rounded corners / lateral gutters is masked while a single seamless border
|
|
18
|
+
* is drawn around the panel — see `index.web.tsx`.
|
|
19
|
+
*
|
|
20
|
+
* On NATIVE none of that applies: there is no document scroll, no sticky
|
|
21
|
+
* positioning, and no bleed to mask, so the panel is simply a rounded, bordered
|
|
22
|
+
* surface wrapping its content. The `framed` and `showStickyFrame` props are
|
|
23
|
+
* accepted for cross-platform API parity but are intentionally no-ops here
|
|
24
|
+
* (mirroring the no-op native fork of `../scroll`).
|
|
25
|
+
*
|
|
26
|
+
* Web bundlers select `./index.web` via the `"browser"` export condition in
|
|
27
|
+
* `package.json`; native bundlers fall through to this file.
|
|
28
|
+
*
|
|
29
|
+
* Styling is NativeWind-className-first; the literal class strings below must
|
|
30
|
+
* stay literal so a consumer's Tailwind content-scan over `lib/**` (and the
|
|
31
|
+
* native `src/**`) picks them up.
|
|
32
|
+
*/
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Width (in px) of the WEB sticky gutter-mask box-shadow ring. Exported for
|
|
36
|
+
* cross-platform API parity (it has no effect on native).
|
|
37
|
+
*/
|
|
38
|
+
const GUTTER_MASK_SPREAD = exports.GUTTER_MASK_SPREAD = 40;
|
|
39
|
+
/** Top sticky inset (px) of the framed panel chrome. */
|
|
40
|
+
const PANEL_TOP_INSET = exports.PANEL_TOP_INSET = 8;
|
|
41
|
+
/** Bottom sticky inset (px) of the framed panel chrome. */
|
|
42
|
+
const PANEL_BOTTOM_INSET = exports.PANEL_BOTTOM_INSET = 8;
|
|
43
|
+
const ContentPanelComponent = ({
|
|
44
|
+
children,
|
|
45
|
+
surfaceClassName,
|
|
46
|
+
surfaceStyle,
|
|
47
|
+
contentClassName,
|
|
48
|
+
contentStyle
|
|
49
|
+
}) => {
|
|
50
|
+
const surfaceClass = ['flex-1', 'rounded-radius-28', 'overflow-hidden border border-border', surfaceClassName ?? 'bg-card'].join(' ');
|
|
51
|
+
const contentClass = ['flex-1', contentClassName].filter(Boolean).join(' ');
|
|
52
|
+
return /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
|
|
53
|
+
className: surfaceClass,
|
|
54
|
+
style: surfaceStyle,
|
|
55
|
+
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
|
|
56
|
+
className: contentClass,
|
|
57
|
+
style: contentStyle,
|
|
58
|
+
children: children
|
|
59
|
+
})
|
|
60
|
+
});
|
|
61
|
+
};
|
|
62
|
+
const ContentPanel = exports.ContentPanel = /*#__PURE__*/(0, _react.memo)(ContentPanelComponent);
|
|
63
|
+
ContentPanel.displayName = 'ContentPanel';
|
|
64
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +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":[]}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.PANEL_TOP_INSET = exports.PANEL_BOTTOM_INSET = exports.GUTTER_MASK_SPREAD = exports.ContentPanel = void 0;
|
|
7
|
+
var _react = _interopRequireWildcard(require("react"));
|
|
8
|
+
var _reactNative = require("react-native");
|
|
9
|
+
var _useTheme = require("../theme/use-theme.js");
|
|
10
|
+
var _jsxRuntime = require("react/jsx-runtime");
|
|
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); }
|
|
12
|
+
/**
|
|
13
|
+
* Web variant of `ContentPanel` — the framed app-content surface.
|
|
14
|
+
*
|
|
15
|
+
* On the web, app content scrolls the DOCUMENT, and a multi-column shell keeps
|
|
16
|
+
* the routed content in a rounded, bordered center panel. Two problems follow
|
|
17
|
+
* from letting feed content bleed to the rounded panel edge, both solved here
|
|
18
|
+
* with sticky, full-viewport-height overlays inside the panel surface:
|
|
19
|
+
*
|
|
20
|
+
* 1. BLEED-MASK overlay (`z-30`): a `box-shadow` ring in the GUTTER color
|
|
21
|
+
* (Bloom `background` token) drawn OVER content that bleeds into the thin
|
|
22
|
+
* lateral gutter / rounded corners. `clip-path: inset(-12px)` keeps that
|
|
23
|
+
* ring off the side columns. It sits below opaque chrome (headers/banners),
|
|
24
|
+
* so it only masks the content's bleed, never the chrome. No border.
|
|
25
|
+
* (`clip-path` MUST be the arbitrary class — RN-web drops it from `style`.)
|
|
26
|
+
*
|
|
27
|
+
* 2. BORDER-FRAME overlay (`z-[120]`): a single 1px rounded `border-border`
|
|
28
|
+
* outline with a transparent interior, ABOVE all content. Being one element
|
|
29
|
+
* above everything, it draws ONE continuous rounded border around all four
|
|
30
|
+
* sides — no seams, no double lines, no per-chrome borders.
|
|
31
|
+
*
|
|
32
|
+
* Both overlays are `position: sticky; top: 8px` with a negative
|
|
33
|
+
* `margin-bottom` so they occupy zero layout height (they overlay the scrolled
|
|
34
|
+
* content rather than displacing it). They render BEFORE the content wrapper so
|
|
35
|
+
* z-index — not DOM order — decides layering.
|
|
36
|
+
*
|
|
37
|
+
* When `framed` is false (narrow screens) the panel is full-bleed: a single
|
|
38
|
+
* flat surface, no rounding, no overlays.
|
|
39
|
+
*
|
|
40
|
+
* Native bundlers use `./index.tsx` (a plain rounded surface); web bundlers
|
|
41
|
+
* select this file via the `"browser"` export condition in `package.json`.
|
|
42
|
+
*
|
|
43
|
+
* Styling is NativeWind-className-first; the literal class strings below MUST
|
|
44
|
+
* stay literal so a consumer's Tailwind content-scan over `lib/**` picks them up
|
|
45
|
+
* (no dynamic concatenation of the arbitrary `web:[…]` / `rounded-radius-28`
|
|
46
|
+
* parts).
|
|
47
|
+
*/
|
|
48
|
+
|
|
49
|
+
/** Width (in px) of the sticky gutter-mask box-shadow ring. */
|
|
50
|
+
const GUTTER_MASK_SPREAD = exports.GUTTER_MASK_SPREAD = 40;
|
|
51
|
+
/** Top sticky inset (px) the framed panel chrome pins to. */
|
|
52
|
+
const PANEL_TOP_INSET = exports.PANEL_TOP_INSET = 8;
|
|
53
|
+
/** Bottom sticky inset (px) of the framed panel chrome. */
|
|
54
|
+
const PANEL_BOTTOM_INSET = exports.PANEL_BOTTOM_INSET = 8;
|
|
55
|
+
const ContentPanelComponent = ({
|
|
56
|
+
children,
|
|
57
|
+
framed,
|
|
58
|
+
surfaceClassName,
|
|
59
|
+
surfaceStyle,
|
|
60
|
+
contentClassName,
|
|
61
|
+
contentStyle,
|
|
62
|
+
showStickyFrame,
|
|
63
|
+
maskColor
|
|
64
|
+
}) => {
|
|
65
|
+
const {
|
|
66
|
+
colors
|
|
67
|
+
} = (0, _useTheme.useTheme)();
|
|
68
|
+
if (!framed) {
|
|
69
|
+
// Full-bleed: a single flat surface. No rounding, no sticky overlays.
|
|
70
|
+
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
|
+
});
|
|
76
|
+
}
|
|
77
|
+
const surfaceClass = ['flex-1', 'rounded-radius-28', surfaceClassName ?? 'bg-card'].join(' ');
|
|
78
|
+
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
|
+
})]
|
|
96
|
+
});
|
|
97
|
+
};
|
|
98
|
+
const ContentPanel = exports.ContentPanel = /*#__PURE__*/(0, _react.memo)(ContentPanelComponent);
|
|
99
|
+
ContentPanel.displayName = 'ContentPanel';
|
|
100
|
+
//# sourceMappingURL=index.web.js.map
|
|
@@ -0,0 +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":[]}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Native variant of `ContentPanel` — the framed app-content surface.
|
|
5
|
+
*
|
|
6
|
+
* `ContentPanel` is the rounded, bordered panel that holds an app's routed
|
|
7
|
+
* content. On WEB it adds two sticky overlays (a gutter "bleed-mask" box-shadow
|
|
8
|
+
* ring and a continuous rounded border frame) so feed content that bleeds into
|
|
9
|
+
* the rounded corners / lateral gutters is masked while a single seamless border
|
|
10
|
+
* is drawn around the panel — see `index.web.tsx`.
|
|
11
|
+
*
|
|
12
|
+
* On NATIVE none of that applies: there is no document scroll, no sticky
|
|
13
|
+
* positioning, and no bleed to mask, so the panel is simply a rounded, bordered
|
|
14
|
+
* surface wrapping its content. The `framed` and `showStickyFrame` props are
|
|
15
|
+
* accepted for cross-platform API parity but are intentionally no-ops here
|
|
16
|
+
* (mirroring the no-op native fork of `../scroll`).
|
|
17
|
+
*
|
|
18
|
+
* Web bundlers select `./index.web` via the `"browser"` export condition in
|
|
19
|
+
* `package.json`; native bundlers fall through to this file.
|
|
20
|
+
*
|
|
21
|
+
* Styling is NativeWind-className-first; the literal class strings below must
|
|
22
|
+
* stay literal so a consumer's Tailwind content-scan over `lib/**` (and the
|
|
23
|
+
* native `src/**`) picks them up.
|
|
24
|
+
*/
|
|
25
|
+
import React, { memo } from 'react';
|
|
26
|
+
import { View } from 'react-native';
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Width (in px) of the WEB sticky gutter-mask box-shadow ring. Exported for
|
|
30
|
+
* cross-platform API parity (it has no effect on native).
|
|
31
|
+
*/
|
|
32
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
33
|
+
export const GUTTER_MASK_SPREAD = 40;
|
|
34
|
+
/** Top sticky inset (px) of the framed panel chrome. */
|
|
35
|
+
export const PANEL_TOP_INSET = 8;
|
|
36
|
+
/** Bottom sticky inset (px) of the framed panel chrome. */
|
|
37
|
+
export const PANEL_BOTTOM_INSET = 8;
|
|
38
|
+
const ContentPanelComponent = ({
|
|
39
|
+
children,
|
|
40
|
+
surfaceClassName,
|
|
41
|
+
surfaceStyle,
|
|
42
|
+
contentClassName,
|
|
43
|
+
contentStyle
|
|
44
|
+
}) => {
|
|
45
|
+
const surfaceClass = ['flex-1', 'rounded-radius-28', 'overflow-hidden border border-border', surfaceClassName ?? 'bg-card'].join(' ');
|
|
46
|
+
const contentClass = ['flex-1', contentClassName].filter(Boolean).join(' ');
|
|
47
|
+
return /*#__PURE__*/_jsx(View, {
|
|
48
|
+
className: surfaceClass,
|
|
49
|
+
style: surfaceStyle,
|
|
50
|
+
children: /*#__PURE__*/_jsx(View, {
|
|
51
|
+
className: contentClass,
|
|
52
|
+
style: contentStyle,
|
|
53
|
+
children: children
|
|
54
|
+
})
|
|
55
|
+
});
|
|
56
|
+
};
|
|
57
|
+
export const ContentPanel = /*#__PURE__*/memo(ContentPanelComponent);
|
|
58
|
+
ContentPanel.displayName = 'ContentPanel';
|
|
59
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +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":[]}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Web variant of `ContentPanel` — the framed app-content surface.
|
|
5
|
+
*
|
|
6
|
+
* On the web, app content scrolls the DOCUMENT, and a multi-column shell keeps
|
|
7
|
+
* the routed content in a rounded, bordered center panel. Two problems follow
|
|
8
|
+
* from letting feed content bleed to the rounded panel edge, both solved here
|
|
9
|
+
* with sticky, full-viewport-height overlays inside the panel surface:
|
|
10
|
+
*
|
|
11
|
+
* 1. BLEED-MASK overlay (`z-30`): a `box-shadow` ring in the GUTTER color
|
|
12
|
+
* (Bloom `background` token) drawn OVER content that bleeds into the thin
|
|
13
|
+
* lateral gutter / rounded corners. `clip-path: inset(-12px)` keeps that
|
|
14
|
+
* ring off the side columns. It sits below opaque chrome (headers/banners),
|
|
15
|
+
* so it only masks the content's bleed, never the chrome. No border.
|
|
16
|
+
* (`clip-path` MUST be the arbitrary class — RN-web drops it from `style`.)
|
|
17
|
+
*
|
|
18
|
+
* 2. BORDER-FRAME overlay (`z-[120]`): a single 1px rounded `border-border`
|
|
19
|
+
* outline with a transparent interior, ABOVE all content. Being one element
|
|
20
|
+
* above everything, it draws ONE continuous rounded border around all four
|
|
21
|
+
* sides — no seams, no double lines, no per-chrome borders.
|
|
22
|
+
*
|
|
23
|
+
* Both overlays are `position: sticky; top: 8px` with a negative
|
|
24
|
+
* `margin-bottom` so they occupy zero layout height (they overlay the scrolled
|
|
25
|
+
* content rather than displacing it). They render BEFORE the content wrapper so
|
|
26
|
+
* z-index — not DOM order — decides layering.
|
|
27
|
+
*
|
|
28
|
+
* When `framed` is false (narrow screens) the panel is full-bleed: a single
|
|
29
|
+
* flat surface, no rounding, no overlays.
|
|
30
|
+
*
|
|
31
|
+
* Native bundlers use `./index.tsx` (a plain rounded surface); web bundlers
|
|
32
|
+
* select this file via the `"browser"` export condition in `package.json`.
|
|
33
|
+
*
|
|
34
|
+
* Styling is NativeWind-className-first; the literal class strings below MUST
|
|
35
|
+
* stay literal so a consumer's Tailwind content-scan over `lib/**` picks them up
|
|
36
|
+
* (no dynamic concatenation of the arbitrary `web:[…]` / `rounded-radius-28`
|
|
37
|
+
* parts).
|
|
38
|
+
*/
|
|
39
|
+
import React, { memo } from 'react';
|
|
40
|
+
import { View } from 'react-native';
|
|
41
|
+
import { useTheme } from "../theme/use-theme.js";
|
|
42
|
+
|
|
43
|
+
/** Width (in px) of the sticky gutter-mask box-shadow ring. */
|
|
44
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
45
|
+
export const GUTTER_MASK_SPREAD = 40;
|
|
46
|
+
/** Top sticky inset (px) the framed panel chrome pins to. */
|
|
47
|
+
export const PANEL_TOP_INSET = 8;
|
|
48
|
+
/** Bottom sticky inset (px) of the framed panel chrome. */
|
|
49
|
+
export const PANEL_BOTTOM_INSET = 8;
|
|
50
|
+
const ContentPanelComponent = ({
|
|
51
|
+
children,
|
|
52
|
+
framed,
|
|
53
|
+
surfaceClassName,
|
|
54
|
+
surfaceStyle,
|
|
55
|
+
contentClassName,
|
|
56
|
+
contentStyle,
|
|
57
|
+
showStickyFrame,
|
|
58
|
+
maskColor
|
|
59
|
+
}) => {
|
|
60
|
+
const {
|
|
61
|
+
colors
|
|
62
|
+
} = useTheme();
|
|
63
|
+
if (!framed) {
|
|
64
|
+
// Full-bleed: a single flat surface. No rounding, no sticky overlays.
|
|
65
|
+
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
|
+
});
|
|
71
|
+
}
|
|
72
|
+
const surfaceClass = ['flex-1', 'rounded-radius-28', surfaceClassName ?? 'bg-card'].join(' ');
|
|
73
|
+
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
|
+
})]
|
|
91
|
+
});
|
|
92
|
+
};
|
|
93
|
+
export const ContentPanel = /*#__PURE__*/memo(ContentPanelComponent);
|
|
94
|
+
ContentPanel.displayName = 'ContentPanel';
|
|
95
|
+
//# sourceMappingURL=index.web.js.map
|
|
@@ -0,0 +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":[]}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Native variant of `ContentPanel` — the framed app-content surface.
|
|
3
|
+
*
|
|
4
|
+
* `ContentPanel` is the rounded, bordered panel that holds an app's routed
|
|
5
|
+
* content. On WEB it adds two sticky overlays (a gutter "bleed-mask" box-shadow
|
|
6
|
+
* ring and a continuous rounded border frame) so feed content that bleeds into
|
|
7
|
+
* the rounded corners / lateral gutters is masked while a single seamless border
|
|
8
|
+
* is drawn around the panel — see `index.web.tsx`.
|
|
9
|
+
*
|
|
10
|
+
* On NATIVE none of that applies: there is no document scroll, no sticky
|
|
11
|
+
* positioning, and no bleed to mask, so the panel is simply a rounded, bordered
|
|
12
|
+
* surface wrapping its content. The `framed` and `showStickyFrame` props are
|
|
13
|
+
* accepted for cross-platform API parity but are intentionally no-ops here
|
|
14
|
+
* (mirroring the no-op native fork of `../scroll`).
|
|
15
|
+
*
|
|
16
|
+
* Web bundlers select `./index.web` via the `"browser"` export condition in
|
|
17
|
+
* `package.json`; native bundlers fall through to this file.
|
|
18
|
+
*
|
|
19
|
+
* Styling is NativeWind-className-first; the literal class strings below must
|
|
20
|
+
* stay literal so a consumer's Tailwind content-scan over `lib/**` (and the
|
|
21
|
+
* native `src/**`) picks them up.
|
|
22
|
+
*/
|
|
23
|
+
import React from 'react';
|
|
24
|
+
import { type StyleProp, type ViewStyle } from 'react-native';
|
|
25
|
+
/**
|
|
26
|
+
* Width (in px) of the WEB sticky gutter-mask box-shadow ring. Exported for
|
|
27
|
+
* cross-platform API parity (it has no effect on native).
|
|
28
|
+
*/
|
|
29
|
+
export declare const GUTTER_MASK_SPREAD = 40;
|
|
30
|
+
/** Top sticky inset (px) of the framed panel chrome. */
|
|
31
|
+
export declare const PANEL_TOP_INSET = 8;
|
|
32
|
+
/** Bottom sticky inset (px) of the framed panel chrome. */
|
|
33
|
+
export declare const PANEL_BOTTOM_INSET = 8;
|
|
34
|
+
export interface ContentPanelProps {
|
|
35
|
+
children: React.ReactNode;
|
|
36
|
+
/**
|
|
37
|
+
* Whether the panel is framed (wide screens) or full-bleed (narrow screens).
|
|
38
|
+
* No-op on native — the surface is always a rounded, bordered View.
|
|
39
|
+
*/
|
|
40
|
+
framed: boolean;
|
|
41
|
+
/** Override the surface background utility (defaults to `bg-card`). */
|
|
42
|
+
surfaceClassName?: string;
|
|
43
|
+
surfaceStyle?: StyleProp<ViewStyle>;
|
|
44
|
+
/** Extra utilities for the inner content wrapper. */
|
|
45
|
+
contentClassName?: string;
|
|
46
|
+
contentStyle?: StyleProp<ViewStyle>;
|
|
47
|
+
/**
|
|
48
|
+
* Whether to render the WEB sticky border frame. No-op on native (the border
|
|
49
|
+
* is always part of the surface).
|
|
50
|
+
*/
|
|
51
|
+
showStickyFrame?: boolean;
|
|
52
|
+
/**
|
|
53
|
+
* Color of the WEB sticky bleed-mask gutter ring. Accepted for cross-platform
|
|
54
|
+
* API parity — no-op on native (there is no bleed-mask), like `framed`.
|
|
55
|
+
*/
|
|
56
|
+
maskColor?: string;
|
|
57
|
+
}
|
|
58
|
+
export declare const ContentPanel: React.NamedExoticComponent<ContentPanelProps>;
|
|
59
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +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"}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Web variant of `ContentPanel` — the framed app-content surface.
|
|
3
|
+
*
|
|
4
|
+
* On the web, app content scrolls the DOCUMENT, and a multi-column shell keeps
|
|
5
|
+
* the routed content in a rounded, bordered center panel. Two problems follow
|
|
6
|
+
* from letting feed content bleed to the rounded panel edge, both solved here
|
|
7
|
+
* with sticky, full-viewport-height overlays inside the panel surface:
|
|
8
|
+
*
|
|
9
|
+
* 1. BLEED-MASK overlay (`z-30`): a `box-shadow` ring in the GUTTER color
|
|
10
|
+
* (Bloom `background` token) drawn OVER content that bleeds into the thin
|
|
11
|
+
* lateral gutter / rounded corners. `clip-path: inset(-12px)` keeps that
|
|
12
|
+
* ring off the side columns. It sits below opaque chrome (headers/banners),
|
|
13
|
+
* so it only masks the content's bleed, never the chrome. No border.
|
|
14
|
+
* (`clip-path` MUST be the arbitrary class — RN-web drops it from `style`.)
|
|
15
|
+
*
|
|
16
|
+
* 2. BORDER-FRAME overlay (`z-[120]`): a single 1px rounded `border-border`
|
|
17
|
+
* outline with a transparent interior, ABOVE all content. Being one element
|
|
18
|
+
* above everything, it draws ONE continuous rounded border around all four
|
|
19
|
+
* sides — no seams, no double lines, no per-chrome borders.
|
|
20
|
+
*
|
|
21
|
+
* Both overlays are `position: sticky; top: 8px` with a negative
|
|
22
|
+
* `margin-bottom` so they occupy zero layout height (they overlay the scrolled
|
|
23
|
+
* content rather than displacing it). They render BEFORE the content wrapper so
|
|
24
|
+
* z-index — not DOM order — decides layering.
|
|
25
|
+
*
|
|
26
|
+
* When `framed` is false (narrow screens) the panel is full-bleed: a single
|
|
27
|
+
* flat surface, no rounding, no overlays.
|
|
28
|
+
*
|
|
29
|
+
* Native bundlers use `./index.tsx` (a plain rounded surface); web bundlers
|
|
30
|
+
* select this file via the `"browser"` export condition in `package.json`.
|
|
31
|
+
*
|
|
32
|
+
* Styling is NativeWind-className-first; the literal class strings below MUST
|
|
33
|
+
* stay literal so a consumer's Tailwind content-scan over `lib/**` picks them up
|
|
34
|
+
* (no dynamic concatenation of the arbitrary `web:[…]` / `rounded-radius-28`
|
|
35
|
+
* parts).
|
|
36
|
+
*/
|
|
37
|
+
import React from 'react';
|
|
38
|
+
import { type StyleProp, type ViewStyle } from 'react-native';
|
|
39
|
+
/** Width (in px) of the sticky gutter-mask box-shadow ring. */
|
|
40
|
+
export declare const GUTTER_MASK_SPREAD = 40;
|
|
41
|
+
/** Top sticky inset (px) the framed panel chrome pins to. */
|
|
42
|
+
export declare const PANEL_TOP_INSET = 8;
|
|
43
|
+
/** Bottom sticky inset (px) of the framed panel chrome. */
|
|
44
|
+
export declare const PANEL_BOTTOM_INSET = 8;
|
|
45
|
+
export interface ContentPanelProps {
|
|
46
|
+
children: React.ReactNode;
|
|
47
|
+
/** Framed (wide screens) vs full-bleed (narrow screens). */
|
|
48
|
+
framed: boolean;
|
|
49
|
+
/** Override the surface background utility (defaults to `bg-card`). */
|
|
50
|
+
surfaceClassName?: string;
|
|
51
|
+
surfaceStyle?: StyleProp<ViewStyle>;
|
|
52
|
+
/** Extra utilities for the inner content wrapper. */
|
|
53
|
+
contentClassName?: string;
|
|
54
|
+
contentStyle?: StyleProp<ViewStyle>;
|
|
55
|
+
/** Render the sticky border frame (defaults to true when framed). */
|
|
56
|
+
showStickyFrame?: boolean;
|
|
57
|
+
/**
|
|
58
|
+
* Color of the sticky bleed-mask gutter ring. The ring is meant to match the
|
|
59
|
+
* UNSCOPED outer gutter band (the app's background), so consumers that wrap
|
|
60
|
+
* `<ContentPanel>` in a `BloomColorScope` (e.g. a tinted per-profile color)
|
|
61
|
+
* should pass the unscoped background here — otherwise the internally-read
|
|
62
|
+
* scoped `background` token would tint the ring and leave a faint corner seam.
|
|
63
|
+
* Defaults to the current theme's `background` token.
|
|
64
|
+
*/
|
|
65
|
+
maskColor?: string;
|
|
66
|
+
}
|
|
67
|
+
export declare const ContentPanel: React.NamedExoticComponent<ContentPanelProps>;
|
|
68
|
+
//# sourceMappingURL=index.web.d.ts.map
|
|
@@ -0,0 +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"}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Native variant of `ContentPanel` — the framed app-content surface.
|
|
3
|
+
*
|
|
4
|
+
* `ContentPanel` is the rounded, bordered panel that holds an app's routed
|
|
5
|
+
* content. On WEB it adds two sticky overlays (a gutter "bleed-mask" box-shadow
|
|
6
|
+
* ring and a continuous rounded border frame) so feed content that bleeds into
|
|
7
|
+
* the rounded corners / lateral gutters is masked while a single seamless border
|
|
8
|
+
* is drawn around the panel — see `index.web.tsx`.
|
|
9
|
+
*
|
|
10
|
+
* On NATIVE none of that applies: there is no document scroll, no sticky
|
|
11
|
+
* positioning, and no bleed to mask, so the panel is simply a rounded, bordered
|
|
12
|
+
* surface wrapping its content. The `framed` and `showStickyFrame` props are
|
|
13
|
+
* accepted for cross-platform API parity but are intentionally no-ops here
|
|
14
|
+
* (mirroring the no-op native fork of `../scroll`).
|
|
15
|
+
*
|
|
16
|
+
* Web bundlers select `./index.web` via the `"browser"` export condition in
|
|
17
|
+
* `package.json`; native bundlers fall through to this file.
|
|
18
|
+
*
|
|
19
|
+
* Styling is NativeWind-className-first; the literal class strings below must
|
|
20
|
+
* stay literal so a consumer's Tailwind content-scan over `lib/**` (and the
|
|
21
|
+
* native `src/**`) picks them up.
|
|
22
|
+
*/
|
|
23
|
+
import React from 'react';
|
|
24
|
+
import { type StyleProp, type ViewStyle } from 'react-native';
|
|
25
|
+
/**
|
|
26
|
+
* Width (in px) of the WEB sticky gutter-mask box-shadow ring. Exported for
|
|
27
|
+
* cross-platform API parity (it has no effect on native).
|
|
28
|
+
*/
|
|
29
|
+
export declare const GUTTER_MASK_SPREAD = 40;
|
|
30
|
+
/** Top sticky inset (px) of the framed panel chrome. */
|
|
31
|
+
export declare const PANEL_TOP_INSET = 8;
|
|
32
|
+
/** Bottom sticky inset (px) of the framed panel chrome. */
|
|
33
|
+
export declare const PANEL_BOTTOM_INSET = 8;
|
|
34
|
+
export interface ContentPanelProps {
|
|
35
|
+
children: React.ReactNode;
|
|
36
|
+
/**
|
|
37
|
+
* Whether the panel is framed (wide screens) or full-bleed (narrow screens).
|
|
38
|
+
* No-op on native — the surface is always a rounded, bordered View.
|
|
39
|
+
*/
|
|
40
|
+
framed: boolean;
|
|
41
|
+
/** Override the surface background utility (defaults to `bg-card`). */
|
|
42
|
+
surfaceClassName?: string;
|
|
43
|
+
surfaceStyle?: StyleProp<ViewStyle>;
|
|
44
|
+
/** Extra utilities for the inner content wrapper. */
|
|
45
|
+
contentClassName?: string;
|
|
46
|
+
contentStyle?: StyleProp<ViewStyle>;
|
|
47
|
+
/**
|
|
48
|
+
* Whether to render the WEB sticky border frame. No-op on native (the border
|
|
49
|
+
* is always part of the surface).
|
|
50
|
+
*/
|
|
51
|
+
showStickyFrame?: boolean;
|
|
52
|
+
/**
|
|
53
|
+
* Color of the WEB sticky bleed-mask gutter ring. Accepted for cross-platform
|
|
54
|
+
* API parity — no-op on native (there is no bleed-mask), like `framed`.
|
|
55
|
+
*/
|
|
56
|
+
maskColor?: string;
|
|
57
|
+
}
|
|
58
|
+
export declare const ContentPanel: React.NamedExoticComponent<ContentPanelProps>;
|
|
59
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +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"}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Web variant of `ContentPanel` — the framed app-content surface.
|
|
3
|
+
*
|
|
4
|
+
* On the web, app content scrolls the DOCUMENT, and a multi-column shell keeps
|
|
5
|
+
* the routed content in a rounded, bordered center panel. Two problems follow
|
|
6
|
+
* from letting feed content bleed to the rounded panel edge, both solved here
|
|
7
|
+
* with sticky, full-viewport-height overlays inside the panel surface:
|
|
8
|
+
*
|
|
9
|
+
* 1. BLEED-MASK overlay (`z-30`): a `box-shadow` ring in the GUTTER color
|
|
10
|
+
* (Bloom `background` token) drawn OVER content that bleeds into the thin
|
|
11
|
+
* lateral gutter / rounded corners. `clip-path: inset(-12px)` keeps that
|
|
12
|
+
* ring off the side columns. It sits below opaque chrome (headers/banners),
|
|
13
|
+
* so it only masks the content's bleed, never the chrome. No border.
|
|
14
|
+
* (`clip-path` MUST be the arbitrary class — RN-web drops it from `style`.)
|
|
15
|
+
*
|
|
16
|
+
* 2. BORDER-FRAME overlay (`z-[120]`): a single 1px rounded `border-border`
|
|
17
|
+
* outline with a transparent interior, ABOVE all content. Being one element
|
|
18
|
+
* above everything, it draws ONE continuous rounded border around all four
|
|
19
|
+
* sides — no seams, no double lines, no per-chrome borders.
|
|
20
|
+
*
|
|
21
|
+
* Both overlays are `position: sticky; top: 8px` with a negative
|
|
22
|
+
* `margin-bottom` so they occupy zero layout height (they overlay the scrolled
|
|
23
|
+
* content rather than displacing it). They render BEFORE the content wrapper so
|
|
24
|
+
* z-index — not DOM order — decides layering.
|
|
25
|
+
*
|
|
26
|
+
* When `framed` is false (narrow screens) the panel is full-bleed: a single
|
|
27
|
+
* flat surface, no rounding, no overlays.
|
|
28
|
+
*
|
|
29
|
+
* Native bundlers use `./index.tsx` (a plain rounded surface); web bundlers
|
|
30
|
+
* select this file via the `"browser"` export condition in `package.json`.
|
|
31
|
+
*
|
|
32
|
+
* Styling is NativeWind-className-first; the literal class strings below MUST
|
|
33
|
+
* stay literal so a consumer's Tailwind content-scan over `lib/**` picks them up
|
|
34
|
+
* (no dynamic concatenation of the arbitrary `web:[…]` / `rounded-radius-28`
|
|
35
|
+
* parts).
|
|
36
|
+
*/
|
|
37
|
+
import React from 'react';
|
|
38
|
+
import { type StyleProp, type ViewStyle } from 'react-native';
|
|
39
|
+
/** Width (in px) of the sticky gutter-mask box-shadow ring. */
|
|
40
|
+
export declare const GUTTER_MASK_SPREAD = 40;
|
|
41
|
+
/** Top sticky inset (px) the framed panel chrome pins to. */
|
|
42
|
+
export declare const PANEL_TOP_INSET = 8;
|
|
43
|
+
/** Bottom sticky inset (px) of the framed panel chrome. */
|
|
44
|
+
export declare const PANEL_BOTTOM_INSET = 8;
|
|
45
|
+
export interface ContentPanelProps {
|
|
46
|
+
children: React.ReactNode;
|
|
47
|
+
/** Framed (wide screens) vs full-bleed (narrow screens). */
|
|
48
|
+
framed: boolean;
|
|
49
|
+
/** Override the surface background utility (defaults to `bg-card`). */
|
|
50
|
+
surfaceClassName?: string;
|
|
51
|
+
surfaceStyle?: StyleProp<ViewStyle>;
|
|
52
|
+
/** Extra utilities for the inner content wrapper. */
|
|
53
|
+
contentClassName?: string;
|
|
54
|
+
contentStyle?: StyleProp<ViewStyle>;
|
|
55
|
+
/** Render the sticky border frame (defaults to true when framed). */
|
|
56
|
+
showStickyFrame?: boolean;
|
|
57
|
+
/**
|
|
58
|
+
* Color of the sticky bleed-mask gutter ring. The ring is meant to match the
|
|
59
|
+
* UNSCOPED outer gutter band (the app's background), so consumers that wrap
|
|
60
|
+
* `<ContentPanel>` in a `BloomColorScope` (e.g. a tinted per-profile color)
|
|
61
|
+
* should pass the unscoped background here — otherwise the internally-read
|
|
62
|
+
* scoped `background` token would tint the ring and leave a faint corner seam.
|
|
63
|
+
* Defaults to the current theme's `background` token.
|
|
64
|
+
*/
|
|
65
|
+
maskColor?: string;
|
|
66
|
+
}
|
|
67
|
+
export declare const ContentPanel: React.NamedExoticComponent<ContentPanelProps>;
|
|
68
|
+
//# sourceMappingURL=index.web.d.ts.map
|
|
@@ -0,0 +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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@oxyhq/bloom",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.22.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",
|
|
@@ -778,6 +778,22 @@
|
|
|
778
778
|
"default": "./lib/commonjs/scroll/index.js"
|
|
779
779
|
}
|
|
780
780
|
},
|
|
781
|
+
"./content-panel": {
|
|
782
|
+
"react-native": "./src/content-panel/index.tsx",
|
|
783
|
+
"browser": {
|
|
784
|
+
"types": "./lib/typescript/module/content-panel/index.web.d.ts",
|
|
785
|
+
"import": "./lib/module/content-panel/index.web.js",
|
|
786
|
+
"require": "./lib/commonjs/content-panel/index.web.js"
|
|
787
|
+
},
|
|
788
|
+
"import": {
|
|
789
|
+
"types": "./lib/typescript/module/content-panel/index.d.ts",
|
|
790
|
+
"default": "./lib/module/content-panel/index.js"
|
|
791
|
+
},
|
|
792
|
+
"require": {
|
|
793
|
+
"types": "./lib/typescript/commonjs/content-panel/index.d.ts",
|
|
794
|
+
"default": "./lib/commonjs/content-panel/index.js"
|
|
795
|
+
}
|
|
796
|
+
},
|
|
781
797
|
"./package.json": "./package.json"
|
|
782
798
|
},
|
|
783
799
|
"files": [
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Native variant of `ContentPanel` — the framed app-content surface.
|
|
3
|
+
*
|
|
4
|
+
* `ContentPanel` is the rounded, bordered panel that holds an app's routed
|
|
5
|
+
* content. On WEB it adds two sticky overlays (a gutter "bleed-mask" box-shadow
|
|
6
|
+
* ring and a continuous rounded border frame) so feed content that bleeds into
|
|
7
|
+
* the rounded corners / lateral gutters is masked while a single seamless border
|
|
8
|
+
* is drawn around the panel — see `index.web.tsx`.
|
|
9
|
+
*
|
|
10
|
+
* On NATIVE none of that applies: there is no document scroll, no sticky
|
|
11
|
+
* positioning, and no bleed to mask, so the panel is simply a rounded, bordered
|
|
12
|
+
* surface wrapping its content. The `framed` and `showStickyFrame` props are
|
|
13
|
+
* accepted for cross-platform API parity but are intentionally no-ops here
|
|
14
|
+
* (mirroring the no-op native fork of `../scroll`).
|
|
15
|
+
*
|
|
16
|
+
* Web bundlers select `./index.web` via the `"browser"` export condition in
|
|
17
|
+
* `package.json`; native bundlers fall through to this file.
|
|
18
|
+
*
|
|
19
|
+
* Styling is NativeWind-className-first; the literal class strings below must
|
|
20
|
+
* stay literal so a consumer's Tailwind content-scan over `lib/**` (and the
|
|
21
|
+
* native `src/**`) picks them up.
|
|
22
|
+
*/
|
|
23
|
+
import React, { memo } from 'react';
|
|
24
|
+
import { View, type StyleProp, type ViewStyle } from 'react-native';
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Width (in px) of the WEB sticky gutter-mask box-shadow ring. Exported for
|
|
28
|
+
* cross-platform API parity (it has no effect on native).
|
|
29
|
+
*/
|
|
30
|
+
export const GUTTER_MASK_SPREAD = 40;
|
|
31
|
+
/** Top sticky inset (px) of the framed panel chrome. */
|
|
32
|
+
export const PANEL_TOP_INSET = 8;
|
|
33
|
+
/** Bottom sticky inset (px) of the framed panel chrome. */
|
|
34
|
+
export const PANEL_BOTTOM_INSET = 8;
|
|
35
|
+
|
|
36
|
+
export interface ContentPanelProps {
|
|
37
|
+
children: React.ReactNode;
|
|
38
|
+
/**
|
|
39
|
+
* Whether the panel is framed (wide screens) or full-bleed (narrow screens).
|
|
40
|
+
* No-op on native — the surface is always a rounded, bordered View.
|
|
41
|
+
*/
|
|
42
|
+
framed: boolean;
|
|
43
|
+
/** Override the surface background utility (defaults to `bg-card`). */
|
|
44
|
+
surfaceClassName?: string;
|
|
45
|
+
surfaceStyle?: StyleProp<ViewStyle>;
|
|
46
|
+
/** Extra utilities for the inner content wrapper. */
|
|
47
|
+
contentClassName?: string;
|
|
48
|
+
contentStyle?: StyleProp<ViewStyle>;
|
|
49
|
+
/**
|
|
50
|
+
* Whether to render the WEB sticky border frame. No-op on native (the border
|
|
51
|
+
* is always part of the surface).
|
|
52
|
+
*/
|
|
53
|
+
showStickyFrame?: boolean;
|
|
54
|
+
/**
|
|
55
|
+
* Color of the WEB sticky bleed-mask gutter ring. Accepted for cross-platform
|
|
56
|
+
* API parity — no-op on native (there is no bleed-mask), like `framed`.
|
|
57
|
+
*/
|
|
58
|
+
maskColor?: string;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const ContentPanelComponent: React.FC<ContentPanelProps> = ({
|
|
62
|
+
children,
|
|
63
|
+
surfaceClassName,
|
|
64
|
+
surfaceStyle,
|
|
65
|
+
contentClassName,
|
|
66
|
+
contentStyle,
|
|
67
|
+
}) => {
|
|
68
|
+
const surfaceClass = [
|
|
69
|
+
'flex-1',
|
|
70
|
+
'rounded-radius-28',
|
|
71
|
+
'overflow-hidden border border-border',
|
|
72
|
+
surfaceClassName ?? 'bg-card',
|
|
73
|
+
].join(' ');
|
|
74
|
+
const contentClass = ['flex-1', contentClassName].filter(Boolean).join(' ');
|
|
75
|
+
|
|
76
|
+
return (
|
|
77
|
+
<View
|
|
78
|
+
{...({ className: surfaceClass } as Record<string, string>)}
|
|
79
|
+
style={surfaceStyle}
|
|
80
|
+
>
|
|
81
|
+
<View
|
|
82
|
+
{...({ className: contentClass } as Record<string, string>)}
|
|
83
|
+
style={contentStyle}
|
|
84
|
+
>
|
|
85
|
+
{children}
|
|
86
|
+
</View>
|
|
87
|
+
</View>
|
|
88
|
+
);
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
export const ContentPanel = memo(ContentPanelComponent);
|
|
92
|
+
ContentPanel.displayName = 'ContentPanel';
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Web variant of `ContentPanel` — the framed app-content surface.
|
|
3
|
+
*
|
|
4
|
+
* On the web, app content scrolls the DOCUMENT, and a multi-column shell keeps
|
|
5
|
+
* the routed content in a rounded, bordered center panel. Two problems follow
|
|
6
|
+
* from letting feed content bleed to the rounded panel edge, both solved here
|
|
7
|
+
* with sticky, full-viewport-height overlays inside the panel surface:
|
|
8
|
+
*
|
|
9
|
+
* 1. BLEED-MASK overlay (`z-30`): a `box-shadow` ring in the GUTTER color
|
|
10
|
+
* (Bloom `background` token) drawn OVER content that bleeds into the thin
|
|
11
|
+
* lateral gutter / rounded corners. `clip-path: inset(-12px)` keeps that
|
|
12
|
+
* ring off the side columns. It sits below opaque chrome (headers/banners),
|
|
13
|
+
* so it only masks the content's bleed, never the chrome. No border.
|
|
14
|
+
* (`clip-path` MUST be the arbitrary class — RN-web drops it from `style`.)
|
|
15
|
+
*
|
|
16
|
+
* 2. BORDER-FRAME overlay (`z-[120]`): a single 1px rounded `border-border`
|
|
17
|
+
* outline with a transparent interior, ABOVE all content. Being one element
|
|
18
|
+
* above everything, it draws ONE continuous rounded border around all four
|
|
19
|
+
* sides — no seams, no double lines, no per-chrome borders.
|
|
20
|
+
*
|
|
21
|
+
* Both overlays are `position: sticky; top: 8px` with a negative
|
|
22
|
+
* `margin-bottom` so they occupy zero layout height (they overlay the scrolled
|
|
23
|
+
* content rather than displacing it). They render BEFORE the content wrapper so
|
|
24
|
+
* z-index — not DOM order — decides layering.
|
|
25
|
+
*
|
|
26
|
+
* When `framed` is false (narrow screens) the panel is full-bleed: a single
|
|
27
|
+
* flat surface, no rounding, no overlays.
|
|
28
|
+
*
|
|
29
|
+
* Native bundlers use `./index.tsx` (a plain rounded surface); web bundlers
|
|
30
|
+
* select this file via the `"browser"` export condition in `package.json`.
|
|
31
|
+
*
|
|
32
|
+
* Styling is NativeWind-className-first; the literal class strings below MUST
|
|
33
|
+
* stay literal so a consumer's Tailwind content-scan over `lib/**` picks them up
|
|
34
|
+
* (no dynamic concatenation of the arbitrary `web:[…]` / `rounded-radius-28`
|
|
35
|
+
* parts).
|
|
36
|
+
*/
|
|
37
|
+
import React, { memo } from 'react';
|
|
38
|
+
import { View, type StyleProp, type ViewStyle } from 'react-native';
|
|
39
|
+
|
|
40
|
+
import { useTheme } from '../theme/use-theme';
|
|
41
|
+
|
|
42
|
+
/** Width (in px) of the sticky gutter-mask box-shadow ring. */
|
|
43
|
+
export const GUTTER_MASK_SPREAD = 40;
|
|
44
|
+
/** Top sticky inset (px) the framed panel chrome pins to. */
|
|
45
|
+
export const PANEL_TOP_INSET = 8;
|
|
46
|
+
/** Bottom sticky inset (px) of the framed panel chrome. */
|
|
47
|
+
export const PANEL_BOTTOM_INSET = 8;
|
|
48
|
+
|
|
49
|
+
export interface ContentPanelProps {
|
|
50
|
+
children: React.ReactNode;
|
|
51
|
+
/** Framed (wide screens) vs full-bleed (narrow screens). */
|
|
52
|
+
framed: boolean;
|
|
53
|
+
/** Override the surface background utility (defaults to `bg-card`). */
|
|
54
|
+
surfaceClassName?: string;
|
|
55
|
+
surfaceStyle?: StyleProp<ViewStyle>;
|
|
56
|
+
/** Extra utilities for the inner content wrapper. */
|
|
57
|
+
contentClassName?: string;
|
|
58
|
+
contentStyle?: StyleProp<ViewStyle>;
|
|
59
|
+
/** Render the sticky border frame (defaults to true when framed). */
|
|
60
|
+
showStickyFrame?: boolean;
|
|
61
|
+
/**
|
|
62
|
+
* Color of the sticky bleed-mask gutter ring. The ring is meant to match the
|
|
63
|
+
* UNSCOPED outer gutter band (the app's background), so consumers that wrap
|
|
64
|
+
* `<ContentPanel>` in a `BloomColorScope` (e.g. a tinted per-profile color)
|
|
65
|
+
* should pass the unscoped background here — otherwise the internally-read
|
|
66
|
+
* scoped `background` token would tint the ring and leave a faint corner seam.
|
|
67
|
+
* Defaults to the current theme's `background` token.
|
|
68
|
+
*/
|
|
69
|
+
maskColor?: string;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const ContentPanelComponent: React.FC<ContentPanelProps> = ({
|
|
73
|
+
children,
|
|
74
|
+
framed,
|
|
75
|
+
surfaceClassName,
|
|
76
|
+
surfaceStyle,
|
|
77
|
+
contentClassName,
|
|
78
|
+
contentStyle,
|
|
79
|
+
showStickyFrame,
|
|
80
|
+
maskColor,
|
|
81
|
+
}) => {
|
|
82
|
+
const { colors } = useTheme();
|
|
83
|
+
|
|
84
|
+
if (!framed) {
|
|
85
|
+
// Full-bleed: a single flat surface. No rounding, no sticky overlays.
|
|
86
|
+
const fullBleedClass = [
|
|
87
|
+
'flex-1',
|
|
88
|
+
surfaceClassName ?? 'bg-card',
|
|
89
|
+
contentClassName,
|
|
90
|
+
]
|
|
91
|
+
.filter(Boolean)
|
|
92
|
+
.join(' ');
|
|
93
|
+
return (
|
|
94
|
+
<View
|
|
95
|
+
{...({ className: fullBleedClass } as Record<string, string>)}
|
|
96
|
+
style={[surfaceStyle, contentStyle]}
|
|
97
|
+
>
|
|
98
|
+
{children}
|
|
99
|
+
</View>
|
|
100
|
+
);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
const surfaceClass = ['flex-1', 'rounded-radius-28', surfaceClassName ?? 'bg-card'].join(' ');
|
|
104
|
+
const contentClass = ['flex-1', 'rounded-radius-28 web:overflow-x-clip', contentClassName]
|
|
105
|
+
.filter(Boolean)
|
|
106
|
+
.join(' ');
|
|
107
|
+
|
|
108
|
+
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. */}
|
|
114
|
+
<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 && (
|
|
124
|
+
<View
|
|
125
|
+
pointerEvents="none"
|
|
126
|
+
{...({
|
|
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)]',
|
|
129
|
+
} as Record<string, string>)}
|
|
130
|
+
/>
|
|
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}
|
|
138
|
+
</View>
|
|
139
|
+
</View>
|
|
140
|
+
);
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
export const ContentPanel = memo(ContentPanelComponent);
|
|
144
|
+
ContentPanel.displayName = 'ContentPanel';
|