# @herongxhr/network-status

![NPM Version](https://img.shields.io/npm/v/@herongxhr/network-status.svg)
![NPM Downloads](https://img.shields.io/npm/dm/@herongxhr/network-status.svg)
![License](https://img.shields.io/npm/l/@herongxhr/network-status.svg)
![Build Status](https://img.shields.io/github/actions/workflow/status/xai/@herongxhr/network-status/ci.yml)

A lightweight Vue 3 composable and optional template for real-time network status monitoring. This package provides a flexible `useNetworkStatus` composable to manage network detection and signal strength, along with a customizable `NetworkStatusTemplate` component styled with Tailwind CSS for quick integration.

## Features

- **Real-time Network Detection**: Monitors network status using `fetch` requests with customizable check intervals.
- **Signal Strength Visualization**: Displays 0-4 signal bars based on latency thresholds.
- **Customizable Alerts**: Configurable alert messages for offline, weak, or lost signals with 3-second auto-dismiss.
- **Latency Formatting**: Supports customizable decimal places for latency display (e.g., `123.4` ms).
- **Positioning**: Flexible alert positioning (`top-right`, `bottom-left`, `center`, etc.).
- **Headless Design**: Use the composable with your own UI or the provided Tailwind-styled template.
- **TypeScript Support**: Full type definitions for seamless integration.

## Installation

Install the package via npm:

```bash
npm install @herongxhr/network-status
```

### Peer Dependency

Requires **Vue 3**:

```bash
npm install vue@3
```

## Usage

### 1. Using the `useNetworkStatus` Composable

The `useNetworkStatus` composable provides reactive state for network status, latency, and alerts. Integrate it into any Vue 3 component with your custom UI.

#### Example: Custom UI with Plain CSS

```vue
<template>
  <div class="network-container">
    <div class="signal-bars">
      <div
        v-for="(active, index) in signalBars"
        :key="index"
        :class="['bar', active ? signalClass : 'bg-gray-300']"
        :style="{ height: `${(index + 1) * 4}px` }"
      ></div>
    </div>
    <span class="status-text">{{ statusText }}</span>
    <span class="latency">{{ formattedLatency }} ms</span>
    <div
      v-if="showAlert"
      class="alert"
      :style="getPositionStyle(alertPosition)"
    >
      {{ alertMessage }}
      <button class="close-btn" @click="closeAlert">×</button>
    </div>
  </div>
</template>

<script setup lang="ts">
import { useNetworkStatus } from "@herongxhr/network-status";

const {
  networkStatus,
  formattedLatency,
  signalBars,
  statusText,
  showAlert,
  alertMessage,
  closeAlert,
  alertPosition,
} = useNetworkStatus({
  checkUrl: "https://your-api-endpoint.com/health",
  statusTexts: ["No Signal", "Very Weak", "Weak", "Good", "Excellent"],
  alertMessages: ["Offline!", "Signal lost!", "Signal weak!"],
  checkInterval: 3000,
  decimalPlaces: 1,
});

const signalClass = computed(() => {
  const status = networkStatus.value;
  if (status === 0) return "bg-gray-500";
  if (status <= 1) return "bg-red-500";
  if (status === 2) return "bg-yellow-500";
  if (status === 3) return "bg-green-500";
  return "bg-green-700";
});

const getPositionStyle = (position: string) => {
  const [vertical, horizontal] = position.split("-");
  const style: { [key: string]: string } = {};
  if (vertical === "top") style.top = "1rem";
  else if (vertical === "bottom") style.bottom = "1rem";
  if (horizontal === "left") style.left = "1rem";
  else if (horizontal === "right") style.right = "1rem";
  else if (horizontal === "center") {
    style.left = "50%";
    style.transform = "translateX(-50%)";
  }
  return style;
};
</script>

<style scoped>
.network-container {
  position: absolute;
  top: 10px;
  right: 10px;
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 6px;
  background: #fff;
  border: 1px solid #e2e8f0;
  border-radius: 4px;
}
.signal-bars {
  display: flex;
  gap: 2px;
}
.bar {
  width: 4px;
  transition: all 0.3s ease;
}
.status-text {
  font-size: 12px;
  color: #2d3748;
}
.latency {
  font-size: 12px;
  color: #4a5568;
}
.alert {
  position: fixed;
  padding: 8px 12px;
  background: #fef2f2;
  color: #741a1a;
  border-radius: 4px;
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
.close-btn {
  margin-left: 8px;
  border: none;
  background: none;
  color: #741a1a;
  cursor: pointer;
}
</style>
```

#### Notes

- **Custom `checkUrl`**: Provide a reliable health check endpoint for accurate latency detection. Defaults to the current host's root (`/`) with a warning.
- **Latency Formatting**: Use `decimalPlaces` to control the precision of `formattedLatency` (e.g., `1` for `123.4` ms).

### 2. Using the `NetworkStatusTemplate` Component

The `NetworkStatusTemplate` component provides a pre-built UI styled with Tailwind CSS.

#### Example: Using Template with Tailwind CSS

```vue
<template>
  <NetworkStatusTemplate
    position="bottom-right"
    position-offset="1.5rem"
    container-class="shadow-md bg-white dark:bg-gray-800"
    alert-class="bg-red-100 text-red-800"
    :status-texts="['No Signal', 'Very Weak', 'Weak', 'Good', 'Excellent']"
    :alert-messages="['Offline!', 'Signal lost!', 'Signal weak!']"
    check-url="https://your-api-endpoint.com/health"
    check-interval="5000"
    decimal-places="2"
  />
</template>

<script setup lang="ts">
import { NetworkStatusTemplate } from "@herongxhr/network-status";
</script>
```

#### Props

| Prop                       | Type       | Default                       | Description                                    |
| -------------------------- | ---------- | ----------------------------- | ---------------------------------------------- |
| `position`                 | `string`   | `"top-right"`                 | Position of the component (`top-right`, etc.). |
| `positionOffset`           | `string`   | `"1rem"`                      | Offset for positioning (e.g., `1rem`).         |
| `containerClass`           | `string`   | `"h-4 flex ..."`              | Tailwind classes for the main container.       |
| `signalBarsContainerClass` | `string`   | `"h-full py-[2px] ..."`       | Classes for the signal bars container.         |
| `signalBarClass`           | `string`   | `"transition-all ..."`        | Classes for individual signal bars.            |
| `statusTextContainerClass` | `string`   | `"flex flex-col"`             | Classes for the status text container.         |
| `statusTextClass`          | `string`   | `"h-full flex ..."`           | Classes for the status text.                   |
| `latencyTextClass`         | `string`   | `"text-xs text-gray-600 ..."` | Classes for the latency text.                  |
| `alertClass`               | `string`   | `"p-2 rounded"`               | Classes for the alert box.                     |
| `alertMessageClass`        | `string`   | `""`                          | Classes for the alert message.                 |
| `closeButtonClass`         | `string`   | `"absolute top-1 ..."`        | Classes for the close button.                  |
| `statusTexts`              | `string[]` | `["No Signal", ...]`          | Array of 5 status texts.                       |
| `alertMessages`            | `string[]` | `["You are offline!", ...]`   | Array of 3 alert messages.                     |
| `checkUrl`                 | `string`   | Current host                  | URL for network detection.                     |
| `checkInterval`            | `number`   | `5000`                        | Network check interval (ms).                   |
| `decimalPlaces`            | `number`   | `0`                           | Decimal places for latency display.            |

#### Styling Props

All style props (e.g., `containerStyle`, `signalBarStyle`) accept `CSSProperties` for inline CSS customization.

## API Reference

### `useNetworkStatus(options?: NetworkStatusOptions)`

#### Options

| Property        | Type       | Default                     | Description                                       |
| --------------- | ---------- | --------------------------- | ------------------------------------------------- |
| `initialStatus` | `number`   | `0`                         | Initial network status (0-4).                     |
| `checkUrl`      | `string`   | Current host (`/`)          | URL for network detection.                        |
| `alertPosition` | `string`   | `"bottom-right"`            | Alert box position (`top-right`, etc.).           |
| `statusTexts`   | `string[]` | `["No Signal", ...]`        | Array of 5 status texts.                          |
| `alertMessages` | `string[]` | `["You are offline!", ...]` | Array of 3 alert messages [offline, status 0, 1]. |
| `checkInterval` | `number`   | `8000`                      | Network check interval (ms).                      |
| `decimalPlaces` | `number`   | `0`                         | Decimal places for latency display.               |

#### Returns

| Property             | Type                     | Description                                |
| -------------------- | ------------------------ | ------------------------------------------ |
| `networkStatus`      | `Ref<number>`            | Current network status (0-4).              |
| `latency`            | `Ref<number>`            | Raw latency value (ms).                    |
| `formattedLatency`   | `ComputedRef<string>`    | Formatted latency with specified decimals. |
| `signalBars`         | `ComputedRef<boolean[]>` | Array of 4 booleans for signal bars.       |
| `statusText`         | `ComputedRef<string>`    | Current status text.                       |
| `showAlert`          | `Ref<boolean>`           | Whether to show the alert.                 |
| `alertMessage`       | `ComputedRef<string>`    | Current alert message.                     |
| `checkNetworkStatus` | `() => Promise<void>`    | Manually check network status.             |
| `closeAlert`         | `() => void`             | Manually close the alert.                  |
| `alertPosition`      | `string`                 | Alert box position.                        |

## Contributing

Contributions are welcome! To contribute:

1. Fork the repository.
2. Create a feature branch: `git checkout -b feature/new-feature`
3. Commit your changes: `git commit -m 'Add new feature'`
4. Push to the branch: `git push origin feature/new-feature`
5. Open a Pull Request.

### Development Setup

- Clone: `git clone https://github.com/xai/@herongxhr/network-status.git`
- Install: `npm install`
- Test: `npm run test`
- Build: `npm run build`

## License

[MIT License](LICENSE)
