JSPM

  • ESM via JSPM
  • ES Module Entrypoint
  • Export Map
  • Keywords
  • License
  • Repository URL
  • TypeScript Types
  • README
  • Created
  • Published
  • Downloads 10
  • Score
    100M100P100Q94436F
  • License MIT

Widget JavaScript pour intégrer facilement des formulaires dynamiques Form Builder

Package Exports

  • @npulse/form-builder-widget
  • @npulse/form-builder-widget/dist/form-builder-widget.esm.js
  • @npulse/form-builder-widget/dist/form-builder-widget.js
  • @npulse/form-builder-widget/react
  • @npulse/form-builder-widget/react.js

This package does not declare an exports field, so the exports above have been automatically detected and optimized by JSPM instead. If any package subpath is missing, it is recommended to post an issue to the original package (@npulse/form-builder-widget) to support the "exports" field. If that is not possible, create a JSPM override to customize the exports field for this package.

Readme

@npulse/form-builder-widget

Widget JavaScript pour intégrer facilement des formulaires dynamiques Form Builder dans vos applications.

🚀 Installation

npm install @npulse/form-builder-widget

📦 Utilisation

1. HTML Simple (Recommandé)

<!DOCTYPE html>
<html>
<head>
    <script src="https://unpkg.com/@npulse/form-builder-widget/dist/form-builder-widget.min.js"></script>
</head>
<body>
    <!-- Widget automatique (URL API requise) -->
    <div
        data-form-builder="YOUR_FORM_ID"
        data-api-base-url="https://your-api.com/api">
    </div>

    <!-- Avec configuration complète -->
    <div
        data-form-builder="YOUR_FORM_ID"
        data-api-base-url="https://your-api.com/api"
        data-theme="light"
        data-size="medium"
        data-auto-refresh="true"
        data-refresh-interval="30000">
    </div>

    <!-- Avec données pré-remplies -->
    <div
        data-form-builder="YOUR_FORM_ID"
        data-api-base-url="https://your-api.com/api"
        data-field-nom="Jean Dupont"
        data-field-email="jean@example.com"
        data-field-telephone="0123456789">
    </div>

    <!-- Mode édition -->
    <div
        data-form-builder="YOUR_FORM_ID"
        data-api-base-url="https://your-api.com/api"
        data-edit-mode="true"
        data-submission-id="sub-123"
        data-field-nom="Marie Martin"
        data-field-email="marie@example.com">
    </div>
</body>
</html>

2. JavaScript/TypeScript

import { createFormBuilder } from '@form-builder/widget';

// Créer un widget (apiBaseUrl est maintenant requis)
const widget = createFormBuilder(document.getElementById('my-form'), {
  formId: 'YOUR_FORM_ID',
  apiBaseUrl: 'https://your-api.com/api', // ← REQUIS
  theme: 'light',
  size: 'medium',
  autoRefresh: true,
  refreshInterval: 30000,
  onLoad: (form) => console.log('Formulaire chargé:', form),
  onSubmit: (data) => console.log('Soumis:', data),
  onError: (error) => console.error('Erreur:', error)
});

// Avec données pré-remplies
const widgetWithData = createFormBuilder(document.getElementById('my-form'), {
  formId: 'YOUR_FORM_ID',
  apiBaseUrl: 'https://your-api.com/api',
  initialData: {
    nom: 'Jean Dupont',
    email: 'jean@example.com',
    telephone: '0123456789'
  },
  onSubmit: (data) => console.log('Soumis:', data)
});

// Mode édition
const editWidget = createFormBuilder(document.getElementById('my-form'), {
  formId: 'YOUR_FORM_ID',
  apiBaseUrl: 'https://your-api.com/api',
  editMode: true,
  submissionId: 'sub-123',
  initialData: {
    nom: 'Marie Martin',
    email: 'marie@example.com'
  },
  onSubmit: (data) => console.log('Mis à jour:', data)
});

// Avec composants personnalisés
const customWidget = createFormBuilder(document.getElementById('my-form'), {
  formId: 'YOUR_FORM_ID',
  apiBaseUrl: 'https://your-api.com/api',
  customComponents: {
    rating: {
      name: 'CustomRating',
      fieldType: 'rating',
      render: (field, value, onChange) => {
        const container = document.createElement('div');
        // Votre logique de rendu personnalisée
        return container;
      },
      validate: (value, field) => {
        // Votre logique de validation personnalisée
        return [];
      }
    }
  },
  onSubmit: (data) => console.log('Soumis:', data)
});

// Mettre à jour les données dynamiquement
widgetWithData.setInitialData({
  nom: 'Nouveau Nom',
  email: 'nouveau@example.com'
});

