react-native-terra-ui 0.11.10 → 0.11.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. package/CHANGELOG.md +17 -0
  2. package/lib/module/components/box/Box.js +35 -10
  3. package/lib/module/components/box/Box.js.map +1 -1
  4. package/lib/module/components/header/hooks/use-header-color.js +16 -0
  5. package/lib/module/components/header/hooks/use-header-color.js.map +1 -0
  6. package/lib/module/components/header/hooks/use-large-title-header.js +81 -0
  7. package/lib/module/components/header/hooks/use-large-title-header.js.map +1 -0
  8. package/lib/module/components/header/parts/HeaderAccessory.js +21 -6
  9. package/lib/module/components/header/parts/HeaderAccessory.js.map +1 -1
  10. package/lib/module/components/header/parts/HeaderBar.js +122 -0
  11. package/lib/module/components/header/parts/HeaderBar.js.map +1 -0
  12. package/lib/module/components/header/parts/HeaderGradientOverlay.js +20 -76
  13. package/lib/module/components/header/parts/HeaderGradientOverlay.js.map +1 -1
  14. package/lib/module/components/header/parts/HeaderSurface.js +16 -37
  15. package/lib/module/components/header/parts/HeaderSurface.js.map +1 -1
  16. package/lib/module/components/header/variants/LargeTitleHeader/index.js +51 -388
  17. package/lib/module/components/header/variants/LargeTitleHeader/index.js.map +1 -1
  18. package/lib/module/components/header/variants/LargeTitleHeader/parts/LargeTitleContent.js +72 -0
  19. package/lib/module/components/header/variants/LargeTitleHeader/parts/LargeTitleContent.js.map +1 -0
  20. package/lib/module/components/header/variants/TitleHeader/index.js +32 -161
  21. package/lib/module/components/header/variants/TitleHeader/index.js.map +1 -1
  22. package/lib/module/components/surface/Surface.js +2 -0
  23. package/lib/module/components/surface/Surface.js.map +1 -1
  24. package/lib/typescript/src/components/box/Box.d.ts +28 -4
  25. package/lib/typescript/src/components/box/Box.d.ts.map +1 -1
  26. package/lib/typescript/src/components/header/hooks/use-header-color.d.ts +4 -0
  27. package/lib/typescript/src/components/header/hooks/use-header-color.d.ts.map +1 -0
  28. package/lib/typescript/src/components/header/hooks/use-large-title-header.d.ts +36 -0
  29. package/lib/typescript/src/components/header/hooks/use-large-title-header.d.ts.map +1 -0
  30. package/lib/typescript/src/components/header/parts/HeaderAccessory.d.ts +8 -5
  31. package/lib/typescript/src/components/header/parts/HeaderAccessory.d.ts.map +1 -1
  32. package/lib/typescript/src/components/header/parts/HeaderBar.d.ts +17 -0
  33. package/lib/typescript/src/components/header/parts/HeaderBar.d.ts.map +1 -0
  34. package/lib/typescript/src/components/header/parts/HeaderGradientOverlay.d.ts +12 -15
  35. package/lib/typescript/src/components/header/parts/HeaderGradientOverlay.d.ts.map +1 -1
  36. package/lib/typescript/src/components/header/parts/HeaderSurface.d.ts +8 -9
  37. package/lib/typescript/src/components/header/parts/HeaderSurface.d.ts.map +1 -1
  38. package/lib/typescript/src/components/header/variants/LargeTitleHeader/index.d.ts +3 -23
  39. package/lib/typescript/src/components/header/variants/LargeTitleHeader/index.d.ts.map +1 -1
  40. package/lib/typescript/src/components/header/variants/LargeTitleHeader/parts/LargeTitleContent.d.ts +11 -0
  41. package/lib/typescript/src/components/header/variants/LargeTitleHeader/parts/LargeTitleContent.d.ts.map +1 -0
  42. package/lib/typescript/src/components/header/variants/TitleHeader/index.d.ts +3 -19
  43. package/lib/typescript/src/components/header/variants/TitleHeader/index.d.ts.map +1 -1
  44. package/lib/typescript/src/components/surface/Surface.d.ts +1 -1
  45. package/lib/typescript/src/components/surface/Surface.d.ts.map +1 -1
  46. package/lib/typescript/src/components/toast/Toast.d.ts +1 -1
  47. package/lib/typescript/src/components/toast/Toast.d.ts.map +1 -1
  48. package/package.json +1 -1
  49. package/src/components/box/Box.tsx +59 -16
  50. package/src/components/header/hooks/use-header-color.ts +14 -0
  51. package/src/components/header/hooks/use-large-title-header.ts +78 -0
  52. package/src/components/header/parts/HeaderAccessory.tsx +26 -7
  53. package/src/components/header/parts/HeaderBar.tsx +125 -0
  54. package/src/components/header/parts/HeaderGradientOverlay.tsx +18 -70
  55. package/src/components/header/parts/HeaderSurface.tsx +16 -35
  56. package/src/components/header/variants/LargeTitleHeader/index.tsx +44 -371
  57. package/src/components/header/variants/LargeTitleHeader/parts/LargeTitleContent.tsx +88 -0
  58. package/src/components/header/variants/TitleHeader/index.tsx +32 -149
  59. package/src/components/surface/Surface.tsx +2 -1
