npm package: @mirawision/domino-inject
Description: Ultra-lean helpers to embed UI into existing pages and sync styles
Author: Yelysei Lukin
Latest version: 1.0.0

# @mirawision/domino-inject

A lightweight DOM injection library for web applications, `@mirawision/domino-inject` provides a comprehensive set of tools for mounting UI elements, managing styles, and syncing visual states with a clean, performant API.

## Features

### Mount Utilities
- **Direct Node Mounting**: Mount existing nodes without wrappers
- **Light DOM Mounting**: Inject elements with controlled wrappers
- **Shadow DOM Support**: Mount isolated components with style encapsulation
- **Position Control**: Flexible element positioning (before, after, first, last)
- **Cleanup Handling**: Automatic resource cleanup and event listener management

### Style Management
- **Style Injection**: Add styles to both Light and Shadow DOM
- **Class Mirroring**: Sync classes between elements with smart filtering
- **State Syncing**: Mirror disabled states and ARIA attributes
- **Theme Support**: Automatically sync ancestor theme classes

### Key Benefits
- **Zero Dependencies**: Tiny footprint for fast loading
- **TypeScript Support**: Full type definitions included
- **Memory Efficient**: Automatic resource cleanup
- **Performance Optimized**: Debounced updates and efficient DOM operations

## Installation

```bash
npm install @mirawision/domino-inject
```

or 

```bash
npm install @mirawision/domino-inject
```

## Usage

Here's a quick overview of how to use the core functionalities:

### Mount Elements

```typescript
import { mountNode, mountLight, mountShadow } from '@mirawision/domino-inject';

// Direct node mounting (no wrapper)
mountNode(
  document.querySelector('.toolbar')!,
  'last',                // position
  existingButton        // node to mount
);

// Light DOM mounting with cleanup
const light = mountLight(
  document.querySelector('.toolbar')!, 
  { 
    position: 'last',
    classes: ['btn-wrap'],
    tag: 'div'
  }, 
  root => {
    const btn = document.createElement('button');
    btn.className = 'btn btn-primary';
    root.appendChild(btn);
    
    // Return cleanup function
    return () => btn.remove();
  }
);

// Shadow DOM with style isolation
const shadow = mountShadow(
  document.body, 
  { 
    position: 'last',
    styles: [':host { all: initial }']
  }, 
  root => {
    const widget = document.createElement('div');
    root.appendChild(widget);
  }
);

// Cleanup when done
light.destroy();
shadow.destroy();
```

### Manage Styles

```typescript
import { injectStyles, mirrorClasses } from '@mirawision/domino-inject';

// Inject styles into any root
injectStyles(root, [
  // CSS strings
  '.btn { padding: 8px 16px; }',
  // CSSStyleSheet objects
  new CSSStyleSheet().replace('.btn-primary { background: blue; }')
]);

// Mirror classes with smart filtering
const controller = mirrorClasses(sourceBtn, targetBtn, {
  // Filter classes to mirror
  allow: cls => cls.startsWith('btn-'),
  deny: cls => cls === 'btn-danger',
  
  // Transform during mirroring
  map: cls => cls === 'primary' ? 'btn-primary' : cls,
  
  // Add permanent classes
  extraClasses: ['custom'],
  
  // Sync with parent themes
  observeAncestors: true,
  ancestorDepth: 2
});

// Cleanup when done
controller.disconnect();
```

## API Reference

### Mount Functions

#### `mountNode(anchor, position, node)`
Mount an existing DOM node at a specified position relative to an anchor element. Provides direct node insertion without wrappers or cleanup handling.

```typescript
function mountNode(
  anchor: Element,
  position: 'before' | 'after' | 'first' | 'last' | 'replace',
  node: Node
): void
```

#### `mountLight(anchor, options?, render?)`
Create a controlled wrapper element in Light DOM with optional styling, attributes, and cleanup handling. Returns a controller for managing the mounted element.

```typescript
function mountLight(
  anchor: Element,
  options?: MountLightOptions,
  render?: RenderFn<HTMLElement>
): LightController

interface MountLightOptions {
  position?: 'before' | 'after' | 'first' | 'last' | 'replace';
  tag?: keyof HTMLElementTagNameMap;
  classes?: string[];
  attrs?: Record<string, string>;
  style?: Partial<CSSStyleDeclaration>;
}
```

#### `mountShadow(anchor, options?, render?)`
Create an isolated component using Shadow DOM with its own style scope. Returns a controller for managing both the host element and shadow root.

```typescript
function mountShadow(
  anchor: Element,
  options?: MountShadowOptions,
  render?: RenderFn<ShadowRoot>
): ShadowController

interface MountShadowOptions {
  position?: 'before' | 'after' | 'first' | 'last' | 'replace';
  tag?: keyof HTMLElementTagNameMap;
  mode?: 'open' | 'closed';
  styles?: (CSSStyleSheet | string)[];
  className?: string;
}
```

### Style Functions

#### `injectStyles(root, styles)`
Add styles to any DOM root (Document, ShadowRoot, or Element) using either CSS strings or CSSStyleSheet objects. Automatically handles adoptedStyleSheets for Shadow DOM when available.

```typescript
function injectStyles(
  root: ShadowRoot | Document | HTMLElement,
  styles: (CSSStyleSheet | string)[]
): void
```

#### `mirrorClasses(source, target, options?)`
Mirror CSS classes from one element to another with smart filtering, transformation, and state syncing. Perfect for matching existing UI styles and themes.

```typescript
function mirrorClasses(
  source: Element,
  target: HTMLElement,
  options?: MirrorOptions
): MirrorController

interface MirrorOptions {
  debounceMs?: number;                   // default: 50
  extraClasses?: string[];               // never removed by mirror
  allow?: (cls: string) => boolean;      // whitelist filter
  deny?: (cls: string) => boolean;       // blacklist filter
  map?: (cls: string) => string | null;  // transform or drop class
  observeAncestors?: boolean;            // default: true
  ancestorDepth?: number;                // default: 2
  syncStateAttrs?: boolean;              // default: true
}

interface MirrorController {
  resync(): void;                        // force immediate sync
  disconnect(): void;                    // stop mirroring
  isConnected(): boolean;                // check if active
}
```

## Contributing

Contributions are always welcome! Feel free to open issues or submit pull requests.

## License

This project is licensed under the MIT License.