@oxyhq/bloom 0.35.6 → 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.
@@ -23,16 +23,33 @@
23
23
  * content rather than displacing it). They render BEFORE the content wrapper so
24
24
  * z-index — not DOM order — decides layering.
25
25
  *
26
- * When `framed` is false (narrow screens) the panel is full-bleed: a single
27
- * flat surface, no rounding, no overlays.
26
+ * Framing is tri-state via the `framed` prop:
27
+ *
28
+ * - `undefined` (DEFAULT) → RESPONSIVE, driven purely by NativeWind: full-bleed
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.
35
+ * - `false` → NEVER framed (full-bleed at every size): a single flat surface,
36
+ * no rounding, no overlays.
37
+ * - `true` → ALWAYS framed (rounded + overlays at every size).
38
+ *
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).
28
45
  *
29
46
  * Native bundlers use `./index.tsx` (a plain rounded surface); web bundlers
30
47
  * select this file via the `"browser"` export condition in `package.json`.
31
48
  *
32
49
  * Styling is NativeWind-className-first; the literal class strings below MUST
33
50
  * 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).
51
+ * (no dynamic concatenation of the arbitrary `web:[…]` / `rounded-radius-28` /
52
+ * `md:` parts — whole class strings are selected per mode instead).
36
53
  */
37
54
  import React from 'react';
38
55
  import { type StyleProp, type ViewStyle } from 'react-native';
@@ -42,10 +59,36 @@ export declare const GUTTER_MASK_SPREAD = 40;
42
59
  export declare const PANEL_TOP_INSET = 8;
43
60
  /** Bottom sticky inset (px) of the framed panel chrome. */
44
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;
45
73
  export interface ContentPanelProps {
46
74
  children: React.ReactNode;
47
- /** Framed (wide screens) vs full-bleed (narrow screens). */
48
- framed: boolean;
75
+ /**
76
+ * Framing mode. Tri-state, resolved purely with NativeWind — no consumer
77
+ * breakpoint hook needed:
78
+ * - `undefined` (DEFAULT) → responsive: full-bleed below the `framedFrom`
79
+ * breakpoint, framed at/above it (rounded corners + sticky bleed-mask/border
80
+ * overlays).
81
+ * - `false` → never framed (full-bleed at every size).
82
+ * - `true` → always framed (rounded + overlays at every size).
83
+ */
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;
49
92
  /** Override the surface background utility (defaults to `bg-card`). */
50
93
  surfaceClassName?: string;
51
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;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AACH,OAAO,KAAe,MAAM,OAAO,CAAC;AACpC,OAAO,EAAQ,KAAK,SAAS,EAAE,KAAK,SAAS,EAAE,MAAM,cAAc,CAAC;AAQpE,+DAA+D;AAC/D,eAAO,MAAM,kBAAkB,KAAK,CAAC;AACrC,6DAA6D;AAC7D,eAAO,MAAM,eAAe,IAAI,CAAC;AACjC,2DAA2D;AAC3D,eAAO,MAAM,kBAAkB,IAAI,CAAC;AAEpC,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;IAC1B,4DAA4D;IAC5D,MAAM,EAAE,OAAO,CAAC;IAChB,uEAAuE;IACvE,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,YAAY,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IACpC,qDAAqD;IACrD,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,YAAY,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IACpC,qEAAqE;IACrE,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B;;;;;;;OAOG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAqED,eAAO,MAAM,YAAY,+CAA8B,CAAC"}
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"}
@@ -7,11 +7,21 @@
7
7
  * the rounded corners / lateral gutters is masked while a single seamless border
8
8
  * is drawn around the panel — see `index.web.tsx`.
9
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`).
10
+ * On NATIVE none of the WEB overlay machinery applies: there is no document
11
+ * scroll, no sticky positioning, and no bleed to mask, so the panel is simply a
12
+ * surface wrapping its content. The `framed` prop still drives whether that
13
+ * surface is rounded + bordered, tri-state and resolved with pure NativeWind
14
+ * (the breakpoint's `min-*:` variant evaluates against the window width on
15
+ * native too — no JS breakpoint hook needed):
16
+ *
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`).
20
+ * - `false` → never framed (plain full-bleed at every size).
21
+ * - `true` → always rounded + bordered.
22
+ *
23
+ * `showStickyFrame` and `maskColor` are accepted for cross-platform API parity
24
+ * but are no-ops here (there are no sticky overlays on native).
15
25
  *
16
26
  * Web bundlers select `./index.web` via the `"browser"` export condition in
17
27
  * `package.json`; native bundlers fall through to this file.
