npm package: @naoki85/visual-html-builder
Description: A modern, TypeScript-based visual HTML builder with drag-and-drop functionality
Author: naoki85
Latest version: 0.0.3

# Visual HTML Builder

[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
[![GitHub Pages](https://img.shields.io/badge/GitHub%20Pages-Live%20Demo-blue?logo=github&logoColor=white)](https://naoki85.github.io/visual-html-builder/)

A modern visual HTML builder for browsers. A lightweight TypeScript library that provides intuitive drag-and-drop HTML editing, complete style isolation, and real-time preview functionality.

## 🌐 Demo

**[Live Demo](https://naoki85.github.io/visual-html-builder/)** - Try the Visual HTML Builder in action

## 🚀 Quick Start

### Installation

```bash
# Install via npm
npm install @naoki85/visual-html-builder

# Or via yarn
yarn add @naoki85/visual-html-builder

# Via CDN (ESM)
<script type="module">
  import VisualHtmlBuilder from "https://unpkg.com/@naoki85/visual-html-builder@latest/dist/index.js";
</script>
```

### Development/Contribution Setup

```bash
# Clone the project
git clone https://github.com/naoki85/visual-html-builder.git
cd visual-html-builder

# Install dependencies
npm install
```

## Basic Usage

```typescript
import VisualHtmlBuilder from '@naoki85/visual-html-builder';

// Prepare HTML container
// <div id="editor-container"></div>

// Basic initialization
const editor = new VisualHtmlBuilder('editor-container', {
  theme: 'default',
  enabledElements: ['title', 'text', 'image', 'list'] // Default element types
});
editor.render();
```

### CDN Usage (ESM)

```html
<body>
  <div id="editor-container"></div>

  <script type="module">
    import VisualHtmlBuilder from "https://unpkg.com/@naoki85/visual-html-builder@latest/dist/index.js";
    
    const editor = new VisualHtmlBuilder('editor-container', {
      enabledElements: ['title', 'text', 'image', 'list']
    });
    editor.render();
  </script>
</body>
```

### Advanced Configuration

```typescript
// Full customization example
const editor = new VisualHtmlBuilder('editor-container', {
  theme: 'default',
  enabledElements: ['title', 'text', 'image', 'list'],
  iframePreviewOptions: {
    enableDragDrop: true,
    enableElementSelection: true,
    customStyles: `
      body { 
        font-family: 'Helvetica Neue', sans-serif;
        background: #f5f5f5; 
      }
    `
  },
  htmlTemplate: {
    doctype: '<!DOCTYPE html>',
    htmlAttributes: { lang: 'en' },
    head: {
      title: 'Generated Page',
      meta: [
        { charset: 'UTF-8' },
        { name: 'viewport', content: 'width=device-width, initial-scale=1.0' },
        { name: 'description', content: 'Generated by Visual HTML Builder' }
      ],
      links: [
        { rel: 'stylesheet', href: 'https://fonts.googleapis.com/css2?family=Inter:wght@300;400;600&display=swap' }
      ]
    },
    bodyAttributes: { class: 'generated-content' }
  }
});
```

### HTML Retrieval and API

```typescript
// Body HTML only (existing API - maintains backward compatibility)
const bodyHTML = editor.getHTML();

// Complete HTML document (new API)
const fullHTML = editor.getFullHTML();

// Template management
const currentTemplate = editor.getHTMLTemplate();
editor.updateHTMLTemplate({
  head: {
    title: 'Updated Title'
  }
});

// Element management
const elements = editor.getElements();
editor.setElements([
  { id: 1, type: 'title', props: { text: 'Welcome!', level: 1 }}
]);

// Adding custom elements
editor.registerElementType('custom-button', {
  name: 'Button',
  icon: '🔲',
  defaultProps: { text: 'Click Me', color: '#007bff' },
  render: (props) => `<button style="background-color: ${props.color};">${props.text}</button>`,
  renderEditor: (props) => `
    <div class="property-group">
      <label>Button Text:</label>
      <input type="text" value="${props.text}" 
             class="property-input" data-property="text">
    </div>
    <div class="property-group">
      <label>Color:</label>
      <input type="color" value="${props.color}" 
             class="property-input" data-property="color">
    </div>
  `,
  validate: (props) => props.text.trim() ? null : 'Button text is required'
});
```

## 📦 Development

```bash
# Install dependencies
npm install

# Start development server
npm run dev

# Build
npm run build

# Linting
npm run lint

# Code formatting
npm run format
```

## 🧪 Testing

This project includes a comprehensive test suite using **Vitest**.

### Running Tests

```bash
# Basic test run (watch mode)
npm run test

# One-shot execution
npm run test:run

# Open test UI
npm run test:ui

# Watch mode (monitor file changes)
npm run test:watch

# Generate coverage report
npm run test:coverage
```

### Testing Tech Stack

- **Vitest 3.2.4**: Fast test runner (Jest API compatible)
- **jsdom**: DOM environment simulation
- **c8**: Coverage report generation
- **TypeScript**: Complete type safety

## 🏗️ Architecture

### Core Class
- **VisualHtmlBuilder** (`src/VisualHtmlBuilder.ts`): Main class (478 lines)
  - Handles DOM management, event processing, element selection, and preview updates
  - Combines various helpers to provide functionality

### Helper Module Structure
```
src/helpers/
├── HTMLTemplateHelper.ts     # 279 lines - Complete HTML document generation
├── IframePreviewHelper.ts    # 443 lines - Style isolation preview functionality
├── ElementTypesHelper.ts     # 196 lines - HTML element type definition & management
├── StylesHelper.ts           # 580 lines - CSS management & injection
├── NotificationHelper.ts     # 266 lines - Notification system
├── DragDropHelper.ts         # 297 lines - Drag & drop functionality
└── UtilityHelpers.ts         # 29 lines - General utilities
```

## 🔧 Tech Stack

### Development & Build
- **TypeScript 5.8.3**: Strict type checking, ES2020 target
- **Vite 7.0.4**: Fast build tool, ES Modules output
- **ESLint + Prettier**: Code quality management, unified formatting

### Testing
- **Vitest 3.2.4**: Fast test runner, Jest API compatible
- **jsdom**: DOM environment simulation
- **c8**: Coverage report generation

### Runtime Environment
- **Node.js**: >=20.0.0
- **npm**: >=8.0.0

## 📋 Use Cases

### 🎯 Intended Usage Scenarios

- **HTML Editor in Web Applications**: CMS, blog editors, email creation features
- **Landing Page Builders**: Code-free page creation tools
- **Educational Tools**: Visual editors for HTML learning
- **Prototyping**: Mockup creation for designers and planners

### 🛠️ Technical Features

- **Framework Agnostic**: Can be combined with any framework like React, Vue, Angular
- **Modular Design**: Select and use only the features you need
- **Type Safety**: Complete type support with TypeScript
- **Lightweight**: Minimal dependencies with 44kB lightweight implementation
- **Default Element Types**: Includes 4 built-in element types: `title`, `text`, `image`, `list`

## 🧩 Custom Element Development

### ElementType Interface

```typescript
interface ElementType {
  name: string;                    // Display name
  icon: string;                    // Icon (emoji or text)
  defaultProps: Record<string, any>; // Default properties
  render: (props: Record<string, any>) => string;        // HTML generation
  renderEditor: (props: Record<string, any>, onChange?: (key: string, value: any) => void) => string; // Property editor
  validate: (props: Record<string, any>) => string | null; // Validation
}
```

### Basic Custom Element Example

```typescript
// Simple button element
editor.registerElementType('custom-button', {
  name: 'Button',
  icon: '🔲',
  defaultProps: { 
    text: 'Click Me', 
    color: '#007bff', 
    size: 'medium' 
  },
  render: (props) => 
    `<button style="background-color: ${props.color}; padding: ${props.size === 'large' ? '12px 24px' : '8px 16px'};">${props.text}</button>`,
  renderEditor: (props) => `
    <div class="property-group">
      <label>Button Text:</label>
      <input type="text" value="${props.text}" 
             class="property-input" data-property="text">
    </div>
    <div class="property-group">
      <label>Color:</label>
      <input type="color" value="${props.color}" 
             class="property-input" data-property="color">
    </div>
    <div class="property-group">
      <label>Size:</label>
      <select class="property-input" data-property="size">
        <option value="small" ${props.size === 'small' ? 'selected' : ''}>Small</option>
        <option value="medium" ${props.size === 'medium' ? 'selected' : ''}>Medium</option>
        <option value="large" ${props.size === 'large' ? 'selected' : ''}>Large</option>
      </select>
    </div>
  `,
  validate: (props) => props.text.trim() ? null : 'Button text is required'
});
```

### Overriding Default Element Types

The built-in element types (`title`, `text`, `image`, `list`) can be completely overridden by registering custom elements with the same names. This allows you to customize the behavior and appearance of default elements while maintaining the same API.

```typescript
// Override the default 'title' element
editor.registerElementType('title', {
  name: 'Custom Title',
  icon: '📝',
  defaultProps: { 
    text: 'Custom Title', 
    level: 1,
    color: '#007bff',
    alignment: 'left'
  },
  render: (props) => `
    <h${props.level} style="color: ${props.color}; text-align: ${props.alignment};">
      ${props.text}
    </h${props.level}>
  `,
  renderEditor: (props) => `
    <div class="property-group">
      <label>Title Text:</label>
      <input type="text" value="${props.text}" 
             class="property-input" data-property="text">
    </div>
    <div class="property-group">
      <label>Heading Level:</label>
      <select class="property-input" data-property="level" data-value-type="int">
        <option value="1" ${props.level === 1 ? 'selected' : ''}>H1</option>
        <option value="2" ${props.level === 2 ? 'selected' : ''}>H2</option>
        <option value="3" ${props.level === 3 ? 'selected' : ''}>H3</option>
        <option value="4" ${props.level === 4 ? 'selected' : ''}>H4</option>
        <option value="5" ${props.level === 5 ? 'selected' : ''}>H5</option>
        <option value="6" ${props.level === 6 ? 'selected' : ''}>H6</option>
      </select>
    </div>
    <div class="property-group">
      <label>Text Color:</label>
      <input type="color" value="${props.color}" 
             class="property-input" data-property="color">
    </div>
    <div class="property-group">
      <label>Alignment:</label>
      <select class="property-input" data-property="alignment">
        <option value="left" ${props.alignment === 'left' ? 'selected' : ''}>Left</option>
        <option value="center" ${props.alignment === 'center' ? 'selected' : ''}>Center</option>
        <option value="right" ${props.alignment === 'right' ? 'selected' : ''}>Right</option>
      </select>
    </div>
  `,
  validate: (props) => props.text.trim() ? null : 'Title text is required'
});
```

### Required Event Handling Attributes

When creating custom elements with `renderEditor`, you **must** use specific class names and data attributes for proper event handling:

#### 1. Required CSS Class
- **`class="property-input"`**: Essential for automatic event binding
- This class is used by the library to detect input elements and attach change event listeners

#### 2. Required Data Attributes
- **`data-property="propertyName"`**: Specifies which property this input controls
- The property name must match a key in your element's `defaultProps`

#### 3. Optional Type Conversion Attributes
- **`data-value-type="int"`**: Converts string values to integers
- **`data-value-type="boolean"`**: Converts checkbox states to boolean values
- Without this attribute, values remain as strings

#### 4. List Element Classes(for list-type elements)

- **`list-editor`** - Container for list editing interface
- **`list-item-editor`** - Wrapper for individual list item editing
- **`list-item-input`** - Input field for list items (requires `data-index` attribute)
- **`remove-list-item`** - Button to remove list items (requires `data-index` attribute)
- **`add-list-item`** - Button to add new list items

#### 5. List Element Data Atributes(for list-type elements)

- **`data-index`** - Required for list item inputs and remove buttons

#### 6. Other prepared CSS Classes
- **`property-group`** - Recommended wrapper for styling property sections

### Event Handling Examples

```typescript
// ✅ Correct: All required attributes present
renderEditor: (props) => `
  <div class="property-group">
    <label>Text Input:</label>
    <input type="text" value="${props.text}" 
           class="property-input" data-property="text">
  </div>
  
  <div class="property-group">
    <label>Number Input:</label>
    <select class="property-input" data-property="level" data-value-type="int">
      <option value="1">Level 1</option>
      <option value="2">Level 2</option>
    </select>
  </div>
  
  <div class="property-group">
    <label>
      <input type="checkbox" ${props.enabled ? 'checked' : ''} 
             class="property-input" data-property="enabled" data-value-type="boolean">
      Enable Feature
    </label>
  </div>
`

// ✅ Correct: List element with all required classes
renderEditor: (props) => `
  <div class="property-group">
    <label>Items:</label>
    <div class="list-editor">
      ${props.items.map((item, index) => `
        <div class="list-item-editor">
          <input type="text" value="${item}" 
                 class="list-item-input" data-index="${index}">
          <button type="button" class="remove-list-item" data-index="${index}">×</button>
        </div>
      `).join('')}
      <button type="button" class="add-list-item">+ Add Item</button>
    </div>
  </div>
`

// ❌ Incorrect: Missing required attributes
renderEditor: (props) => `
  <input type="text" value="${props.text}">  <!-- Missing class and data-property -->
  <select>  <!-- Missing class and data-property -->
    <option value="1">Level 1</option>
  </select>
`
```

### Usage Notes

1. **Security**: Prevent XSS with `UtilityHelpers.escapeHtml()`
2. **Element Activation**: Need to add custom element name to `enabledElements` array
3. **Event Handling**: Always use `class="property-input"` and `data-property` attributes
4. **Type Safety**: Use `data-value-type` for automatic type conversion
5. **Validation**: Input validation with appropriate error messages
6. **Accessibility**: Consider HTML structure and aria attributes

---

## 📄 License

MIT License - See [LICENSE](LICENSE) file for details

---

### 📧 Contact

- **GitHub Issues**: [Bug reports & feature requests](https://github.com/naoki85/visual-html-builder/issues)