// Obtenir les données actuelles
const currentData = widgetWithData.getUserData();
console.log('Données actuelles:', currentData);

3. React

import React, { useEffect, useRef } from 'react';
import { createFormBuilder } from '@form-builder/widget';

function FormBuilder({ formId, config }) {
  const containerRef = useRef(null);
  const widgetRef = useRef(null);

  useEffect(() => {
    if (containerRef.current && formId) {
      widgetRef.current = createFormBuilder(containerRef.current, {
        formId,
        ...config
      });
    }

    return () => {
      if (widgetRef.current) {
        widgetRef.current.destroy();
      }
    };
  }, [formId, config]);

  return <div ref={containerRef} />;
}

// Utilisation
<FormBuilder 
  formId="YOUR_FORM_ID" 
  config={{
    theme: 'dark',
    size: 'large',
    onSubmit: (data) => console.log('Soumis:', data)
  }} 
/>

4. Vue.js

<template>
  <div ref="formContainer"></div>
</template>

<script>
import { createFormBuilder } from '@form-builder/widget';

export default {
  props: ['formId', 'config'],
  mounted() {
    if (this.formId) {
      this.widget = createFormBuilder(this.$refs.formContainer, {
        formId: this.formId,
        ...this.config
      });
    }
  },
  beforeDestroy() {
    if (this.widget) {
      this.widget.destroy();
    }
  }
}
</script>

⚙️ Configuration

Attributs HTML

Attribut Description Valeurs Défaut
data-form-builder ID du formulaire string requis
data-api-base-url URL de l'API string requis
data-theme Thème du widget light, dark light
data-size Taille du widget small, medium, large medium
data-position Position du widget inline, modal, sidebar inline
data-auto-refresh Rechargement auto true, false true
data-refresh-interval Intervalle de refresh (ms) number 30000
data-language Langue fr, en fr
data-edit-mode Mode édition true, false false
data-submission-id ID de soumission à modifier string -
data-field-* Données pré-remplies string -

Configuration JavaScript

interface FormBuilderConfig {
  formId: string;                    // ID du formulaire (requis)
  apiBaseUrl: string;                // URL de l'API (requis)
  theme?: 'light' | 'dark';          // Thème
  size?: 'small' | 'medium' | 'large'; // Taille
  position?: 'inline' | 'modal' | 'sidebar'; // Position
  autoRefresh?: boolean;             // Rechargement automatique
  refreshInterval?: number;          // Intervalle en ms
  language?: 'fr' | 'en';            // Langue
  initialData?: Record<string, any>; // Données pré-remplies
  editMode?: boolean;                // Mode édition
  submissionId?: string;              // ID de soumission à modifier
  customClasses?: {                  // Classes CSS personnalisées
    container?: string;
    form?: string;
    field?: string;
    button?: string;
  };
  onLoad?: (form: FormData) => void;           // Callback chargement
  onSubmit?: (data: FormSubmissionData) => void; // Callback soumission
  onError?: (error: FormBuilderError) => void;   // Callback erreur
  onRefresh?: (changes: FormChanges) => void;    // Callback refresh
}

🎯 API du Widget

const widget = createFormBuilder(element, config);

// Recharger manuellement
await widget.refresh();

// Activer/désactiver auto-refresh
widget.setAutoRefresh(true, 10000); // 10 secondes

// Obtenir les données du formulaire
const formData = widget.getFormData();

// Obtenir les données saisies par l'utilisateur
const userData = widget.getUserData();

// Pré-remplir le formulaire avec des données
widget.setInitialData({
  nom: 'Jean Dupont',
  email: 'jean@example.com'
});

// Valider le formulaire
const validation = await widget.validate();

// Soumettre le formulaire
const result = await widget.submit();

// Détruire le widget
widget.destroy();

📡 Événements

// Écouter les événements
element.addEventListener('formbuilder:loaded', (event) => {
  console.log('Widget chargé:', event.detail);
});

element.addEventListener('formbuilder:submit', (event) => {
  console.log('Formulaire soumis:', event.detail);
});

element.addEventListener('formbuilder:error', (event) => {
  console.error('Erreur:', event.detail);
});

element.addEventListener('formbuilder:refresh', (event) => {
  console.log('Formulaire mis à jour:', event.detail);
});

🎨 Personnalisation CSS

/* Personnaliser les styles */
.form-builder-widget {
  border-radius: 12px;
  box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1);
}

.form-builder-widget .btn-primary {
  background: linear-gradient(45deg, #667eea 0%, #764ba2 100%);
}

.form-builder-widget .field-input:focus {
  border-color: #667eea;
  box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1);
}

🔧 Configuration Globale

import { configure } from '@form-builder/widget';