@@ -31,13 +41,34 @@ export declare const GUTTER_MASK_SPREAD = 40;
31
41
  export declare const PANEL_TOP_INSET = 8;
32
42
  /** Bottom sticky inset (px) of the framed panel chrome. */
33
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;
34
54
  export interface ContentPanelProps {
35
55
  children: React.ReactNode;
36
56
  /**
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.
57
+ * Framing mode. Tri-state, resolved purely with NativeWind no consumer
58
+ * breakpoint hook needed:
59
+ * - `undefined` (DEFAULT) → responsive: full-bleed below the `framedFrom`
60
+ * breakpoint, rounded + bordered at/above it.
61
+ * - `false` → never framed (plain full-bleed at every size).
62
+ * - `true` → always rounded + bordered.
63
+ */
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.
39
70
  */
40
- framed: boolean;
71
+ framedFrom?: ContentPanelFramedBreakpoint;
41
72
  /** Override the surface background utility (defaults to `bg-card`). */
42
73
  surfaceClassName?: string;
43
74
  surfaceStyle?: StyleProp<ViewStyle>;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/content-panel/index.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,OAAO,KAAe,MAAM,OAAO,CAAC;AACpC,OAAO,EAAQ,KAAK,SAAS,EAAE,KAAK,SAAS,EAAE,MAAM,cAAc,CAAC;AAOpE;;;GAGG;AACH,eAAO,MAAM,kBAAkB,KAAK,CAAC;AACrC,wDAAwD;AACxD,eAAO,MAAM,eAAe,IAAI,CAAC;AACjC,2DAA2D;AAC3D,eAAO,MAAM,kBAAkB,IAAI,CAAC;AAEpC,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;IAC1B;;;OAGG;IACH,MAAM,EAAE,OAAO,CAAC;IAChB,uEAAuE;IACvE,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,YAAY,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IACpC,qDAAqD;IACrD,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,YAAY,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IACpC;;;OAGG;IACH,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAqCD,eAAO,MAAM,YAAY,+CAA8B,CAAC"}
1
+ {"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"}
@@ -23,16 +23,33 @@
23
23
  * content rather than displacing it). They render BEFORE the content wrapper so
24
24
  * z-index — not DOM order — decides layering.
25
25
  *
26
- * When `framed` is false (narrow screens) the panel is full-bleed: a single
27
- * flat surface, no rounding, no overlays.
26
+ * Framing is tri-state via the `framed` prop:
27
+ *
28
+ * - `undefined` (DEFAULT) → RESPONSIVE, driven purely by NativeWind: full-bleed
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.
35
+ * - `false` → NEVER framed (full-bleed at every size): a single flat surface,
36
+ * no rounding, no overlays.
37
+ * - `true` → ALWAYS framed (rounded + overlays at every size).
38
+ *
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).
28
45
  *
29
46
  * Native bundlers use `./index.tsx` (a plain rounded surface); web bundlers
30
47
  * select this file via the `"browser"` export condition in `package.json`.
31
48
  *
32
49
  * Styling is NativeWind-className-first; the literal class strings below MUST
33
50
  * 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).
51
+ * (no dynamic concatenation of the arbitrary `web:[…]` / `rounded-radius-28` /
52
+ * `md:` parts — whole class strings are selected per mode instead).
36
53
  */
37
54
  import React from 'react';
38
55
  import { type StyleProp, type ViewStyle } from 'react-native';
@@ -42,10 +59,36 @@ export declare const GUTTER_MASK_SPREAD = 40;
42
59
  export declare const PANEL_TOP_INSET = 8;
43
60
  /** Bottom sticky inset (px) of the framed panel chrome. */
