npm package: @rokku-x/roks-rn-utils
Description: Utilities for webviews in React Native
Author: rokku-x
Latest version: 0.2.0

# roks-rn-utils

Utilities for webviews in React Native.

## Features

- Safe-area CSS variables injected into webviews
- Hook and component for keyboard-aware updates
- TypeScript types and ESM/CJS builds

## Installation

```bash
npm install @rokku-x/roks-rn-utils
# or
yarn add @rokku-x/roks-rn-utils
# or
bun add @rokku-x/roks-rn-utils
```

**Important:** This library requires `react-native-safe-area-context`. Make sure to install it if you haven't already:

```bash
npm install react-native-safe-area-context
# or
yarn add react-native-safe-area-context
# or
bun add react-native-safe-area-context
```

## Usage

### Option 1: Using the component (easiest)

The `SafeInsetWebviewInjectionComponent` includes its own `SafeAreaProvider`, so no additional setup is required:

```tsx
import React, { useRef } from 'react';
import WebView from 'react-native-webview';
import { SafeInsetWebviewInjectionComponent } from '@rokku-x/roks-rn-utils';

export function WebviewScreen() {
  const webviewRef = useRef<WebView>(null);

  return (
    <>
      <SafeInsetWebviewInjectionComponent webview={webviewRef.current!} />
      <WebView
        ref={webviewRef}
        source={{ html: '<body>Hello</body>' }}
      />
    </>
  );
}
```

### Option 2: Using the hook

If you use the hook directly, your app must be wrapped with `SafeAreaProvider` from `react-native-safe-area-context`:

```tsx
import { SafeAreaProvider } from 'react-native-safe-area-context';

export default function App() {
  return (
    <SafeAreaProvider>
      {/* Your app content */}
    </SafeAreaProvider>
  );
}
```

Then use the hook in your component

```tsx
import React, { useRef } from 'react';
import WebView from 'react-native-webview';
import { useSafeInsetWebviewInjection } from '@rokku-x/roks-rn-utils';

export function CustomWebviewScreen() {
  const webviewRef = useRef<WebView>(null);

  useSafeInsetWebviewInjection(webviewRef.current!);

  return (
    <WebView
      ref={webviewRef}
      source={{ html: '<body>Hello</body>' }}
    />
  );
}
```

> **Note:** When using the hook, your component must be a descendant of `SafeAreaProvider` to work properly.

### CSS inside your webview

Use the injected custom properties in your HTML/CSS:

```css
body {
  padding-top: var(--safe-area-inset-top-injected);
  padding-bottom: var(--safe-area-inset-bottom-injected);
  padding-left: var(--safe-area-inset-left-injected);
  padding-right: var(--safe-area-inset-right-injected);
}

.toolbar {
  margin-top: var(--safe-area-inset-top-injected);
}

/* Use -initial variants for the original insets before keyboard changes */
.fixed-header {
  height: calc(60px + var(--safe-area-inset-top-initial));
}
```

### Reference tables

| Item | Description |
| --- | --- |
| `--safe-area-inset-top-injected` | CSS custom property with the current top safe-area inset in px. |
| `--safe-area-inset-bottom-injected` | CSS custom property with the current bottom safe-area inset in px. |
| `--safe-area-inset-left-injected` | CSS custom property with the current left safe-area inset in px. |
| `--safe-area-inset-right-injected` | CSS custom property with the current right safe-area inset in px. |
| `--safe-area-inset-top-initial` | CSS custom property with the initial top safe-area inset in px (before layout changes). |
| `--safe-area-inset-bottom-initial` | CSS custom property with the initial bottom safe-area inset in px (before layout changes). |
| `--safe-area-inset-left-initial` | CSS custom property with the initial left safe-area inset in px (before layout changes). |
| `--safe-area-inset-right-initial` | CSS custom property with the initial right safe-area inset in px (before layout changes). |
| `useSafeInsetWebviewInjection(webview, insets?)` | Hook that injects the CSS variables into a given WebView instance and keeps them updated (keyboard show/hide). |

## API

-- `useSafeInsetWebviewInjection(webview, insets?)`: Hook that injects safe-area CSS variables into the current webview and keeps them updated when the keyboard toggles. Requires `SafeAreaProvider` as a parent component. (returns `{ refreshInitial: () => void; forceInject: () => void }`)

Returns an object with the following shape:

```ts
{
  refreshInitial: () => void; // dismiss keyboard and inject initial inset values
  forceInject: () => void;    // force immediate re-injection of current inset values
}
```
- `SafeInsetWebviewInjectionComponent`: Component that handles safe-area injection for webviews. Includes its own `SafeAreaProvider`, so no additional setup is required.

## Requirements

- Node.js >= 18
- React Native >= 0.70
- `react-native-safe-area-context` (peer dependency)
- `SafeAreaProvider` wrapper required only when using the hook directly (the component includes its own provider)

## Development

```bash
bun install
bun run build
bun test
```

## License

MIT