// Configuration globale
configure({
  apiBaseUrl: 'https://my-api.com/api',
  theme: 'dark',
  language: 'en'
});

📱 Responsive

Le widget s'adapte automatiquement aux différentes tailles d'écran :

  • Mobile : 1 colonne, boutons pleine largeur
  • Tablet : 2 colonnes, layout adaptatif
  • Desktop : Nombre de colonnes configuré

🧩 Composants Personnalisés

Créer un composant personnalisé

const customComponent = {
  name: 'CustomRating',
  fieldType: 'rating', // Type de champ qu'il remplace
  render: (field, value, onChange) => {
    const container = document.createElement('div');
    
    // Créer votre interface personnalisée
    for (let i = 1; i <= 5; i++) {
      const star = document.createElement('span');
      star.textContent = '★';
      star.style.color = i <= value ? '#ffc107' : '#ddd';
      star.addEventListener('click', () => onChange(i));
      container.appendChild(star);
    }
    
    return container;
  },
  validate: (value, field) => {
    const errors = [];
    if (field.required && !value) {
      errors.push({
        field: field.name,
        message: 'Veuillez donner une note',
        code: 'REQUIRED'
      });
    }
    return errors;
  },
  destroy: (element) => {
    // Nettoyage si nécessaire
    element.innerHTML = '';
  }
};

// Utiliser le composant
const widget = createFormBuilder(element, {
  formId: 'YOUR_FORM_ID',
  apiBaseUrl: 'https://your-api.com/api',
  customComponents: {
    rating: customComponent
  }
});

Composants React

// Adapter un composant React existant
const reactComponent = {
  name: 'ReactDatePicker',
  fieldType: 'date',
  render: (field, value, onChange) => {
    const container = document.createElement('div');
    
    // Créer un élément React (avec ReactDOM.render)
    const reactElement = React.createElement(YourDatePickerComponent, {
      value: value,
      onChange: onChange,
      field: field
    });
    
    ReactDOM.render(reactElement, container);
    return container;
  },
  destroy: (element) => {
    ReactDOM.unmountComponentAtNode(element);
  }
};

Composants Vue

// Adapter un composant Vue existant
const vueComponent = {
  name: 'VueColorPicker',
  fieldType: 'color',
  render: (field, value, onChange) => {
    const container = document.createElement('div');
    
    // Créer une instance Vue
    const vueInstance = new Vue({
      el: container,
      template: '<your-color-picker :value="value" @change="onChange" />',
      data: { value },
      methods: { onChange }
    });
    
    return container;
  },
  destroy: (element) => {
    // Nettoyer l'instance Vue
    element.__vue__?.$destroy();
  }
};

Composants Angular

// Adapter un composant Angular existant
const angularComponent = {
  name: 'AngularSlider',
  fieldType: 'slider',
  render: (field, value, onChange) => {
    const container = document.createElement('div');
    
    // Utiliser Angular Elements ou Dynamic Component Loading
    const sliderElement = document.createElement('your-slider');
    sliderElement.setAttribute('ng-reflect-value', value);
    sliderElement.addEventListener('valueChange', (e) => onChange(e.detail));
    
    container.appendChild(sliderElement);
    return container;
  }
};

Composants intégrés (Upload & Cartes)

// Upload de fichiers avec configuration personnalisée
const fileUploadComponent = {
  name: 'CustomFileUpload',
  fieldType: 'file',
  render: (field, value, onChange) => {
    const container = document.createElement('div');
    
    const uploadConfig = {
      uploadUrl: 'https://your-api.com/upload',
      multiple: true,
      maxSize: 5 * 1024 * 1024, // 5MB
      accept: 'image/*,.pdf,.doc,.docx',
      headers: {
        'Authorization': 'Bearer ' + getAuthToken()
      },
      template: {
        button: '📁 Choisir vos fichiers',
        dropZone: 'Glissez-déposez vos fichiers ici',
        styles: `
          .file-upload-container {
            border: 2px dashed #007bff;
            border-radius: 12px;
            background: linear-gradient(45deg, #f8f9fa, #e9ecef);
          }
        `
      }
    };
    
    const fileUpload = new FileUploadComponent(container, uploadConfig, onChange);
    return container;
  }
};

// Cartes géographiques avec multi-providers
const mapComponent = {
  name: 'CustomMap',
  fieldType: 'map',
  render: (field, value, onChange) => {
    const container = document.createElement('div');
    
    const mapConfig = {
      provider: 'google', // ou 'openstreetmap', 'mapbox'
      apiKey: 'YOUR_API_KEY',
      center: { lat: 48.8566, lng: 2.3522 },
      zoom: 13,
      height: '400px',
      selectionMode: 'point',
      draggable: true,
      template: {
        controls: 'Vos contrôles personnalisés',
        styles: `
          .map-component-container {
            border: 2px solid #007bff;
            border-radius: 12px;
            box-shadow: 0 4px 12px rgba(0,123,255,0.15);
          }
        `
      }
    };
    
    const mapComponent = new MapComponent(container, mapConfig, onChange);
    return container;
  }
};

