# Jervis Icons

A universal icon/image web component that works with **any framework** - React, Vue, Angular, Svelte, or vanilla JavaScript.

## Installation

```bash
npm install jervis-icons
# or
yarn add jervis-icons
# or
pnpm add jervis-icons
```

## Features

- Works with any framework (React, Vue, Angular, Svelte, vanilla JS)
- **5,900+ SVG icons** — ~100 essential icons bundled inline, all available on demand
- **28 CSS animations** - spin, bounce, heartbeat, tada, and more (Lottie-like)
- **4 animation triggers** - auto, hover, click, viewport visibility
- SVG icons with color customization
- Image support (PNG, JPG, JPEG, WebP, GIF)
- **Smart icon resolution** - bundled/local icons first by default to avoid per-icon network requests
- **Duplicate-safe names** - numeric variants like `add-circle-1` collapse to `add-circle` by default
- Flexible sizing: tokens (`xs`-`xl`), numbers (`4`, `24`, `64`) as px, CSS lengths (`0.5rem`, `1em`, `24px`), or `free`
- RTL support with automatic transformation
- TypeScript support
- Zero runtime dependencies

## Quick Start

### Vanilla JavaScript / HTML

```html
<!-- Import the component -->
<script type="module">
  import "jervis-icons";
</script>

<!-- Use bundled icons directly by name -->
<svg-icon name="bell" size="md" color="#3b82f6"></svg-icon>
<svg-icon name="edit" size="lg" color="#10b981"></svg-icon>
<svg-icon name="trash" size="sm" color="#ef4444"></svg-icon>

<!-- Or use custom icons from your project -->
<svg-icon src="/my-icons/custom.svg" size="lg"></svg-icon>
```

### React

```tsx
import { useEffect } from "react";
import { configureSvgIcon } from "jervis-icons";

// Configure once in your app entry
configureSvgIcon({
  basePath: "/assets/images",
  locale: "en",
});

function App() {
  return (
    <div>
      <svg-icon name="home" size="md" color="#3b82f6"></svg-icon>
      <svg-icon src="/icons/star.svg" size="lg"></svg-icon>
    </div>
  );
}

// TypeScript: Add type declaration
declare global {
  namespace JSX {
    interface IntrinsicElements {
      "svg-icon": React.DetailedHTMLProps<
        React.HTMLAttributes<HTMLElement> & {
          name?: string;
          src?: string;
          ext?: string;
          type?: "auto" | "svg" | "image";
          size?: string | number;
          color?: string;
          filled?: boolean;
          "read-only"?: boolean;
          transform?: boolean;
          "max-width"?: string | number;
        },
        HTMLElement
      >;
    }
  }
}
```

### Vue 3

```vue
<script setup>
import { configureSvgIcon } from "jervis-icons";

// Configure once
configureSvgIcon({
  basePath: "/assets/images",
  locale: "ar", // For RTL support
});
</script>

<template>
  <svg-icon name="home" size="md" color="#3b82f6" />
  <svg-icon :src="iconSrc" size="lg" />
</template>
```

Add to `vite.config.ts` to avoid Vue warnings:

```ts
export default defineConfig({
  plugins: [
    vue({
      template: {
        compilerOptions: {
          isCustomElement: (tag) => tag === "svg-icon",
        },
      },
    }),
  ],
});
```

### Angular

```typescript
// app.module.ts
import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from "@angular/core";
import "jervis-icons";

@NgModule({
  schemas: [CUSTOM_ELEMENTS_SCHEMA],
  // ...
})
export class AppModule {}
```

```typescript
// main.ts or app.component.ts
import { configureSvgIcon } from "jervis-icons";

configureSvgIcon({
  basePath: "/assets/images",
});
```

```html
<!-- component.html -->
<svg-icon name="home" size="md" color="#3b82f6"></svg-icon>
```

### Svelte

```svelte
<script>
  import { onMount } from 'svelte'
  import { configureSvgIcon } from 'jervis-icons'

  onMount(() => {
    configureSvgIcon({
      basePath: '/assets/images'
    })
  })
</script>

<svg-icon name="home" size="md" color="#3b82f6"></svg-icon>
```

## API

### Attributes