@@ -10,7 +10,7 @@ export interface LargeTitleHeaderProps extends TitleHeaderProps {
10
10
  /**
11
11
  * How `accessory` moves while scrolling. `'sticky'` starts below the large title
12
12
  * and pins below the compact bar when scrolled up. A hidden large title always pins.
13
- * @defaultValue 'pinned'
13
+ * @defaultValue 'sticky'
14
14
  */
15
15
  accessoryBehavior?: HeaderAccessoryBehavior;
16
16
  /**
@@ -19,26 +19,6 @@ export interface LargeTitleHeaderProps extends TitleHeaderProps {
19
19
  */
20
20
  titleAlignment?: "center" | "left";
21
21
  }
22
- /**
23
- * iOS-style collapsing large-title header. The large title is injected (via the
24
- * `Screen`'s portal) into the top of the scroll content, so it scrolls away
25
- * naturally; the compact title cross-fades over the theme header height.
26
- * The top safe area and bar overlay fade from transparent when expanded to
27
- * the header background when collapsed. The large-title content stays transparent.
28
- * Snapping is opt-in through the scroll container's snapToOffsets.
29
- *
30
- * Must be used inside a `Screen` with a `Screen.ScrollView` or `Screen.List`.
31
- *
32
- * @example
33
- * ```tsx
34
- * <Screen.Header>
35
- * <Header.LargeTitle
36
- * overline="Today"
37
- * title="Meditate"
38
- * subtitle="Good morning"
39
- * />
40
- * </Screen.Header>
41
- * ```
42
- */
43
- export declare function LargeTitleHeader({ title, overline, subtitle, leadingActions, trailingActions, LeftToolbar, RightToolbar, accessory, accessoryPadding, accessoryBehavior, dismissAction, navigation, onDismiss, titleAlignment, isLargeTitleHidden, background, bg, }: LargeTitleHeaderProps): import("react").JSX.Element;
22
+ /** Collapsing header. The expanded title lives in scroll content; the bar and accessory overlay it. */
23
+ export declare function LargeTitleHeader({ title, overline, subtitle, leadingActions, trailingActions, LeftToolbar, RightToolbar, accessory, accessoryPadding, accessoryBehavior, titleAlignment, isLargeTitleHidden, background, bg, ...dismissProps }: LargeTitleHeaderProps): import("react").JSX.Element;
44
24
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../../../src/components/header/variants/LargeTitleHeader/index.tsx"],"names":[],"mappings":"AAgBA,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,gBAAa,CAAC;AAE3D,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,yBAAgB,CAAC;AASvD,MAAM,WAAW,qBAAsB,SAAQ,gBAAgB;IAC7D,uDAAuD;IACvD,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,2DAA2D;IAC3D,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,yEAAyE;IACzE,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B;;;;OAIG;IACH,iBAAiB,CAAC,EAAE,uBAAuB,CAAC;IAC5C;;;OAGG;IACH,cAAc,CAAC,EAAE,QAAQ,GAAG,MAAM,CAAC;CACpC;AAyED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,gBAAgB,CAAC,EAC/B,KAAK,EACL,QAAQ,EACR,QAAQ,EACR,cAAc,EACd,eAAe,EACf,WAAW,EACX,YAAY,EACZ,SAAS,EACT,gBAAsB,EACtB,iBAA4B,EAC5B,aAAsB,EACtB,UAAU,EACV,SAAS,EACT,cAAyB,EACzB,kBAA0B,EAC1B,UAAU,EACV,EAAE,GACH,EAAE,qBAAqB,+BA2LvB"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../../../src/components/header/variants/LargeTitleHeader/index.tsx"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,gBAAa,CAAC;AAC3D,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,yBAAgB,CAAC;AAGvD,MAAM,WAAW,qBAAsB,SAAQ,gBAAgB;IAC7D,uDAAuD;IACvD,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,2DAA2D;IAC3D,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,yEAAyE;IACzE,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B;;;;OAIG;IACH,iBAAiB,CAAC,EAAE,uBAAuB,CAAC;IAC5C;;;OAGG;IACH,cAAc,CAAC,EAAE,QAAQ,GAAG,MAAM,CAAC;CACpC;AAED,uGAAuG;AACvG,wBAAgB,gBAAgB,CAAC,EAC/B,KAAK,EACL,QAAQ,EACR,QAAQ,EACR,cAAc,EACd,eAAe,EACf,WAAW,EACX,YAAY,EACZ,SAAS,EACT,gBAAsB,EACtB,iBAA4B,EAC5B,cAAyB,EACzB,kBAA0B,EAC1B,UAAU,EACV,EAAE,EACF,GAAG,YAAY,EAChB,EAAE,qBAAqB,+BA8CvB"}
@@ -0,0 +1,11 @@
1
+ import type { ComponentProps } from "react";
2
+ import Animated from "react-native-reanimated";
3
+ import type { LargeTitleHeaderProps } from "../index.js";
4
+ export declare function LargeTitleContent({ title, overline, subtitle, hasAccessoryBelow, onHeightChange, animatedStyle, }: Pick<LargeTitleHeaderProps, "title" | "overline" | "subtitle"> & {
5
+ /** Reserve a gap before a sticky accessory below this block. */
6
+ hasAccessoryBelow: boolean;
7
+ /** Reports the large-title block's layout height in dp. */
8
+ onHeightChange?: (height: number) => void;
9
+ animatedStyle: ComponentProps<typeof Animated.View>["style"];
10
+ }): import("react").JSX.Element;
11
+ //# sourceMappingURL=LargeTitleContent.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"LargeTitleContent.d.ts","sourceRoot":"","sources":["../../../../../../../../src/components/header/variants/LargeTitleHeader/parts/LargeTitleContent.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,OAAO,CAAC;AAG5C,OAAO,QAAQ,MAAM,yBAAyB,CAAC;AAK/C,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,aAAI,CAAC;AAEhD,wBAAgB,iBAAiB,CAAC,EAChC,KAAK,EACL,QAAQ,EACR,QAAQ,EACR,iBAAiB,EACjB,cAAc,EACd,aAAa,GACd,EAAE,IAAI,CAAC,qBAAqB,EAAE,OAAO,GAAG,UAAU,GAAG,UAAU,CAAC,GAAG;IAClE,gEAAgE;IAChE,iBAAiB,EAAE,OAAO,CAAC;IAC3B,2DAA2D;IAC3D,cAAc,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;IAC1C,aAAa,EAAE,cAAc,CAAC,OAAO,QAAQ,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC,CAAC;CAC9D,+BAmCA"}
@@ -1,6 +1,6 @@
1
1
  import { type ReactNode } from "react";
