Skip to content

Commit 99588fa

Browse files
committed
chore: improve JSDoc, tighten typings and update package keywords
- Expand and clarify JSDoc for SizedWebView, useAutoHeight and composeInjectedScript (more examples, usage notes and param docs) - Use explicit FC import and replace React.FC in SizedWebView - Reorder/clean up exports in src/index.ts - Add a broader set of keywords to package.json to improve discovery
1 parent be0288a commit 99588fa

File tree

5 files changed

+412
-19
lines changed

5 files changed

+412
-19
lines changed

package.json

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,25 @@
4646
},
4747
"keywords": [
4848
"react-native",
49+
"webview",
4950
"ios",
50-
"android"
51+
"android",
52+
"auto-size",
53+
"auto-height",
54+
"responsive",
55+
"html-rendering",
56+
"web-content",
57+
"javascript-bridge",
58+
"typescript",
59+
"component",
60+
"hook",
61+
"sizing",
62+
"layout",
63+
"dynamic-height",
64+
"content-measurement",
65+
"mobile",
66+
"native",
67+
"performance"
5168
],
5269
"repository": {
5370
"type": "git",

src/components/SizedWebView.tsx

Lines changed: 124 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { useCallback, useMemo } from 'react';
2+
import type { FC } from 'react';
23
import { View, type StyleProp, type ViewStyle } from 'react-native';
34
import {
45
WebView,
@@ -10,26 +11,141 @@ import { AUTO_HEIGHT_BRIDGE } from '../constants/autoHeightBridge';
1011
import { useAutoHeight } from '../hooks/useAutoHeight';
1112
import { composeInjectedScript } from '../utils/composeInjectedScript';
1213

14+
/**
15+
* Props for the SizedWebView component.
16+
*
17+
* Extends all standard WebViewProps with additional auto-sizing capabilities.
18+
*
19+
* @example
20+
* ```tsx
21+
* <SizedWebView
22+
* source={{ html: '<h1>Hello World</h1>' }}
23+
* minHeight={100}
24+
* onHeightChange={(height) => console.log(`New height: ${height}`)}
25+
* />
26+
* ```
27+
*/
1328
export interface SizedWebViewProps extends WebViewProps {
1429
/**
15-
* Minimum height (in dp) applied to the WebView container.
16-
* Useful to avoid layout shifts on the initial render while the content height loads.
30+
* Minimum height (in dp/points) for the WebView container.
31+
*
32+
* Useful to:
33+
* - Avoid layout shifts during initial render while content height is loading
34+
* - Prevent the WebView from becoming too small if content is minimal
35+
* - Provide a baseline height for skeleton/loading states
36+
*
37+
* @default 0
1738
*/
1839
minHeight?: number;
19-
/** Optional styles applied to the wrapping `View` that hosts the WebView. */
40+
41+
/**
42+
* Style object applied to the wrapping `View` container that hosts the WebView.
43+
*
44+
* Use this to add padding, margins, borders, or other styling to the container.
45+
* Note: Do not set `height` here—it's automatically managed by the component.
46+
*
47+
* @example
48+
* ```tsx
49+
* containerStyle={{ paddingHorizontal: 12, borderRadius: 8 }}
50+
* ```
51+
*/
2052
containerStyle?: StyleProp<ViewStyle>;
53+
2154
/**
22-
* Callback invoked whenever the auto-calculated height changes.
23-
* Receives the committed height (in dp).
55+
* Callback fired whenever the WebView's auto-calculated height changes.
56+
*
57+
* This is useful for:
58+
* - Analytics tracking when content size changes
59+
* - Triggering animations or other side effects
60+
* - Syncing height with parent layout logic
61+
*
62+
* The callback receives the new committed height in density-independent pixels (dp).
63+
*
64+
* @param height - The new height in dp/points
65+
*
66+
* @example
67+
* ```tsx
68+
* onHeightChange={(height) => {
69+
* console.log(`WebView height updated to: ${height}dp`);
70+
* }}
71+
* ```
2472
*/
2573
onHeightChange?: (height: number) => void;
2674
}
2775

2876
/**
29-
* High-level WebView wrapper that automatically grows to match its HTML content height.
30-
* The component stays scroll-less and keeps the parent scroll view in control.
77+
* A React Native WebView component that automatically sizes itself to fit its HTML content.
78+
*
79+
* ## Overview
80+
* `SizedWebView` wraps the standard React Native `WebView` component and automatically
81+
* adjusts its height to match the rendered HTML content. This prevents layout flicker,
82+
* eliminates manual height calculations, and keeps scroll control with the parent ScrollView.
83+
*
84+
* ## Key Features
85+
* - ✅ Automatic height adjustment based on HTML content
86+
* - ✅ Smooth rendering with no layout flicker
87+
* - ✅ Scroll control remains with parent container
88+
* - ✅ Supports both local HTML and external URLs
89+
* - ✅ Configurable minimum height to prevent excessive shrinking
90+
* - ✅ Height change notifications via callback
91+
*
92+
* ## How it Works
93+
* 1. An injected JavaScript bridge measures the HTML content's height
94+
* 2. The height value is sent back to the native layer
95+
* 3. The component updates its container size on each frame
96+
* 4. The WebView stays scroll-disabled to prevent internal scrolling
97+
*
98+
* ## Usage Example
99+
* ```tsx
100+
* import { SizedWebView } from 'react-native-sized-webview';
101+
*
102+
* export function MyComponent() {
103+
* return (
104+
* <ScrollView>
105+
* <SizedWebView
106+
* source={{ html: '<h1>Dynamic Content</h1>' }}
107+
* minHeight={100}
108+
* containerStyle={{ marginVertical: 10 }}
109+
* onHeightChange={(height) => console.log(`Height: ${height}dp`)}
110+
* />
111+
* </ScrollView>
112+
* );
113+
* }
114+
* ```
115+
*
116+
* ## Props
117+
* - All standard `WebViewProps` are supported
118+
* - Plus 3 additional props: `minHeight`, `containerStyle`, `onHeightChange`
119+
*
120+
* ## Important Notes
121+
* - The component disables scroll by default (`scrollEnabled={false}`)
122+
* - Origin whitelist defaults to `['*']` to allow all sources
123+
* - JavaScript is automatically enabled for the height bridge to work
124+
* - Minimum height is always enforced to avoid layout issues
125+
*
126+
* @component
127+
* @param props - SizedWebViewProps containing all configuration options
128+
* @returns A View containing the auto-sized WebView
129+
*
130+
* @example
131+
* ```tsx
132+
* // With inline HTML
133+
* <SizedWebView
134+
* source={{ html: '<p>Hello</p>' }}
135+
* minHeight={50}
136+
* />
137+
* ```
138+
*
139+
* @example
140+
* ```tsx
141+
* // With external URL
142+
* <SizedWebView
143+
* source={{ uri: 'https://example.com' }}
144+
* containerStyle={{ borderRadius: 8 }}
145+
* />
146+
* ```
31147
*/
32-
export const SizedWebView: React.FC<SizedWebViewProps> = ({
148+
export const SizedWebView: FC<SizedWebViewProps> = ({
33149
minHeight = 0,
34150
containerStyle,
35151
style,

src/hooks/useAutoHeight.ts

Lines changed: 155 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,175 @@
11
import { useCallback, useEffect, useRef, useState } from 'react';
22

3+
/**
4+
* Configuration options for the useAutoHeight hook.
5+
*
6+
* @interface UseAutoHeightOptions
7+
*/
38
export interface UseAutoHeightOptions {
4-
/** Minimum height (in dp) enforced for the WebView container. */
9+
/**
10+
* Minimum height (in dp/points) enforced for the WebView container.
11+
*
12+
* Prevents the component from becoming too small when content is minimal.
13+
* The actual height will be: `max(minHeight, measuredContentHeight)`
14+
*
15+
* @default 0
16+
*/
517
minHeight: number;
6-
/** Optional callback triggered whenever a new height is committed. */
18+
19+
/**
20+
* Optional callback triggered whenever a new height is committed.
21+
*
22+
* This callback fires after the height has been validated, throttled by the
23+
* HEIGHT_DIFF_THRESHOLD, and scheduled for the next animation frame.
24+
*
25+
* Use this callback for:
26+
* - Analytics or logging height changes
27+
* - Triggering dependent UI updates
28+
* - Syncing with external state management
29+
*
30+
* The callback is not called for changes smaller than 1dp to avoid noise.
31+
*
32+
* @param height - The new committed height in dp/points
33+
*
34+
* @example
35+
* ```ts
36+
* onHeightChange={(height) => {
37+
* analytics.track('webview_height_change', { height });
38+
* }}
39+
* ```
40+
*/
741
onHeightChange?: (height: number) => void;
842
}
943

44+
/**
45+
* Return value from the useAutoHeight hook.
46+
*
47+
* @interface UseAutoHeightResult
48+
*/
1049
export interface UseAutoHeightResult {
11-
/** Current height applied to the WebView container. */
50+
/**
51+
* The current height applied to the WebView container in dp/points.
52+
*
53+
* This value is:
54+
* - At least `minHeight` (respects minimum)
55+
* - Updated smoothly using requestAnimationFrame
56+
* - Batched to avoid excessive re-renders
57+
* - Only committed if change exceeds 1dp threshold
58+
*
59+
* @type {number}
60+
*/
1261
height: number;
62+
1363
/**
14-
* Helper that parses any incoming payload (from the WebView bridge) and
15-
* commits a new height when appropriate.
64+
* Parser and dispatcher for incoming height payloads from the WebView bridge.
65+
*
66+
* This function should be called with any data received from the injected JavaScript
67+
* bridge. It handles:
68+
* - Type coercion (number strings are converted to numbers)
69+
* - Validation (finite positive numbers only)
70+
* - Throttling (changes < 1dp are ignored)
71+
* - Scheduling (uses requestAnimationFrame for smooth updates)
72+
*
73+
* Safe to call with any value—invalid inputs are silently ignored.
74+
*
75+
* @param rawValue - Any value received from the WebView bridge
76+
*
77+
* @example
78+
* ```ts
79+
* const { height, setHeightFromPayload } = useAutoHeight({ minHeight: 100 });
80+
*
81+
* const handleMessage = (event) => {
82+
* setHeightFromPayload(event.nativeEvent.data);
83+
* };
84+
* ```
1685
*/
1786
setHeightFromPayload: (rawValue: unknown) => void;
1887
}
1988

89+
/**
90+
* Threshold (in dp) below which height changes are ignored.
91+
*
92+
* Prevents excessive re-renders from minor content layout fluctuations.
93+
* @internal
94+
*/
2095
const HEIGHT_DIFF_THRESHOLD = 1;
2196

2297
/**
23-
* React hook encapsulating the logic for tracking and updating the WebView height.
24-
* Ensures we avoid unnecessary renders while keeping the implementation testable.
98+
* React hook for managing automatic WebView height calculation and updates.
99+
*
100+
* ## Overview
101+
* This hook encapsulates all the logic needed to:
102+
* 1. Track the WebView's content height
103+
* 2. Validate and normalize incoming height values
104+
* 3. Throttle updates to prevent layout thrashing
105+
* 4. Schedule updates on the next animation frame
106+
* 5. Invoke callbacks when height changes
107+
*
108+
* ## Features
109+
* - 🎯 Enforces minimum height to prevent shrinking below acceptable bounds
110+
* - ⚡ Batches updates using requestAnimationFrame for smooth 60fps rendering
111+
* - 🚫 Ignores changes smaller than 1dp to reduce noise
112+
* - 🔒 Type-safe with strong validation of incoming values
113+
* - 🧪 Fully testable with deterministic behavior
114+
* - 🧹 Automatically cleans up animation frame requests on unmount
115+
*
116+
* ## How it Works
117+
* ```
118+
* WebView Bridge sends height → setHeightFromPayload() validates
119+
* → scheduleCommit() batches update
120+
* → requestAnimationFrame executes flushPendingHeight()
121+
* → commitHeight() updates state + callback
122+
* ```
123+
*
124+
* ## Usage Example
125+
* ```ts
126+
* import { useAutoHeight } from 'react-native-sized-webview';
127+
*
128+
* function MyComponent() {
129+
* const { height, setHeightFromPayload } = useAutoHeight({
130+
* minHeight: 100,
131+
* onHeightChange: (h) => console.log(`Height: ${h}dp`),
132+
* });
133+
*
134+
* const handleMessage = (event) => {
135+
* setHeightFromPayload(event.nativeEvent.data);
136+
* };
137+
*
138+
* return (
139+
* <View style={{ height }}>
140+
* <WebView onMessage={handleMessage} />
141+
* </View>
142+
* );
143+
* }
144+
* ```
145+
*
146+
* ## Performance Considerations
147+
* - Uses requestAnimationFrame to sync with 60fps screen refresh
148+
* - Only processes height changes exceeding 1dp threshold
149+
* - Maintains refs to avoid unnecessary re-render triggers
150+
* - Properly cancels pending frames on unmount
151+
*
152+
* @param options - Configuration object with minHeight and optional onHeightChange callback
153+
* @returns Object containing current height and payload processor function
154+
*
155+
* @throws Does not throw—all invalid inputs are silently ignored
156+
*
157+
* @example
158+
* ```ts
159+
* // Basic usage with defaults
160+
* const { height, setHeightFromPayload } = useAutoHeight({ minHeight: 50 });
161+
* ```
162+
*
163+
* @example
164+
* ```ts
165+
* // With callback to track changes
166+
* const { height, setHeightFromPayload } = useAutoHeight({
167+
* minHeight: 100,
168+
* onHeightChange: (newHeight) => {
169+
* analytics.track('webview_resized', { newHeight });
170+
* },
171+
* });
172+
* ```
25173
*/
26174
export const useAutoHeight = (
27175
options: UseAutoHeightOptions

0 commit comments

Comments
 (0)