| Attribute   | Type                         | Default          | Description                                                                |
| ----------- | ---------------------------- | ---------------- | -------------------------------------------------------------------------- |
| `name`      | `string`                     | -                | Icon name (filename without extension)                                     |
| `src`       | `string`                     | -                | Direct URL to the icon/image                                               |
| `ext`       | `string`                     | `'svg'`          | File extension when using `name`                                           |
| `type`      | `'auto' \| 'svg' \| 'image'` | `'auto'`         | Force icon type                                                            |
| `size`      | `string \| number`           | `'md'`           | Icon size (`xs`-`xl`, `free`, numbers as px, or CSS lengths like `0.5rem`) |
| `color`     | `string`                     | `'currentColor'` | Primary SVG color                                                          |
| `color2`    | `string`                     | -                | Secondary color for dual-tone icons                                        |
| `color3`    | `string`                     | -                | Third color for multi-tone icons                                           |
| `color4`    | `string`                     | -                | Fourth color for multi-tone icons                                          |
| `color5`    | `string`                     | -                | Fifth color for multi-tone icons                                           |
| `filled`    | `boolean`                    | `true`           | Whether SVG is filled or stroked                                           |
| `read-only` | `boolean`                    | `false`          | Shows not-allowed cursor                                                   |
| `transform` | `boolean`                    | `true`           | Enable RTL transformation                                                  |
| `max-width` | `string \| number`           | `'100%'`         | Maximum width                                                              |

### Size Map

| Size | Pixels |
| ---- | ------ |
| xs   | 20px   |
| sm   | 24px   |
| md   | 32px   |
| lg   | 40px   |
| xl   | 48px   |

Size value rules:

- `xs`, `sm`, `md`, `lg`, `xl` -> mapped pixel sizes from the table above
- `free` -> no inline width/height (icon fills container)
- `number` or numeric string (for example `4`, `24`, `"64"`) -> treated as pixels
- CSS length string (for example `"0.5rem"`, `"1em"`, `"24px"`, `"50%"`) -> used as-is

### Configuration

Use `configureSvgIcon()` to set global defaults:

```typescript
import { configureSvgIcon } from "jervis-icons";

configureSvgIcon({
  basePath: "/assets/images", // Base path for your project's icons
  svgPath: "/assets/svg", // Specific path for SVGs
  imagePath: "/assets/images", // Specific path for images
  locale: "ar", // 'ar' enables RTL transformation
  preferExternal: false, // Bundled/local-first (default)
  iconCategory: "base", // Default: collapse numeric variants (e.g. add-circle-1 -> add-circle)
});
```

### Icon Priority System

When you use an icon by name, svg-icon follows this priority:

1. **Preloaded icon maps** from `configureSvgIcon({ icons })` (including Vite plugin inlined icons)
2. **If `preferExternal: true`:** your configured external path first
3. **Bundled inline icons**
4. **Library fallback** (`/__jervis-icons__/...`) for known library icons
5. **If `preferExternal: false` (default):** external path fallback for unknown names

Use this when you want your project assets to override bundled names:

```typescript
// Your project's "bell.svg" will be used instead of bundled one
configureSvgIcon({
  basePath: "/my-icons",
  preferExternal: true,
});
```

```html
<svg-icon name="bell"></svg-icon>
<!-- Loads from /my-icons/bell.svg if exists, otherwise uses bundled -->
```

### Bundled Icons

Jervis Icons includes ~100 essential bundled icons (available without network requests) and 5,900+ total icons. All icons use `currentColor` for easy styling.

```typescript
import { getBundledIconNames, hasBundledIcon } from "jervis-icons";

// Get all available icon names
const icons = getBundledIconNames();
console.log(icons); // ['activity', 'add-user', 'bell', ...]

// Check if an icon is bundled
if (hasBundledIcon("bell")) {
  console.log("bell icon is available!");
}
```

Get deduplicated names and categories:

```typescript
import { getAllIconNames, getIconCategories } from "jervis-icons";

const names = await getAllIconNames(); // default config -> deduplicated base names
const categories = await getIconCategories();
```

### Vite: Avoid Per-Icon HTTP Requests

Use the Vite plugin to auto-inline only referenced library icons:

```ts
// vite.config.ts
import { defineConfig } from "vite";
import vue from "@vitejs/plugin-vue";
import { jervisIcons } from "jervis-icons/vite";

export default defineConfig({
  plugins: [vue(), jervisIcons()],
});
```

With this plugin, commonly used library icons are served from an in-memory map (no per-icon request), while unknown icons still fall back safely.

**Available icons include:**

