Widened React lib peer deps to fix legacy peer deps issue in host apps
All checks were successful
armco-org/icon/pipeline/head This commit looks good

This commit is contained in:
2026-02-08 14:37:47 +05:30
parent 5ed23f77c8
commit b4061e71ba
3 changed files with 374 additions and 7 deletions

356
docs/USAGE.md Normal file
View File

@@ -0,0 +1,356 @@
# @armco/icon — Usage Guide
## Overview
`@armco/icon` is a flexible, theme-aware React Icon component. It renders icons from multiple sources: **react-icons identifiers**, SVG strings, base64-encoded SVGs, image URLs, and raw image elements. Icons fetched via identifier or URL are cached in a global registry to prevent duplicate network requests.
The component is a React class component wrapped with the `withTheme` HOC from `@armco/utils`, giving it automatic access to the current theme context.
## Installation
```bash
npm install @armco/icon
```
Peer dependencies: `react`, `react-dom` (^18.x)
---
## Quick Start
```tsx
import Icon from "@armco/icon"
// Identifier-based (most common usage)
<Icon icon="md.MdEdit" />
<Icon icon="hi.HiViewBoards" />
<Icon icon="tb.TbHomeEdit" />
```
---
## Icon Identifier Format
The most common way to use the component is with **react-icons identifiers**:
```
<category>.<IconName>
```
- **category** — 23 letter prefix matching a [react-icons](https://react-icons.github.io/react-icons/) library:
- `ai` — Ant Design Icons
- `bi` — Bootstrap Icons
- `bs` — Bootstrap Icons (alt)
- `ci` — Circum Icons
- `cg` — css.gg
- `di` — Devicons
- `fa` — Font Awesome
- `fc` — Flat Color Icons
- `fi` — Feather Icons
- `gi` — Game Icons
- `go` — Github Octicons
- `gr` — Grommet Icons
- `hi` — Heroicons
- `im` — IcoMoon
- `io` — Ionicons
- `lu` — Lucide
- `md` — Material Design Icons
- `pi` — Phosphor Icons
- `ri` — Remix Icons
- `rx` — Radix Icons
- `si` — Simple Icons
- `sl` — Simple Line Icons
- `tb` — Tabler Icons
- `ti` — Typicons
- `vsc` — VS Code Icons
- `wi` — Weather Icons
- **IconName** — the PascalCase icon name from that library.
### Resolution
When an identifier like `hi.HiViewBoards` is passed, the component:
1. Detects it as an `identifier` type via regex: `/^[a-zA-Z0-9]{2,3}[.\/][a-zA-Z0-9]+$/`
2. Converts dots to slashes: `hi.HiViewBoards``/icon/hi/HiViewBoards`
3. Fetches the SVG from the icon server at that path
4. Caches the result in a global `iconRegistry` (keyed by the original identifier string)
5. Subsequent uses of the same identifier skip the network call
---
## Props API
### `IconProps`
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| `icon` | `string \| object \| IconSource` | — | The icon source. Can be an identifier (`"hi.HiViewBoards"`), SVG string, base64 string, URL, or an `IconSource` object. |
| `attributes` | `IconAttributes` | — | Sizing, colors, classes, and inline styles. |
| `events` | `IconEvents` | — | Event handlers: `onClick`, `onMouseEnter`, `onMouseLeave`. |
| `fillPath` | `boolean \| number[]` | — | Apply fill/stroke colors to specific `<path>` elements inside the SVG. `true` = all paths, `[0, 2]` = paths at those indices. |
| `toggled` | `boolean` | `false` | Initial toggle state. Toggles on click. Affects which colors are applied. |
| `hoverShadow` | `boolean` | `false` | Adds a box-shadow on hover. |
| `slot` | `ArPopoverSlots` | — | Popover slot assignment (`"popover"` or `"anchor"`). |
| `theme` | `ArThemes` | auto | Injected by `withTheme` HOC. Can be overridden. |
### `IconSource` (explicit type specification)
```ts
interface IconSource {
source: string | object
type: ArIconSourceTypes // "URL" | "identifier" | "svgString" | "b64" | "raw"
}
```
Use this when you need to explicitly specify the source type instead of relying on auto-detection:
```tsx
<Icon icon={{ source: "https://example.com/icon.svg", type: "URL" }} />
```
### `IconAttributes`
```ts
interface IconAttributes {
height?: string // e.g., "24px", "2rem"
width?: string
size?: string // Shorthand — sets both height and width
strokeWidth?: string // SVG stroke-width attribute
colors?: {
fillColor?: string
strokeColor?: string
strokeWidth?: string
toggleFillColor?: string
toggleStrokeColor?: string
hoverFillColor?: string
hoverStrokeColor?: string
darkFillColor?: string
darkStrokeColor?: string
darkToggleFillColor?: string
darkToggleStrokeColor?: string
darkHoverFillColor?: string
darkHoverStrokeColor?: string
}
styles?: CSSProperties // React inline styles applied to the SVG element
classes?: string // CSS class names appended to the SVG element
}
```
### `IconEvents`
```ts
interface IconEvents {
onClick?: (e?: MouseEvent) => void
onMouseEnter?: (e?: MouseEvent) => void
onMouseLeave?: (e?: MouseEvent) => void
}
```
---
## Source Types
The component auto-detects the icon source type. Detection order:
| Type | Detection Rule | Example |
|------|---------------|---------|
| `identifier` | Matches regex `^[a-zA-Z0-9]{2,3}[./][a-zA-Z0-9]+$` | `"hi.HiViewBoards"` |
| `URL` | Starts with `http://` or `https://` | `"https://cdn.example.com/icon.svg"` |
| `b64` | Starts with `data:image/svg+xml;base64,` | Base64-encoded SVG data URI |
| `svgString` | Starts with `<svg` | Raw SVG markup string |
| `raw` | `typeof source === "object"` (not IconSource) | React element or image object |
---
## Usage Examples
### Basic — Identifier
```tsx
import Icon from "@armco/icon"
<Icon icon="hi.HiViewBoards" />
<Icon icon="md.MdDelete" />
<Icon icon="tb.TbHomeEdit" />
```
### Sizing
```tsx
// Using size shorthand (sets both width and height)
<Icon icon="hi.HiViewBoards" attributes={{ size: "2rem" }} />
// Using explicit width/height
<Icon icon="hi.HiViewBoards" attributes={{ width: "32px", height: "32px" }} />
// Using inline styles
<Icon icon="hi.HiViewBoards" attributes={{ styles: { width: 32, height: 32 } }} />
```
### Colors
```tsx
// Fill color
<Icon icon="md.MdEdit" attributes={{ colors: { fillColor: "#333" } }} />
// Stroke color
<Icon icon="fi.FiEdit" attributes={{ colors: { strokeColor: "red" } }} />
// Hover colors
<Icon
icon="md.MdDelete"
attributes={{
colors: {
fillColor: "#666",
hoverFillColor: "#ff0000",
},
}}
/>
// Dark theme colors (applied when theme is DARK1)
<Icon
icon="md.MdEdit"
attributes={{
colors: {
fillColor: "#333",
darkFillColor: "#ccc",
},
}}
/>
```
### Toggle State
```tsx
// Icon toggles fill on click
<Icon
icon="md.MdFavorite"
attributes={{
colors: {
fillColor: "#ccc",
toggleFillColor: "#ff0000",
},
}}
/>
```
### Path-Level Coloring
```tsx
// Apply colors to all <path> elements inside the SVG
<Icon icon="md.MdEdit" fillPath={true} attributes={{ colors: { fillColor: "blue" } }} />
// Apply colors to specific paths by index
<Icon icon="md.MdEdit" fillPath={[0, 2]} attributes={{ colors: { strokeColor: "red" } }} />
```
### Events
```tsx
<Icon
icon="md.MdDelete"
events={{
onClick: (e) => console.log("clicked", e),
onMouseEnter: () => console.log("hovered"),
onMouseLeave: () => console.log("left"),
}}
/>
```
### CSS Classes
```tsx
<Icon icon="hi.HiViewBoards" attributes={{ classes: "me-2 text-primary" }} />
```
### Hover Shadow
```tsx
<Icon icon="md.MdEdit" hoverShadow />
```
### SVG String Source
```tsx
<Icon icon='<svg viewBox="0 0 24 24"><circle cx="12" cy="12" r="10"/></svg>' />
```
### URL Source
```tsx
<Icon icon="https://cdn.example.com/my-icon.svg" />
```
### Explicit IconSource
```tsx
<Icon icon={{ source: "https://cdn.example.com/icon.svg", type: "URL" }} />
<Icon icon={{ source: "hi.HiViewBoards", type: "identifier" }} />
```
---
## Exported Utilities
The package also exports helper functions and types from `index.ts`:
### Helper Functions (`helper.ts`)
| Function | Signature | Description |
|----------|-----------|-------------|
| `inferIconType` | `(source: string \| object) => ArIconSourceTypes` | Auto-detects the icon source type from the input. |
| `parseSvgString` | `(svgString: string) => SVGSVGElement` | Parses an SVG markup string into an `SVGSVGElement`. |
| `parseSvgB64String` | `(b64: string) => SVGSVGElement` | Decodes a base64 SVG string and parses it. |
| `createElementFromSvg` | `(svg: SVGSVGElement) => ReactNode` | Converts an `SVGSVGElement` DOM node into a React element tree. |
| `applyStyles` | `(milk, props, state) => SVGSVGElement` | Applies sizing, colors, classes, and styles to a cloned SVG element. |
| `getFillColor` | `(props, state?) => string \| undefined` | Resolves the correct fill color based on theme, hover, and toggle state. |
| `getStrokeColor` | `(props, state?) => string \| undefined` | Resolves the correct stroke color based on theme, hover, and toggle state. |
| `placeholder` | `SVGSVGElement` | A default placeholder SVG (empty rectangle) used when icon loading fails. |
### Enums (`enums.ts`)
| Enum | Values | Description |
|------|--------|-------------|
| `ArIconSourceTypes` | `URL`, `identifier`, `svgString`, `b64`, `raw` | Icon source type classification. |
| `ArIconTileTypes` | `COMFY`, `COMPACT`, `LIST` | Icon tile display modes. |
| `ArValidImageMimeTypes` | `JPEG`, `PNG`, `GIF`, `WEBP`, `SVG`, `TIFF`, `BMP`, `ICON` | Valid image MIME types for content-type detection. |
| `ArPopoverSlots` | `POPOVER`, `ANCHOR` | Slot assignment for popover usage. |
### Types (`types.ts`)
| Type | Description |
|------|-------------|
| `IconProps` | Full props interface for the Icon component. |
| `IconSource` | Explicit `{ source, type }` icon descriptor. |
| `IconAttributes` | Sizing, colors, classes, styles. |
| `IconEvents` | Click, hover event handlers. |
| `IconState` | Internal component state (hovered, toggled, milk, shake, type). |
| `IconRegistry` | Global cache type: `{ [key]: { promise?, icon? } }`. |
| `IconStyles` | Simplified style descriptor (fill, stroke, bg). |
| `IconResponse` | Server API response shape for icon data. |
---
## Architecture
1. **Icon Resolution**: The `parseIconDescriptor` method determines the source type and either fetches from the server (identifier/URL) or parses locally (SVG string, base64, raw).
2. **Registry Caching**: A module-level `iconRegistry` object caches both the fetch promise and the resolved icon. This prevents duplicate network requests when the same icon is used multiple times.
3. **Style Application**: On every render-relevant state change (hover, toggle, attribute change), `applyStyles` clones the SVG DOM node and applies colors, sizing, classes, and inline styles.
4. **React Conversion**: The styled SVG DOM node is converted to a React element tree via `createElementFromSvg`, enabling React event binding.
5. **Theme Awareness**: The `withTheme` HOC injects the current theme. Color resolution functions (`getFillColor`, `getStrokeColor`) select the appropriate color variant based on theme + hover + toggle state.
---
## CSS
The component applies the class `ar-Icon` to all rendered SVG elements. Additional classes can be added via `attributes.classes`.
```scss
.ar-Icon {
transition: all 0.3s;
&:hover.hover-shadow {
box-shadow: 0px 4px 8px var(--ar-shadow);
}
}
```

View File

@@ -1,6 +1,6 @@
{
"name": "@armco/icon",
"version": "0.0.12",
"version": "0.0.13",
"type": "module",
"main": "build/cjs/index.js",
"module": "build/es/index.js",
@@ -28,8 +28,8 @@
}
},
"peerDependencies": {
"react": "^18.2.0",
"react-dom": "^18.3.1"
"react": ">=18.0.0",
"react-dom": ">=18.0.0"
},
"prettier": "prettier-config-nick",
"repository": {

View File

@@ -6,11 +6,22 @@ set -e
npm run build
cp package.json build/
sed -i '' -E 's/"build"/"*"/' build/package.json
sed -i '' 's#"build/cjs/Icon.js"#"cjs/Icon.js"#' build/package.json
sed -i '' 's#"build/es/Icon.js"#"es/Icon.js"#' build/package.json
sed -i '' 's#"build/types/Icon.d.ts"#"types/Icon.d.ts"#' build/package.json
# Use Node.js for portable package.json normalization
PKG_PATH="$(pwd)/build/package.json" node - <<'EOF'
const fs = require('fs');
const path = process.env.PKG_PATH;
const pkg = JSON.parse(fs.readFileSync(path, 'utf8'));
pkg.private = false;
delete pkg.scripts;
delete pkg.devDependencies;
if (!pkg.files) pkg.files = ['*'];
else pkg.files = pkg.files.map(x => x === 'build' ? '*' : x);
['main','module','types'].forEach(k => {
if (pkg[k]) pkg[k] = pkg[k].replace(/^\.??\/build\//, '').replace(/^build\//, '');
});
fs.writeFileSync(path, JSON.stringify(pkg, null, 2) + '\n');
EOF
cd build
npm pack --pack-destination ~/__Projects__/Common