{# # ============================================================================ # MOLECULE: FORM-FIELD # ============================================================================ # # Champ de formulaire complet combinant Label + Input + Messages. # Molécule réutilisable pour tous les formulaires de l'application. # Supporte aussi type: file avec mode dropzone. # # ============================================================================ # COMPOSITION (Atomes utilisés) # ============================================================================ # # - _atoms/form/label.html.twig # - _atoms/form/input.html.twig (ou textarea) # - _atoms/icon/icon.html.twig # # ============================================================================ # PARAMÈTRES # ============================================================================ # # | Paramètre | Type | Défaut | Description | # |----------------|---------|-----------|------------------------------------------| # | name | string | (requis) | Nom du champ (id auto-généré) | # | label | string | null | Texte du label | # | type | string | 'text' | Type: text, email, password, textarea, file | # | placeholder | string | '' | Placeholder | # | value | string | '' | Valeur du champ | # | required | bool | false | Champ obligatoire | # | disabled | bool | false | Champ désactivé | # | error | string | null | Message d'erreur | # | hint | string | null | Texte d'aide | # | icon | string | null | Nom de l'icône (left par défaut) | # | iconPosition | string | 'left' | Position: left, right | # | size | string | 'md' | Taille: sm, md, lg | # | variant | string | 'default' | Style: default, filled | # | labelTooltip | string | null | Tooltip sur le label | # | rows | number | 4 | Lignes pour textarea | # | optional | bool | false | Affiche "(optionnel)" sur le label | # | srOnly | bool | false | Label visible uniquement pour lecteurs | # | class | string | '' | Classes additionnelles sur le wrapper | # | inputClass | string | '' | Classes additionnelles sur l'input | # | attr | object | {} | Attributs HTML additionnels | # | | | | | # | --- TYPE FILE SPECIFIQUE --- | # | accept | string | null | Types acceptés (ex: '.pdf,.doc') | # | multiple | bool | false | Autoriser plusieurs fichiers | # | maxSize | string | null | Taille max affichée (ex: '20 MB') | # | dropzone | bool | false | Mode drag & drop | # | dropzoneText | string | 'Glissez...'| Texte principal dropzone | # | dropzoneHint | string | null | Texte secondaire dropzone | # # ============================================================================ # EXEMPLES D'UTILISATION # ============================================================================ # # Champ email simple: # {% include '_molecules/form/form-field.html.twig' with { # name: 'email', # label: 'Adresse email', # type: 'email', # placeholder: 'votre@email.com', # required: true # } %} # # Champ mot de passe avec erreur: # {% include '_molecules/form/form-field.html.twig' with { # name: 'password', # label: 'Mot de passe', # type: 'password', # icon: 'lock', # required: true, # error: 'Le mot de passe est incorrect' # } %} # # File upload simple: # {% include '_molecules/form/form-field.html.twig' with { # name: 'document', # label: 'Document', # type: 'file', # accept: '.pdf,.doc,.docx', # hint: 'PDF ou Word, max 20 MB' # } %} # # File upload avec dropzone: # {% include '_molecules/form/form-field.html.twig' with { # name: 'files', # label: 'Fichiers', # type: 'file', # dropzone: true, # multiple: true, # accept: '.pdf,.pptx', # maxSize: '20 MB', # dropzoneText: 'Glissez vos fichiers ici', # dropzoneHint: 'ou cliquez pour parcourir' # } %} # # Textarea: # {% include '_molecules/form/form-field.html.twig' with { # name: 'description', # label: 'Description', # type: 'textarea', # rows: 6, # hint: 'Maximum 500 caractères' # } %} # # ============================================================================ #} {# ============================================================================ CONFIGURATION DES VALEURS PAR DÉFAUT ============================================================================ #} {% set name = name ?? '' %} {% set label = label ?? null %} {% set type = type ?? 'text' %} {% set placeholder = placeholder ?? '' %} {% set value = value ?? '' %} {% set required = required ?? false %} {% set disabled = disabled ?? false %} {% set error = error ?? null %} {% set hint = hint ?? null %} {% set icon = icon ?? null %} {% set iconPosition = iconPosition ?? 'left' %} {% set size = size ?? 'md' %} {% set variant = variant ?? 'default' %} {% set labelTooltip = labelTooltip ?? null %} {% set rows = rows ?? 4 %} {% set optional = optional ?? false %} {% set srOnly = srOnly ?? false %} {% set class = class ?? '' %} {% set inputClass = inputClass ?? '' %} {% set attr = attr ?? {} %} {# File specific parameters #} {% set accept = accept ?? null %} {% set multiple = multiple ?? false %} {% set maxSize = maxSize ?? null %} {% set dropzone = dropzone ?? false %} {% set dropzoneText = dropzoneText ?? 'Glissez vos fichiers ici' %} {% set dropzoneHint = dropzoneHint ?? 'ou cliquez pour parcourir' %} {# ID auto-généré #} {% set fieldId = 'field-' ~ name %} {% set isFileType = type == 'file' %} {# ============================================================================ RENDU HTML ============================================================================ #}
{# ----- Label (sauf si srOnly) ----- #} {% if label %} {% if srOnly %} {% else %} {% include '_atoms/form/label.html.twig' with { text: label, for: fieldId, required: required, optional: optional, tooltip: labelTooltip, size: size, disabled: disabled } only %} {% endif %} {% endif %} {# ----- Input / Textarea / File ----- #} {% if type == 'textarea' %} {% include '_atoms/form/textarea.html.twig' with { id: fieldId, name: name, value: value, placeholder: placeholder, rows: rows, required: required, disabled: disabled, error: error, size: size, variant: variant, class: inputClass, attr: attr } only %} {% elseif isFileType %} {# ----- File Input ----- #} {% if dropzone %} {# Dropzone mode #}
{% include '_atoms/icon/icon.html.twig' with { name: 'cloud', size: 'lg', color: 'primary' } only %}

{{ dropzoneText }}

{{ dropzoneHint }}

{% if accept or maxSize %}

{% if accept %}{{ accept|replace({'.': '', ',': ', '}) }}{% endif %} {% if accept and maxSize %} • {% endif %} {% if maxSize %}Max {{ maxSize }}{% endif %}

{% endif %}
{# File name preview (via JS) #} {% else %} {# Simple file input #}
{% endif %} {% else %} {# Input sans label intégré (on utilise celui de la molécule) #}
{# Icône gauche #} {% if icon and iconPosition == 'left' %} {% set iconSizes = { 'sm': 'sm', 'md': 'md', 'lg': 'lg' } %} {% include '_atoms/icon/icon.html.twig' with { name: icon, size: iconSizes[size] ?? 'md', color: error ? 'error' : 'gray' } only %} {% endif %} {# Input field #} {% set baseClasses = 'w-full font-primary transition-all duration-base ease-default border-2 outline-none focus:border-primary focus:ring-2 focus:ring-primary/20' %} {% set variantClasses = { 'default': 'bg-white border-gray-300 text-black placeholder-gray-400', 'filled': 'bg-gray-100 border-transparent text-black placeholder-gray-500 focus:bg-white focus:border-primary' } %} {% set sizeClasses = { 'sm': 'py-2.5 px-4 text-sm rounded-lg', 'md': 'py-4 px-5 text-base rounded-lg', 'lg': 'py-5 px-6 text-lg rounded-xl' } %} {% set errorClasses = error ? 'border-error focus:ring-error/20 focus:border-error' : '' %} {% set disabledClasses = disabled ? 'bg-gray-100 text-gray-400 cursor-not-allowed' : '' %} {# Padding pour icône #} {% set iconPaddingClasses = '' %} {% if icon %} {% if iconPosition == 'left' %} {% set iconPaddingClasses = size == 'sm' ? 'pl-10' : (size == 'lg' ? 'pl-14' : 'pl-12') %} {% else %} {% set iconPaddingClasses = size == 'sm' ? 'pr-10' : (size == 'lg' ? 'pr-14' : 'pr-12') %} {% endif %} {% endif %} {% set inputClasses = [ baseClasses, variantClasses[variant] ?? variantClasses['default'], sizeClasses[size] ?? sizeClasses['md'], errorClasses, disabledClasses, iconPaddingClasses, inputClass ]|join(' ')|trim %} {# Icône droite #} {% if icon and iconPosition == 'right' %} {% set iconSizes = { 'sm': 'sm', 'md': 'md', 'lg': 'lg' } %} {% include '_atoms/icon/icon.html.twig' with { name: icon, size: iconSizes[size] ?? 'md', color: error ? 'error' : 'gray' } only %} {% endif %}
{% endif %} {# ----- Hint ----- #} {% if hint and not error %}

{{ hint }}

{% endif %} {# ----- Error message ----- #} {% if error %}

{% include '_atoms/icon/icon.html.twig' with { name: 'alert', size: 'xs' } only %} {{ error }}

{% endif %}