- UI icons: `edit`, `trash`, `close`, `check`, `plus`, `search`, `filter`, `menu`...
- Navigation: `arrow-left`, `arrow-right`, `chevron-down`, `chevron-right`...
- File types: `pdf`, `doc`, `xls`, `zip`, `jpg`, `png`, `mp4`...
- Actions: `download`, `upload-cloud`, `export`, `reload`, `logout`...
- Status: `success`, `error`, `warning`, `info_circle`...
- And many more!

### Custom Tag Name

Register with a custom tag name:

```typescript
import { register } from "jervis-icons";

register("my-icon"); // Use as <my-icon>
```

## Examples

### Basic SVG Icon

```html
<svg-icon name="home" size="md" color="#3b82f6"></svg-icon>
```

### Direct URL

```html
<svg-icon src="https://example.com/icon.svg" size="lg"></svg-icon>
```

### Custom Size (Pixels)

```html
<svg-icon name="star" size="64"></svg-icon>
<svg-icon name="star" size="4"></svg-icon>
```

### Custom Size (CSS Length)

```html
<svg-icon name="star" size="0.5rem"></svg-icon>
<svg-icon name="star" size="1em"></svg-icon>
<svg-icon name="star" size="24px"></svg-icon>
```

### Image (PNG)

```html
<svg-icon name="logo" ext="png" size="xl"></svg-icon>
```

### Outlined Style

```html
<svg-icon name="heart" filled="false" color="red"></svg-icon>
```

### Disabled State

```html
<svg-icon name="edit" read-only></svg-icon>
```

## Animations

Add smooth Lottie-like animations to any icon with zero extra dependencies:

```html
<!-- Spinning loader -->
<svg-icon name="settings" animate="spin"></svg-icon>

<!-- Bounce on hover -->
<svg-icon name="bell" animate="bounce" animate-trigger="hover"></svg-icon>

<!-- Heartbeat on click toggle -->
<svg-icon
  name="heart"
  animate="heartbeat"
  animate-trigger="click"
  color="#ef4444"
></svg-icon>

<!-- Custom speed & timing -->
<svg-icon
  name="star"
  animate="pulse"
  animate-duration="0.5s"
  animate-timing="linear"
></svg-icon>
```

### Available Animations

| Category      | Animations                                                                                         |
| ------------- | -------------------------------------------------------------------------------------------------- |
| **Looping**   | `spin`, `spin-reverse`, `pulse`, `bounce`, `float`, `heartbeat`, `fade`, `ping`, `swing`           |
| **Attention** | `shake`, `wiggle`, `tada`, `jello`, `rubber-band`, `flash`, `wobble`                               |
| **3D**        | `flip`, `flip-x`, `flip-y`                                                                         |
| **Entrances** | `fade-in`, `zoom-in`, `roll-in`, `slide-in-left`, `slide-in-right`, `slide-in-up`, `slide-in-down` |
| **Exits**     | `fade-out`, `zoom-out`                                                                             |

### Animation Triggers

- `auto` — starts immediately (default)
- `hover` — plays on mouse hover
- `click` — toggles on click
- `visible` — plays when element enters viewport

### Programmatic Control

```js
const icon = document.querySelector("svg-icon");
icon.startAnimation(); // Start
icon.stopAnimation(); // Stop
icon.pauseAnimation(); // Pause
icon.resumeAnimation(); // Resume
icon.replayAnimation(); // Restart
```

### RTL Support

```typescript
configureSvgIcon({ locale: "ar" });
```

```html
<!-- Will be mirrored automatically -->
<svg-icon name="arrow-right"></svg-icon>

<!-- Disable transformation for specific icons -->
<svg-icon name="logo" transform="false"></svg-icon>
```

## Building from Source

```bash
# Install dependencies
npm install

# Development
npm run dev

# Build (includes SVG optimization and bundling)
npm run build

# Optimize SVGs only
npm run optimize:svg

# Generate SVG bundle only
npm run generate:bundle
```

## SVG Guidelines

All bundled SVGs follow these guidelines:

- No hardcoded colors - uses `currentColor`
- No width/height attributes - scales via CSS
- Optimized and minified
- viewBox preserved for proper scaling

## Adding Custom Icons to the Package

1. Add SVG files to `images/svg/`
2. Run `npm run optimize:svg` to clean them
3. Run `npm run build` to regenerate the bundle

## Browser Support

Works in all modern browsers that support Web Components (Custom Elements v1):

- Chrome 67+
- Firefox 63+
- Safari 10.1+
- Edge 79+

## License

MIT
