@npulse/form-builder-widget
v1.0.12
Published
Widget JavaScript pour intégrer facilement des formulaires dynamiques Form Builder
Maintainers
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="[email protected]"
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="[email protected]">
</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: '[email protected]',
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: '[email protected]'
},
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: '[email protected]'
});
// 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: '[email protected]'
});
// 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="[email protected]"
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
- 📧 Email: [email protected]
- 💬 Discord: Form Builder Community
- 📖 Documentation: docs.form-builder.com