Gestion dynamique des composants

const widget = createFormBuilder(element, config);

// Enregistrer un nouveau composant
widget.registerCustomComponent({
  name: 'CustomToggle',
  fieldType: 'toggle',
  render: (field, value, onChange) => {
    // Votre logique de rendu
  }
});

// Désenregistrer un composant
widget.unregisterCustomComponent('toggle');

// Obtenir un composant
const component = widget.getCustomComponent('rating');

🎯 Cas d'usage avec données pré-remplies

1. Formulaires d'édition

<!-- Édition d'un profil utilisateur -->
<div 
  data-form-builder="user-profile-form"
  data-api-base-url="https://api.myapp.com/api"
  data-edit-mode="true"
  data-submission-id="user-123"
  data-field-nom="Jean Dupont"
  data-field-email="jean@example.com"
  data-field-telephone="0123456789">
</div>

2. Formulaires de mise à jour

// Mise à jour d'une commande
const editWidget = createFormBuilder(element, {
  formId: 'order-update-form',
  apiBaseUrl: 'https://api.mystore.com/api',
  editMode: true,
  submissionId: 'order-456',
  initialData: {
    quantite: 5,
    adresse: '123 Rue de la Paix',
    ville: 'Paris',
    codePostal: '75001'
  },
  onSubmit: (data) => {
    console.log('Commande mise à jour:', data);
    // Rediriger vers la page de confirmation
  }
});

3. Formulaires avec données contextuelles

// Formulaire pré-rempli selon le contexte
function loadContextualForm(userContext) {
  const widget = createFormBuilder(element, {
    formId: 'contextual-form',
    apiBaseUrl: 'https://api.myapp.com/api',
    initialData: {
      // Données du contexte utilisateur
      nom: userContext.name,
      email: userContext.email,
      role: userContext.role,
      // Données de session
      sessionId: getCurrentSessionId(),
      timestamp: new Date().toISOString()
    }
  });
}

4. Formulaires multi-étapes avec sauvegarde

// Sauvegarde automatique des données
const multiStepWidget = createFormBuilder(element, {
  formId: 'multi-step-form',
  apiBaseUrl: 'https://api.myapp.com/api',
  initialData: getSavedFormData(), // Récupérer les données sauvegardées
  onSubmit: (data) => {
    // Sauvegarder les données pour la prochaine étape
    saveFormData(data);
    // Soumettre au serveur
    submitToServer(data);
  }
});

// Fonction de sauvegarde locale
function saveFormData(data) {
  localStorage.setItem('form-draft', JSON.stringify(data));
}

function getSavedFormData() {
  const saved = localStorage.getItem('form-draft');
  return saved ? JSON.parse(saved) : {};
}

🚀 Exemples Complets

E-commerce

<!-- Page produit -->
<div class="product-form">
  <h2>Commander ce produit</h2>
  <div 
    data-form-builder="product-order-form"
    data-theme="light"
    data-size="large">
  </div>
</div>

Contact

<!-- Page contact -->
<div class="contact-section">
  <div 
    data-form-builder="contact-form"
    data-theme="dark"
    data-size="medium"
    data-auto-refresh="false">
  </div>
</div>

SPA (Single Page Application)

// Dans votre SPA
import { createFormBuilder } from '@form-builder/widget';

class FormManager {
  constructor() {
    this.widgets = new Map();
  }

  loadForm(containerId, formId) {
    const container = document.getElementById(containerId);
    if (container && !this.widgets.has(containerId)) {
      const widget = createFormBuilder(container, {
        formId,
        onSubmit: (data) => this.handleSubmit(data),
        onError: (error) => this.handleError(error)
      });
      this.widgets.set(containerId, widget);
    }
  }

  handleSubmit(data) {
    // Logique de soumission
    console.log('Données soumises:', data);
  }

  handleError(error) {
    // Gestion d'erreur
    console.error('Erreur:', error);
  }
}

const formManager = new FormManager();

🐛 Debug

// Activer les logs
localStorage.setItem('form-builder-debug', 'true');

// Vérifier l'état du widget
console.log('Widget state:', widget.getFormData());

📄 Licence

MIT

🤝 Contribution

Les contributions sont les bienvenues ! Voir CONTRIBUTING.md pour plus de détails.

📞 Support