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
- 📧 Email: support@form-builder.com
- 💬 Discord: Form Builder Community
- 📖 Documentation: docs.form-builder.com