2
2
  import type { ColorToken } from "../../../../theme/types";
3
- import { type HeaderDismissProps } from "../../header-buttons.js";
3
+ import type { HeaderDismissProps } from "../../header-buttons.js";
4
4
  import type { HeaderAccessoryPadding } from "../../types.js";
5
5
  export interface TitleHeaderProps extends HeaderDismissProps {
6
6
  /**
@@ -42,22 +42,6 @@ export interface TitleHeaderProps extends HeaderDismissProps {
42
42
  /** @deprecated Use background. */
43
43
  bg?: ColorToken;
44
44
  }
45
- /**
46
- * Compact title bar (leading slot · title · trailing slot) with a top safe-area
47
- * inset outside Screen. Within Screen, the root owns safe-area clearance. The inline counterpart to `Header.LargeTitle`; use it for screens that
48
- * don't need a collapsing large title.
49
- *
50
- * @example
51
- * ```tsx
52
- * <Screen.Header>
53
- * <Header.Title
54
- * title="Stats"
55
- * trailingActions={
56
- * <Toolbar.Button icon="add" accessibilityLabel="Add" onPress={add} />
57
- * }
58
- * />
59
- * </Screen.Header>
60
- * ```
61
- */
62
- export declare function TitleHeader({ title, leadingActions, trailingActions, LeftToolbar, RightToolbar, accessory, accessoryPadding, dismissAction, navigation, onDismiss, titleAlignment, background, bg, }: TitleHeaderProps): import("react").JSX.Element;
45
+ /** Compact header. Screen owns the safe area when present; standalone headers reserve it here. */
46
+ export declare function TitleHeader({ title, leadingActions, trailingActions, LeftToolbar, RightToolbar, accessory, accessoryPadding, titleAlignment, background, bg, ...dismissProps }: TitleHeaderProps): import("react").JSX.Element;
63
47
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../../../src/components/header/variants/TitleHeader/index.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,SAAS,EAA2B,MAAM,OAAO,CAAC;AAKhE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAM/C,OAAO,EAAuB,KAAK,kBAAkB,EAAE,MAAM,yBAAsB,CAAC;AAGpF,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,gBAAa,CAAC;AAG1D,MAAM,WAAW,gBAAiB,SAAQ,kBAAkB;IAC1D;;;;OAIG;IACH,KAAK,CAAC,EAAE,SAAS,CAAC;IAClB;;;OAGG;IACH,cAAc,CAAC,EAAE,SAAS,CAAC;IAC3B,sCAAsC;IACtC,WAAW,CAAC,EAAE,SAAS,CAAC;IACxB;;;OAGG;IACH,eAAe,CAAC,EAAE,SAAS,CAAC;IAC5B,uCAAuC;IACvC,YAAY,CAAC,EAAE,SAAS,CAAC;IACzB;;;;OAIG;IACH,SAAS,CAAC,EAAE,SAAS,CAAC;IACtB;;;;OAIG;IACH,gBAAgB,CAAC,EAAE,sBAAsB,CAAC;IAC1C,8DAA8D;IAC9D,cAAc,CAAC,EAAE,QAAQ,GAAG,MAAM,CAAC;IACnC,mGAAmG;IACnG,UAAU,CAAC,EAAE,UAAU,CAAC;IACxB,kCAAkC;IAClC,EAAE,CAAC,EAAE,UAAU,CAAC;CACjB;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,WAAW,CAAC,EAC1B,KAAK,EACL,cAAc,EACd,eAAe,EACf,WAAW,EACX,YAAY,EACZ,SAAS,EACT,gBAAsB,EACtB,aAAsB,EACtB,UAAU,EACV,SAAS,EACT,cAAyB,EACzB,UAAU,EACV,EAAE,GACH,EAAE,gBAAgB,+BAiElB"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../../../src/components/header/variants/TitleHeader/index.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,SAAS,EAAc,MAAM,OAAO,CAAC;AAKnD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAG/C,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,yBAAsB,CAAC;AAI/D,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,gBAAa,CAAC;AAE1D,MAAM,WAAW,gBAAiB,SAAQ,kBAAkB;IAC1D;;;;OAIG;IACH,KAAK,CAAC,EAAE,SAAS,CAAC;IAClB;;;OAGG;IACH,cAAc,CAAC,EAAE,SAAS,CAAC;IAC3B,sCAAsC;IACtC,WAAW,CAAC,EAAE,SAAS,CAAC;IACxB;;;OAGG;IACH,eAAe,CAAC,EAAE,SAAS,CAAC;IAC5B,uCAAuC;IACvC,YAAY,CAAC,EAAE,SAAS,CAAC;IACzB;;;;OAIG;IACH,SAAS,CAAC,EAAE,SAAS,CAAC;IACtB;;;;OAIG;IACH,gBAAgB,CAAC,EAAE,sBAAsB,CAAC;IAC1C,8DAA8D;IAC9D,cAAc,CAAC,EAAE,QAAQ,GAAG,MAAM,CAAC;IACnC,mGAAmG;IACnG,UAAU,CAAC,EAAE,UAAU,CAAC;IACxB,kCAAkC;IAClC,EAAE,CAAC,EAAE,UAAU,CAAC;CACjB;AAED,kGAAkG;AAClG,wBAAgB,WAAW,CAAC,EAC1B,KAAK,EACL,cAAc,EACd,eAAe,EACf,WAAW,EACX,YAAY,EACZ,SAAS,EACT,gBAAsB,EACtB,cAAyB,EACzB,UAAU,EACV,EAAE,EACF,GAAG,YAAY,EAChB,EAAE,gBAAgB,+BA0BlB"}
@@ -34,5 +34,5 @@ export interface SurfaceProps extends Omit<BoxProps, "background"> {
34
34
  * </Surface>
35
35
  * ```
36
36
  */
37
- export declare const Surface: import("react").ForwardRefExoticComponent<SurfaceProps & import("react").RefAttributes<import("react-native/types_generated/src/private/webapis/dom/nodes/ReactNativeElement").default>>;
37
+ export declare const Surface: import("react").ForwardRefExoticComponent<Omit<SurfaceProps, "ref"> & import("react").RefAttributes<import("react-native/types_generated/src/private/webapis/dom/nodes/ReactNativeElement").default>>;
38
38
  //# sourceMappingURL=Surface.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"Surface.d.ts","sourceRoot":"","sources":["../../../../../src/components/surface/Surface.tsx"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAc,YAAY,EAAE,MAAM,cAAc,CAAC;AAC7D,OAAO,EAA8B,KAAK,QAAQ,EAAE,MAAM,iBAAQ,CAAC;AAEnE,6CAA6C;AAC7C,MAAM,MAAM,cAAc,GAAG,MAAM,GAAG,QAAQ,GAAG,QAAQ,GAAG,aAAa,CAAC;AAE1E,MAAM,WAAW,YAAa,SAAQ,IAAI,CAAC,QAAQ,EAAE,YAAY,CAAC;IAChE,sDAAsD;IACtD,OAAO,CAAC,EAAE,cAAc,CAAC;IACzB;;;;;;OAMG;IACH,SAAS,CAAC,EAAE,YAAY,CAAC;CAC1B;AA6BD;;;;;;;;;;;;;;;;;;;GAmBG;AACH,eAAO,MAAM,OAAO,0LAiBlB,CAAC"}
1
+ {"version":3,"file":"Surface.d.ts","sourceRoot":"","sources":["../../../../../src/components/surface/Surface.tsx"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAc,YAAY,EAAE,MAAM,cAAc,CAAC;AAC7D,OAAO,EAA8B,KAAK,QAAQ,EAAE,MAAM,iBAAQ,CAAC;AAEnE,6CAA6C;AAC7C,MAAM,MAAM,cAAc,GAAG,MAAM,GAAG,QAAQ,GAAG,QAAQ,GAAG,aAAa,CAAC;AAE1E,MAAM,WAAW,YAAa,SAAQ,IAAI,CAAC,QAAQ,EAAE,YAAY,CAAC;IAChE,sDAAsD;IACtD,OAAO,CAAC,EAAE,cAAc,CAAC;IACzB;;;;;;OAMG;IACH,SAAS,CAAC,EAAE,YAAY,CAAC;CAC1B;AA6BD;;;;;;;;;;;;;;;;;;;GAmBG;AACH,eAAO,MAAM,OAAO,uMAkBlB,CAAC"}
@@ -22,7 +22,7 @@ export interface ToastProps extends Omit<SurfaceProps, "id" | "variant"> {
22
22
  /** Local close callback used by `Toast.Close`. */
23
23
  onClose?: () => void;
24
24
  }
25
- declare const ToastRoot: import("react").ForwardRefExoticComponent<ToastProps & import("react").RefAttributes<import("react-native/types_generated/src/private/webapis/dom/nodes/ReactNativeElement").default>>;
25
+ declare const ToastRoot: import("react").ForwardRefExoticComponent<Omit<ToastProps, "ref"> & import("react").RefAttributes<import("react-native/types_generated/src/private/webapis/dom/nodes/ReactNativeElement").default>>;
26
26
  export interface ToastTitleProps extends TextProps {
27
27
  }
28
28
  declare const ToastTitle: import("react").ForwardRefExoticComponent<ToastTitleProps & import("react").RefAttributes<import("react-native/types_generated/src/private/webapis/dom/nodes/ReactNativeElement").default>>;
@@ -1 +1 @@
1
- {"version":3,"file":"Toast.d.ts","sourceRoot":"","sources":["../../../../../src/components/toast/Toast.tsx"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"Toast.d.ts","sourceRoot":"","sources":["../../../../../src/components/toast/Toast.tsx"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-terra-ui",
3
- "version": "0.11.10",
3
+ "version": "0.11.12",
4
4
  "description": "Themeable React Native UI components built on react-native-unistyles",
5
5
  "main": "./lib/module/index.js",
6
6
  "types": "./lib/typescript/src/index.d.ts",
@@ -1,53 +1,96 @@
1
- import { type ComponentRef, forwardRef } from "react";
2
- import { View, type ViewProps } from "react-native";
1
+ import {
2
+ type ComponentPropsWithRef,
3
+ type ElementType,
4
+ forwardRef,
5
+ type ReactElement,
6
+ type ReactNode,
7
+ } from "react";
8
+ import { type StyleProp, View, type ViewStyle } from "react-native";
3
9
 
4
10
  import { StyleSheet, useUnistyles } from "react-native-unistyles";
5
11
 
6
12
  import { Slot } from "../slot";
7
13
  import { BOX_STYLE_PROP_KEYS, type BoxStyleProps, resolveBoxStyle } from "./utils";
8
14
 
9
- export interface BoxProps extends ViewProps, BoxStyleProps {
10
- /** Merge Box's style/props onto a single child instead of rendering a View. */
15
+ /** Props owned by Box itself — everything else belongs to the rendered element. */
16
+ interface BoxOwnProps extends BoxStyleProps {
17
+ /** Merge Box's style/props onto a single child instead of rendering an element. Takes precedence over `as`. */
11
18
  asChild?: boolean;
12
19
  }
13
20
 
14
- const STYLE_KEYS = new Set<string>(BOX_STYLE_PROP_KEYS as readonly string[]);
15
-
16
21
  /**
17
- * Base layout primitive: a `View` styled through theme-token props.
18
- * The only component family exposing token style-props.
22
+ * Props of `Box` when cast as `T`. Token style-props plus everything the
23
+ * rendered component accepts — `<Box as={Pressable} onPress={…} />` type-checks
24
+ * against `Pressable`, not `View`.
19
25
  */
20
- export const Box = forwardRef<ComponentRef<typeof View>, BoxProps>(function Box(
21
- { asChild, style, children, ...rest },
26
+ export type BoxProps<T extends ElementType = typeof View> = BoxOwnProps & {
27
+ /** Render as this component instead of `View`. It must accept a `style` prop. */
28
+ as?: T;
29
+ } & Omit<ComponentPropsWithRef<T>, keyof BoxOwnProps | "as">;
30
+
31
+ /** Erased prop shape the implementation works with — the generic lives on the exported cast. */
32
+ interface BoxRenderProps extends BoxOwnProps {
33
+ as?: ElementType;
34
+ style?: StyleProp<ViewStyle>;
35
+ children?: ReactNode;
36
+ [key: string]: unknown;
37
+ }
38
+
39
+ const STYLE_KEYS = new Set<string>(BOX_STYLE_PROP_KEYS as readonly string[]);
40
+
41
+ const BoxWithRef = forwardRef<unknown, BoxRenderProps>(function Box(
42
+ { as, asChild, style, children, ...rest }: BoxRenderProps,
22
43
  ref
23
44
  ) {
24
45
  const { theme } = useUnistyles();
25
46
 
26
47
  const styleProps: Record<string, unknown> = {};
27
- const viewProps: Record<string, unknown> = {};
48
+ const elementProps: Record<string, unknown> = {};
28
49
  for (const key of Object.keys(rest)) {
29
- const value = (rest as Record<string, unknown>)[key];
50
+ const value = rest[key];
30
51
  if (STYLE_KEYS.has(key)) styleProps[key] = value;
31
- else viewProps[key] = value;
52
+ else elementProps[key] = value;
32
53
  }
33
54
 
34
55
  const mergedStyle = [styles.root, resolveBoxStyle(styleProps as BoxStyleProps, theme), style];
35
56
 
36
57
  if (asChild) {
37
58
  return (
38
- <Slot ownerName="Box" slotProps={{ ...viewProps, style: mergedStyle, ref }}>
59
+ <Slot ownerName="Box" slotProps={{ ...elementProps, style: mergedStyle, ref }}>
39
60
  {children}
40
61
  </Slot>
41
62
  );
42
63
  }
43
64
 
65
+ const Component = (as ?? View) as ElementType;
66
+
44
67
  return (
45
- <View ref={ref} style={mergedStyle} {...(viewProps as ViewProps)}>
68
+ <Component ref={ref} style={mergedStyle} {...elementProps}>
46
69
  {children}
47
- </View>
70
+ </Component>
48
71
  );
49
72
  });
50
73
 
74
+ /**
75
+ * Base layout primitive: a `View` styled through theme-token props.
76
+ * The only component family exposing token style-props.
77
+ *
78
+ * Pass `as` to cast it as another component — the token style-props still
79
+ * resolve, and the rest of the props are typed against that component.
80
+ * Note that `as` renders a component Box does not own, so a Unistyles style
81
+ * handed to it only stays theme-reactive if that component's own module runs
82
+ * through the Unistyles Babel plugin (see the stylesheet note below).
83
+ *
84
+ * @example
85
+ * ```tsx
86
+ * <Box row gap="2" p="4" />
87
+ * <Box as={Pressable} onPress={onPress} p="3" radius="md" />
88
+ * ```
89
+ */
90
+ export const Box = BoxWithRef as unknown as <T extends ElementType = typeof View>(
91
+ props: BoxProps<T>
92
+ ) => ReactElement | null;
93
+
51
94
  // ─── Styles ───────────────────────────────────────────────────────────────────
52
95
  //
53
96
  // Box has no static style of its own — this stylesheet exists so the
@@ -0,0 +1,14 @@
1
+ import { useContext } from "react";
2
+
3
+ import { useUnistyles } from "react-native-unistyles";
4
+
5
+ import type { ColorToken } from "#theme/types";
6
+ import { resolveThemeColor } from "#utils/resolve-theme-color";
7
+ import { ScreenContext } from "../../screen/ScreenContext";
8
+
9
+ /** Explicit header color wins, then Screen, then the theme canvas. */
10
+ export function useHeaderColor(token?: ColorToken) {
11
+ const { theme } = useUnistyles();
12
+ const screen = useContext(ScreenContext);
13
+ return (token ? resolveThemeColor(token, theme) : screen?.background) ?? theme.color["surface.canvas"];
14
+ }
@@ -0,0 +1,78 @@
1
+ import { useEffect, useLayoutEffect, useState } from "react";
2
+
3
+ import { interpolate, useAnimatedStyle, useSharedValue } from "react-native-reanimated";
4
+ import { useUnistyles } from "react-native-unistyles";
5
+
6
+ import { useScreen } from "../../screen/ScreenContext";
7
+ import type { HeaderAccessoryBehavior } from "../types";
8
+
9
+ interface LargeTitleLayoutOptions {
10
+ hasAccessory: boolean;
11
+ accessoryBehavior: HeaderAccessoryBehavior;
12
+ isLargeTitleHidden: boolean;
13
+ }
14
+
15
+ /** Coordinates measured layout and UI-thread animation without animating accessory padding. */
16
+ export function useLargeTitleHeader({
17
+ hasAccessory,
18
+ accessoryBehavior,
19
+ isLargeTitleHidden,
20
+ }: LargeTitleLayoutOptions) {
21
+ const { theme } = useUnistyles();
22
+ const { scrollY, contentTopPadding, setHeaderAccessoryHeight } = useScreen();
23
+ const [accessoryHeight, setAccessoryHeight] = useState(0);
24
+ const [largeTitleHeight, setLargeTitleHeight] = useState(0);
25
+ const hidden = useSharedValue(isLargeTitleHidden);
26
+ useEffect(() => {
27
+ hidden.value = isLargeTitleHidden;
28
+ }, [hidden, isLargeTitleHidden]);
29
+
30
+ const barHeight = theme.layout.header.height;
31
+ const isSticky = hasAccessory && accessoryBehavior === "sticky" && !isLargeTitleHidden;
32
+ // The measured overlay always includes bottom padding. Expanded scroll content
33
+ // excludes it; compact-only screens reserve the entire accessory.
34
+ const reservedAccessoryHeight = hasAccessory
35
+ ? Math.max(0, Math.round(accessoryHeight - (isLargeTitleHidden ? 0 : theme.spacing["2"])))
36
+ : 0;
37
+ const accessoryOffset = isSticky ? Math.max(0, contentTopPadding + largeTitleHeight - barHeight) : 0;
38
+
39
+ useLayoutEffect(() => {
40
+ setHeaderAccessoryHeight(isSticky ? 0 : reservedAccessoryHeight);
41
+ return () => setHeaderAccessoryHeight(0);
42
+ }, [isSticky, reservedAccessoryHeight, setHeaderAccessoryHeight]);
43
+
44
+ const accessoryStyle = useAnimatedStyle(() => ({
45
+ transform: [{ translateY: isSticky && !hidden.value ? Math.max(0, accessoryOffset - scrollY.value) : 0 }],
46
+ }));
47
+ const surfaceStyle = useAnimatedStyle(() => {
48
+ if (hidden.value) return { opacity: 1 };
49
+ const distance = isSticky ? accessoryOffset : barHeight;
50
+ return {
51
+ opacity: distance > 0 ? interpolate(scrollY.value, [0, distance], [0, 1], "clamp") : isSticky ? 1 : 0,
52
+ };
53
+ });
54
+ const compactTitleStyle = useAnimatedStyle(() => {
55
+ const progress = hidden.value
56
+ ? 1
57
+ : barHeight > 0
58
+ ? interpolate(scrollY.value, [0, barHeight], [0, 1], "clamp")
59
+ : 0;
60
+ return { opacity: progress, transform: [{ translateY: (1 - progress) * 8 }] };
61
+ });
62
+ const largeTitleStyle = useAnimatedStyle(() => {
63
+ const progress = barHeight > 0 ? interpolate(scrollY.value, [0, barHeight], [0, 1], "clamp") : 0;
64
+ return { opacity: 1 - progress, transform: [{ translateY: -progress * 6 }] };
65
+ });
66
+
67
+ return {
68
+ isSticky,
69
+ accessoryOffset,
70
+ reservedAccessoryHeight,
71
+ setAccessoryHeight,
72
+ setLargeTitleHeight,
73
+ accessoryStyle,
74
+ surfaceStyle,
75
+ compactTitleStyle,
76
+ largeTitleStyle,
77
+ };
78
+ }
@@ -1,37 +1,53 @@
1
- import { type ReactNode, useCallback, useEffect } from "react";
2
- import { type LayoutChangeEvent, type StyleProp, View, type ViewStyle } from "react-native";
1
+ import { type ComponentProps, type ReactNode, useCallback, useLayoutEffect, useRef } from "react";
2
+ import { type LayoutChangeEvent, View } from "react-native";
3
3
 
4
4
  import Animated from "react-native-reanimated";
5
5
  import { StyleSheet } from "react-native-unistyles";
6
6
 
7
7
  import type { HeaderAccessoryPadding } from "../types";
8
+ import { HeaderSurface, type HeaderSurfaceProps } from "./HeaderSurface";
8
9
 
9
10
  export interface HeaderAccessoryProps {
10
11
  children: ReactNode;
11
12
  /** Horizontal padding around the accessory. */
12
13
  padding: HeaderAccessoryPadding;
14
+ color: string;
15
+ surfaceStyle?: HeaderSurfaceProps["animatedStyle"];
13
16
  /** Reports layout height in dp; reports zero on cleanup to release reserved space. */
14
- onHeightChange: (height: number) => void;
17
+ onHeightChange?: (height: number) => void;
15
18
  /** Additional styles for the accessory container. */
16
- style?: StyleProp<ViewStyle>;
19
+ style?: ComponentProps<typeof Animated.View>["style"];
17
20
  }
18
21
 
19
22
  /** Measures accessory content so the header can reserve space in the scroll container. */
20
- export function HeaderAccessory({ children, padding, onHeightChange, style }: HeaderAccessoryProps) {
23
+ export function HeaderAccessory({
24
+ children,
25
+ padding,
26
+ color,
27
+ surfaceStyle,
28
+ onHeightChange,
29
+ style,
30
+ }: HeaderAccessoryProps) {
31
+ const measuredHeight = useRef(0);
21
32
  const handleLayout = useCallback(
22
33
  (event: LayoutChangeEvent) => {
23
- onHeightChange(event.nativeEvent.layout.height);
34
+ measuredHeight.current = event.nativeEvent.layout.height;
35
+ onHeightChange?.(measuredHeight.current);
24
36
  },
25
37
  [onHeightChange]
26
38
  );
27
39
 
28
40
  // Release the previous owner’s space when the callback changes or the accessory unmounts.
29
- useEffect(() => () => onHeightChange(0), [onHeightChange]);
41
+ useLayoutEffect(() => {
42
+ onHeightChange?.(measuredHeight.current);
43
+ return () => onHeightChange?.(0);
44
+ }, [onHeightChange]);
30
45
 
31
46
  styles.useVariants({ padding });
32
47
 
33
48
  return (
34
49
  <Animated.View style={[styles.accessory, style]} onLayout={handleLayout}>
50
+ <HeaderSurface color={color} animatedStyle={surfaceStyle} accessory />
35
51
  {children}
36
52
  </Animated.View>
37
53
  );
@@ -44,6 +60,9 @@ export function HeaderAccessorySpacer({ height }: { height: number }) {
44
60
 
45
61
  const styles = StyleSheet.create((theme) => ({
46
62
  accessory: {
63
+ // Static layout padding is intentional: the background must measure the full
64
+ // padded height even when a horizontal ScrollView is the accessory content.
65
+ paddingBottom: theme.spacing["2"],
47
66
  variants: {
48
67
  padding: {
49
68
  x: { paddingHorizontal: theme.layout.screen.padding.x },
@@ -0,0 +1,125 @@
1
+ import type { ComponentProps, ReactNode } from "react";
2
+ import { Text, View } from "react-native";
3
+
4
+ import Animated from "react-native-reanimated";
5
+ import { StyleSheet, useUnistyles } from "react-native-unistyles";
6
+
7
+ import { FONT_WEIGHT_VALUE } from "#utils/typography";
8
+ import { Toolbar } from "../../toolbar";
9
+ import { HeaderDismissButton, type HeaderDismissProps } from "../header-buttons";
10
+ import { isCustomTitle, isTextTitle } from "../utils";
11
+ import { HeaderSurface, type HeaderSurfaceProps } from "./HeaderSurface";
12
+
13
+ interface HeaderBarProps extends HeaderDismissProps {
14
+ title?: ReactNode;
15
+ leading?: ReactNode;
16
+ trailing?: ReactNode;
17
+ titleAlignment: "center" | "left";
18
+ color: string;
19
+ surfaceStyle?: HeaderSurfaceProps["animatedStyle"];
20
+ titleStyle?: ComponentProps<typeof Animated.View>["style"];
21
+ }
22
+
23
+ /** Shared compact bar. Only the title and background animate; actions stay interactive. */
24
+ export function HeaderBar({
25
+ title,
26
+ leading,
27
+ trailing,
28
+ titleAlignment,
29
+ color,
30
+ surfaceStyle,
31
+ titleStyle,
32
+ dismissAction = "none",
33
+ navigation,
34
+ onDismiss,
35
+ }: HeaderBarProps) {
36
+ const { theme } = useUnistyles();
37
+ styles.useVariants({ titleAlignment });
38
+ const dismiss = (
39
+ <HeaderDismissButton dismissAction={dismissAction} navigation={navigation} onDismiss={onDismiss} />
40
+ );
41
+ return (
42
+ <View style={styles.bar}>
43
+ <HeaderSurface color={color} animatedStyle={surfaceStyle} />
44
+ {(leading != null || dismissAction === "back") && (
45
+ <Toolbar>
46
+ {dismissAction === "back" && dismiss}
47
+ {leading}
48
+ </Toolbar>
49
+ )}
50
+ <Animated.View
51
+ style={[styles.titleContainer, titleStyle]}
52
+ pointerEvents={isCustomTitle(title) ? "box-none" : "none"}
53
+ >
54
+ {isTextTitle(title)
55
+ ? String(title).trim().length > 0 && (
56
+ <Text
57
+ style={styles.title}
58
+ maxFontSizeMultiplier={theme.typography.variants["title-md"].maxFontSizeMultiplier}
59
+ >
60
+ {title}
61
+ </Text>
62
+ )
63
+ : title}
64
+ </Animated.View>
65
+ {(trailing != null || dismissAction === "close") && (
66
+ <Toolbar style={styles.trailing}>
67
+ {dismissAction === "close" && dismiss}
68
+ {trailing}
69
+ </Toolbar>
70
+ )}
71
+ </View>
72
+ );
73
+ }
74
+
75
+ const styles = StyleSheet.create((theme) => ({
76
+ bar: {
77
+ height: theme.layout.header.height,
78
+ flexDirection: "row",
79
+ alignItems: "center",
80
+ paddingHorizontal: theme.layout.screen.padding.x,
81
+ gap: theme.spacing["3"],
82
+ justifyContent: "space-between",
83
+ },
84
+ // A centered title is absolutely positioned, so it leaves the flex row. With
85
+ // no leading slot the trailing toolbar would be the only in-flow child and
86
+ // `space-between` would park it on the left — the auto margin pins it right.
87
+ trailing: {
88
+ justifyContent: "flex-end",
89
+ variants: {
90
+ titleAlignment: {
91
+ center: { marginLeft: "auto" },
92
+ left: {},
93
+ },
94
+ },
95
+ },
96
+ titleContainer: {
97
+ variants: {
98
+ titleAlignment: {
99
+ center: {
100
+ position: "absolute",
101
+ left: 0,
102
+ right: 0,
103
+ alignItems: "center",
104
+ },
105
+ left: {
106
+ flex: 1,
107
+ alignItems: "flex-start",
108
+ },
109
+ },
110
+ },
111
+ },
112
+ title: {
113
+ fontSize: theme.typography.variants["title-md"].fontSize,
114
+ lineHeight: theme.typography.variants["title-md"].lineHeight,
115
+ letterSpacing: theme.typography.variants["title-md"].letterSpacing,
116
+ fontWeight: FONT_WEIGHT_VALUE[theme.typography.variants["title-md"].fontWeight],
117
+ color: (theme.color as unknown as Record<string, string | undefined>)["text.primary"] ?? "",
118
+ variants: {
119
+ titleAlignment: {
120
+ center: { textAlign: "center" },
121
+ left: { textAlign: "left" },
122
+ },
123
+ },
124
+ },
125
+ }));