44
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;
45
73
  export interface ContentPanelProps {
46
74
  children: React.ReactNode;
47
- /** Framed (wide screens) vs full-bleed (narrow screens). */
48
- framed: boolean;
75
+ /**
76
+ * Framing mode. Tri-state, resolved purely with NativeWind — no consumer
77
+ * breakpoint hook needed:
78
+ * - `undefined` (DEFAULT) → responsive: full-bleed below the `framedFrom`
79
+ * breakpoint, framed at/above it (rounded corners + sticky bleed-mask/border
80
+ * overlays).
81
+ * - `false` → never framed (full-bleed at every size).
82
+ * - `true` → always framed (rounded + overlays at every size).
83
+ */
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;
49
92
  /** Override the surface background utility (defaults to `bg-card`). */
50
93
  surfaceClassName?: string;
51
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;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AACH,OAAO,KAAe,MAAM,OAAO,CAAC;AACpC,OAAO,EAAQ,KAAK,SAAS,EAAE,KAAK,SAAS,EAAE,MAAM,cAAc,CAAC;AAQpE,+DAA+D;AAC/D,eAAO,MAAM,kBAAkB,KAAK,CAAC;AACrC,6DAA6D;AAC7D,eAAO,MAAM,eAAe,IAAI,CAAC;AACjC,2DAA2D;AAC3D,eAAO,MAAM,kBAAkB,IAAI,CAAC;AAEpC,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;IAC1B,4DAA4D;IAC5D,MAAM,EAAE,OAAO,CAAC;IAChB,uEAAuE;IACvE,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,YAAY,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IACpC,qDAAqD;IACrD,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,YAAY,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IACpC,qEAAqE;IACrE,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B;;;;;;;OAOG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAqED,eAAO,MAAM,YAAY,+CAA8B,CAAC"}
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@oxyhq/bloom",
3
- "version": "0.35.6",
3
+ "version": "0.35.8",
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",
@@ -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
+ });
@@ -7,11 +7,21 @@
7
7
  * the rounded corners / lateral gutters is masked while a single seamless border
8
8
  * is drawn around the panel — see `index.web.tsx`.
9
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`).
10
+ * On NATIVE none of the WEB overlay machinery applies: there is no document
11
+ * scroll, no sticky positioning, and no bleed to mask, so the panel is simply a
12
+ * surface wrapping its content. The `framed` prop still drives whether that
13
+ * surface is rounded + bordered, tri-state and resolved with pure NativeWind
14
+ * (the breakpoint's `min-*:` variant evaluates against the window width on
15
+ * native too — no JS breakpoint hook needed):
16
+ *
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`).
20
+ * - `false` → never framed (plain full-bleed at every size).
21
+ * - `true` → always rounded + bordered.
22
+ *
23
+ * `showStickyFrame` and `maskColor` are accepted for cross-platform API parity
24
+ * but are no-ops here (there are no sticky overlays on native).
15
25
  *
16
26
  * Web bundlers select `./index.web` via the `"browser"` export condition in
17
27
  * `package.json`; native bundlers fall through to this file.
@@ -38,13 +48,35 @@ export const PANEL_TOP_INSET = 8;
38
48
  /** Bottom sticky inset (px) of the framed panel chrome. */
39
49
  export const PANEL_BOTTOM_INSET = 8;
40
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
+
41
62
  export interface ContentPanelProps {
42
63
  children: React.ReactNode;
43
64
  /**
44
- * Whether the panel is framed (wide screens) or full-bleed (narrow screens).
45
- * No-op on native — the surface is always a rounded, bordered View.
65
+ * Framing mode. Tri-state, resolved purely with NativeWind no consumer
66
+ * breakpoint hook needed:
67
+ * - `undefined` (DEFAULT) → responsive: full-bleed below the `framedFrom`
68
+ * breakpoint, rounded + bordered at/above it.
69
+ * - `false` → never framed (plain full-bleed at every size).
70
+ * - `true` → always rounded + bordered.
46
71
  */
47
- framed: boolean;
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;
48
80
  /** Override the surface background utility (defaults to `bg-card`). */
49
81
  surfaceClassName?: string;
50
82
  surfaceStyle?: StyleProp<ViewStyle>;
@@ -63,8 +95,24 @@ export interface ContentPanelProps {
63
95
  maskColor?: string;
64
96
  }
65
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
+
66
112
  const ContentPanelComponent: React.FC<ContentPanelProps> = ({
67
113
  children,
114
+ framed,
115
+ framedFrom = 768,
68
116
  surfaceClassName,
69
117
  surfaceStyle,
70
118
  contentClassName,
@@ -73,12 +121,19 @@ const ContentPanelComponent: React.FC<ContentPanelProps> = ({
73
121
  // Dev-only invariant — a ContentPanel must never be nested inside another.
74
122
  useContentPanelNestingGuard();
75
123
 
76
- const surfaceClass = [
77
- 'flex-1',
78
- 'rounded-radius-28',
79
- 'overflow-hidden border border-border',
80
- surfaceClassName ?? 'bg-card',
81
- ].join(' ');
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).
130
+ const surfaceBase =
131
+ framed === undefined
132
+ ? `flex-1 ${RESPONSIVE_SURFACE[framedFrom]}`
133
+ : framed
134
+ ? 'flex-1 overflow-hidden rounded-radius-28 border border-border'
135
+ : 'flex-1';
136
+ const surfaceClass = [surfaceBase, surfaceClassName ?? 'bg-card'].join(' ');
82
137
  const contentClass = ['flex-1', contentClassName].filter(Boolean).join(' ');
83
138
 
84
139
  return (