@butternutbox/pawprint-native 0.17.0 → 0.18.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.
@@ -1,5 +1,11 @@
1
- import React, { useMemo } from "react"
2
- import { Image, ImageSourcePropType, View, ViewProps } from "react-native"
1
+ import React, { useCallback, useMemo } from "react"
2
+ import {
3
+ Image,
4
+ ImageSourcePropType,
5
+ View,
6
+ ViewProps,
7
+ type LayoutChangeEvent
8
+ } from "react-native"
3
9
  import styled from "@emotion/native"
4
10
  import { useTheme } from "@emotion/react"
5
11
  import { DrawerClose } from "./DrawerClose"
@@ -8,6 +14,7 @@ import {
8
14
  type DrawerHeaderVariant
9
15
  } from "./DrawerHeaderContext"
10
16
  import { useDrawerDragContext } from "./DrawerDragContext"
17
+ import { useDrawerMeasureContext } from "./DrawerMeasureContext"
11
18
 
12
19
  const parseTokenValue = (value: string): number => parseFloat(value)
13
20
 
@@ -139,11 +146,25 @@ function splitChildren(children: React.ReactNode) {
139
146
  * @param {ImageSourcePropType} [imageSource] - Image source for the `image` variant
140
147
  */
141
148
  export const DrawerHeader = React.forwardRef<View, DrawerHeaderProps>(
142
- ({ variant = "titleAndText", imageSource, children, ...props }, ref) => {
149
+ (
150
+ { variant = "titleAndText", imageSource, children, onLayout, ...props },
151
+ ref
152
+ ) => {
143
153
  const theme = useTheme()
144
154
  const { drawer, grabber } = theme.tokens.components
145
155
  const { colour, dimensions } = theme.tokens.semantics
146
156
  const dragContext = useDrawerDragContext()
157
+ const measureContext = useDrawerMeasureContext()
158
+
159
+ // Report the header's measured height so DrawerContent can reserve it as
160
+ // chrome directly (rather than deriving it from the panel minus the body).
161
+ const handleLayout = useCallback(
162
+ (event: LayoutChangeEvent): void => {
163
+ measureContext?.setHeaderHeight(event.nativeEvent.layout.height)
164
+ onLayout?.(event)
165
+ },
166
+ [measureContext, onLayout]
167
+ )
147
168
 
148
169
  const contextValue = useMemo(() => ({ variant }), [variant])
149
170
  const { closeChild, otherChildren } = splitChildren(children)
@@ -219,6 +240,7 @@ export const DrawerHeader = React.forwardRef<View, DrawerHeaderProps>(
219
240
  ? drawer.drawerHeader.colour.background.fullBleed
220
241
  : undefined
221
242
  }
243
+ onLayout={handleLayout}
222
244
  {...props}
223
245
  >
224
246
  <StyledGrabberWrapper
@@ -1,14 +1,17 @@
1
1
  import { createContext, useContext } from "react"
2
2
 
3
3
  /**
4
- * Lets `DrawerBody` report its scroll content's natural height up to
5
- * `DrawerContent`, which uses it to re-fit the panel when the content grows or
6
- * shrinks after open. A ScrollView's content size is measured in an unbounded
7
- * container, so it reflects the true content height independently of the
8
- * ScrollView's own (bounded) frame no measurement feedback loop.
4
+ * Lets the header and footer report their measured heights up to
5
+ * `DrawerContent`, which uses them to derive the body ScrollView's chrome-aware
6
+ * `maxHeight` (`90%·window header footer border`). Measuring the chrome
7
+ * directly (not by subtraction) keeps that cap correct even when the body grows
8
+ * after the first layout, so tall content scrolls inside the cap and the footer
9
+ * is never clipped. The panel itself wraps its content, so there is no body
10
+ * content-height measurement and no feedback loop.
9
11
  */
10
12
  export type DrawerMeasureContextValue = {
11
- setBodyContentHeight: (height: number) => void
13
+ setHeaderHeight: (height: number) => void
14
+ setFooterHeight: (height: number) => void
12
15
  }
13
16
 
14
17
  export const DrawerMeasureContext =
@@ -19,7 +19,7 @@ export const DrawerOverlay = React.forwardRef<
19
19
  DrawerOverlayProps
20
20
  >(({ accessible = false }, _ref) => {
21
21
  const theme = useTheme()
22
- const { isOpen, closeDrawer } = useDrawerContext()
22
+ const { isOpen, closeDrawer, backdrop } = useDrawerContext()
23
23
  const opacity = useRef(new Animated.Value(0)).current
24
24
 
25
25
  useEffect(() => {
@@ -30,6 +30,9 @@ export const DrawerOverlay = React.forwardRef<
30
30
  }).start()
31
31
  }, [isOpen, opacity])
32
32
 
33
+ // A backdrop-less drawer omits the dimming/tap-to-dismiss overlay entirely.
34
+ if (!backdrop) return null
35
+
33
36
  return (
34
37
  <Pressable
35
38
  style={StyleSheet.absoluteFillObject}
@@ -0,0 +1,21 @@
1
+ import { createContext, useContext, type ComponentType } from "react"
2
+
3
+ /**
4
+ * Optional app-provided component rendered on top of *every* modal drawer,
5
+ * inside the drawer's native `Modal` window.
6
+ *
7
+ * A native `Modal` is a separate OS window, so anything mounted at the app root
8
+ * — e.g. a toast/notification stack — is painted *behind* the drawer. Pointing
9
+ * this context at that root overlay lets the app render a second instance inside
10
+ * the drawer's Modal, so it surfaces above the drawer for all modal drawers at
11
+ * once, with no per-drawer wiring. React context flows through RN Modals, so the
12
+ * in-Modal instance reads the same app state as the root one.
13
+ *
14
+ * Defaults to `null` (nothing extra rendered) — fully opt-in and backward
15
+ * compatible. Only applies to modal (backdrop) drawers; backdrop-less drawers
16
+ * render inline, where the app-root overlay already paints on top.
17
+ */
18
+ export const DrawerOverlayContext = createContext<ComponentType | null>(null)
19
+
20
+ export const useDrawerOverlay = (): ComponentType | null =>
21
+ useContext(DrawerOverlayContext)
@@ -2,7 +2,8 @@ export { Drawer } from "./Drawer"
2
2
  export type {
3
3
  DrawerProps,
4
4
  DrawerTriggerProps,
5
- DrawerPortalProps
5
+ DrawerPortalProps,
6
+ DrawerOverlayProviderProps
6
7
  } from "./Drawer"
7
8
  export type { DrawerContentProps } from "./DrawerContent"
8
9
  export type { DrawerOverlayProps } from "./DrawerOverlay"