feat: Nouveaux composants et styles Lions.dev
Ajout de nouveaux composants composites et styles SCSS Lions.dev pour enrichir l'extension Freya. ## Nouveaux composants - confirmDialog: Dialogue de confirmation avec actions - dialog: Dialogue générique personnalisable - menu: Menu de navigation vertical - menubar: Barre de menu horizontale - messages: Composant d'affichage de messages multiples - overlayPanel: Panneau overlay positionnable - sidebar: Panneau latéral collapsible - toast: Notifications toast ## Améliorations composants existants - Amélioration support Ajax pour tous les champs (fieldInput, fieldSelect, etc.) - Correction attributs pour compatibilité avec composite:clientBehavior - Optimisation panel avec support toggleable et collapsed - Amélioration button, commandButton, linkButton avec severities - Amélioration dataTable et dataView avec pagination ## Styles SCSS Lions.dev - Création architecture SCSS modulaire dans sass/lions/ - Variables globales Lions.dev (_variables.scss) - Mixins réutilisables (_mixins.scss) - Styles par catégorie de composants (12 fichiers) - Point d'entrée lions.scss pour compilation ## Tests et démo - Page buttons-showcase pour démonstration boutons - Amélioration components-demo avec nouveaux composants - Styles CSS demo pour tests visuels ## Infrastructure - Mise à jour .gitignore (exclusion docs temporaires) - Mise à jour versions dans pom.xml Tous les composants respectent les patterns PrimeFaces et la charte graphique Lions.dev.
This commit is contained in:
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<groupId>dev.lions</groupId>
|
||||
<artifactId>primefaces-freya-extension-parent</artifactId>
|
||||
<version>1.0.0-SNAPSHOT</version>
|
||||
<version>1.0.0</version>
|
||||
</parent>
|
||||
<artifactId>primefaces-freya-extension</artifactId>
|
||||
<name>PrimeFaces Freya Extension - Runtime</name>
|
||||
|
||||
@@ -4,13 +4,151 @@
|
||||
xmlns:composite="http://xmlns.jcp.org/jsf/composite"
|
||||
xmlns:h="http://xmlns.jcp.org/jsf/html"
|
||||
xmlns:p="http://primefaces.org/ui">
|
||||
|
||||
<!--
|
||||
Freya Avatar - Avatar utilisateur
|
||||
═══════════════════════════════════════════════════════════
|
||||
Lions.dev Avatar - Photo de profil ou initiales utilisateur
|
||||
═══════════════════════════════════════════════════════════
|
||||
Version: 1.0.0 | Priority: HAUTE #34
|
||||
|
||||
Avatar circulaire ou carré pour représenter un utilisateur.
|
||||
Styles Lions.dev avec gradients, bordures et indicateurs de statut.
|
||||
|
||||
Usage:
|
||||
<fr:avatar label="JD" size="large" shape="circle" />
|
||||
<fr:avatar image="/images/user.png" size="xlarge" />
|
||||
<fr:avatar label="JD" size="xlarge" shape="circle" />
|
||||
|
||||
Exemples:
|
||||
|
||||
1. Avatar avec initiales:
|
||||
<fr:avatar label="JD"
|
||||
shape="circle"
|
||||
size="large" />
|
||||
|
||||
2. Avatar avec image:
|
||||
<fr:avatar image="#{resource['images:user.jpg']}"
|
||||
shape="circle"
|
||||
size="xlarge" />
|
||||
|
||||
3. Avatar avec icône:
|
||||
<fr:avatar icon="pi pi-user"
|
||||
shape="circle"
|
||||
size="normal"
|
||||
severity="success" />
|
||||
|
||||
4. Avatar carré avec bordure:
|
||||
<fr:avatar label="AB"
|
||||
shape="square"
|
||||
bordered="true" />
|
||||
|
||||
5. Avatar avec indicateur de statut:
|
||||
<fr:avatar image="#{userBean.avatar}"
|
||||
shape="circle"
|
||||
status="online" />
|
||||
|
||||
6. Avatar arrondi (rounded):
|
||||
<fr:avatar label="CD"
|
||||
shape="rounded"
|
||||
severity="warning" />
|
||||
|
||||
7. Groupe d'avatars:
|
||||
<div class="ui-avatar-group">
|
||||
<fr:avatar label="JD" />
|
||||
<fr:avatar label="AB" severity="success" />
|
||||
<fr:avatar label="CD" severity="danger" />
|
||||
<fr:avatar label="+5" severity="secondary" />
|
||||
</div>
|
||||
|
||||
Attributs disponibles:
|
||||
- id: Identifiant du composant
|
||||
- label: Initiales affichées (2 lettres max, ex: "JD")
|
||||
- icon: Icône PrimeIcons (ex: "pi pi-user")
|
||||
- image: URL de l'image de profil
|
||||
- size: xs | sm | normal (défaut) | lg | xl - Taille de l'avatar
|
||||
- shape: circle (défaut) | square | rounded - Forme de l'avatar
|
||||
- severity: primary (défaut) | secondary | success | info | warning | danger
|
||||
- bordered: true/false - Ajouter une bordure blanche
|
||||
- status: online | offline | busy | away - Indicateur de statut
|
||||
- styleClass: Classes CSS additionnelles
|
||||
- style: Styles CSS inline
|
||||
|
||||
Sizes disponibles:
|
||||
- xs: 24px (1.5rem) - Pour listes compactes
|
||||
- sm: 32px (2rem) - Pour petits espaces
|
||||
- normal: 40px (2.5rem) - Taille par défaut
|
||||
- lg: 48px (3rem) - Pour profils importants
|
||||
- xl: 64px (4rem) - Pour pages de profil
|
||||
|
||||
Shapes disponibles:
|
||||
- circle: Avatar circulaire (par défaut)
|
||||
- square: Avatar carré avec coins droits
|
||||
- rounded: Avatar carré avec coins arrondis
|
||||
|
||||
Severities (couleurs de fond si pas d'image):
|
||||
- primary: Gradient bleu Lions.dev (défaut)
|
||||
- secondary: Gris neutre
|
||||
- success: Gradient vert
|
||||
- info: Gradient cyan
|
||||
- warning: Gradient orange
|
||||
- danger: Gradient rouge
|
||||
|
||||
Status indicators:
|
||||
- online: Point vert en bas à droite
|
||||
- offline: Point gris
|
||||
- busy: Point rouge
|
||||
- away: Point orange
|
||||
|
||||
Avatar Group:
|
||||
Pour afficher plusieurs avatars chevauchés, utilisez:
|
||||
<div class="ui-avatar-group">
|
||||
<fr:avatar ... />
|
||||
<fr:avatar ... />
|
||||
</div>
|
||||
|
||||
Utilisation en code:
|
||||
```java
|
||||
@Named
|
||||
@ViewScoped
|
||||
public class UserBean {
|
||||
private String userInitials = "JD";
|
||||
private String avatarUrl = "/resources/images/avatar.jpg";
|
||||
private boolean hasAvatar = true;
|
||||
|
||||
public String getUserInitials() {
|
||||
String firstName = user.getFirstName();
|
||||
String lastName = user.getLastName();
|
||||
return (firstName.charAt(0) + "" + lastName.charAt(0)).toUpperCase();
|
||||
}
|
||||
|
||||
public String getAvatarUrl() {
|
||||
return hasAvatar ? avatarUrl : null;
|
||||
}
|
||||
|
||||
public String getStatusIndicator() {
|
||||
return user.isOnline() ? "online" : "offline";
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Cas d'usage recommandés:
|
||||
- Profil utilisateur: xl, circle, avec image
|
||||
- Liste utilisateurs: sm, circle, initiales
|
||||
- Commentaires: normal, circle, avec image
|
||||
- Navigation: sm, circle, icône
|
||||
- Équipe: lg, circle avec groupe
|
||||
|
||||
Notes de style:
|
||||
- Avatar avec gradient bleu Lions.dev par défaut
|
||||
- Initiales en majuscules automatiques
|
||||
- Image en object-fit: cover pour remplissage
|
||||
- Bordure blanche optionnelle avec ombre
|
||||
- Indicateur de statut positionné en bas à droite
|
||||
- Groupe: avatars chevauchés avec hover scale
|
||||
- Responsive: xl devient lg sur mobile
|
||||
|
||||
Accessibilité:
|
||||
- Attribut alt automatique sur les images
|
||||
- Initiales lisibles par lecteurs d'écran
|
||||
- Contraste WCAG AA respecté
|
||||
- Indicateur de statut avec aria-label
|
||||
-->
|
||||
|
||||
<composite:interface>
|
||||
@@ -19,7 +157,10 @@
|
||||
<composite:attribute name="icon" required="false" type="java.lang.String" />
|
||||
<composite:attribute name="image" required="false" type="java.lang.String" />
|
||||
<composite:attribute name="size" required="false" type="java.lang.String" default="normal" />
|
||||
<composite:attribute name="shape" required="false" type="java.lang.String" default="square" />
|
||||
<composite:attribute name="shape" required="false" type="java.lang.String" default="circle" />
|
||||
<composite:attribute name="severity" required="false" type="java.lang.String" default="primary" />
|
||||
<composite:attribute name="bordered" required="false" type="java.lang.Boolean" default="false" />
|
||||
<composite:attribute name="status" required="false" type="java.lang.String" />
|
||||
<composite:attribute name="styleClass" required="false" type="java.lang.String" />
|
||||
<composite:attribute name="style" required="false" type="java.lang.String" />
|
||||
</composite:interface>
|
||||
@@ -31,9 +172,14 @@
|
||||
image="#{cc.attrs.image}"
|
||||
size="#{cc.attrs.size}"
|
||||
shape="#{cc.attrs.shape}"
|
||||
styleClass="#{cc.attrs.styleClass}"
|
||||
style="#{cc.attrs.style}" />
|
||||
styleClass="ui-avatar-#{cc.attrs.severity} #{cc.attrs.bordered ? 'ui-avatar-bordered' : ''} #{cc.attrs.styleClass}"
|
||||
style="#{cc.attrs.style}">
|
||||
|
||||
<span class="ui-avatar-status ui-avatar-status-#{cc.attrs.status}"
|
||||
rendered="#{not empty cc.attrs.status}"
|
||||
role="img"
|
||||
aria-label="#{cc.attrs.status eq 'online' ? 'En ligne' : cc.attrs.status eq 'offline' ? 'Hors ligne' : cc.attrs.status eq 'busy' ? 'Occupé' : 'Absent'}" />
|
||||
</p:avatar>
|
||||
</composite:implementation>
|
||||
|
||||
</html>
|
||||
|
||||
|
||||
@@ -8,16 +8,25 @@
|
||||
<!--
|
||||
Freya Breadcrumb - Fil d'Ariane avec style Freya
|
||||
|
||||
Usage:
|
||||
Usage with MenuModel:
|
||||
<fr:breadcrumb model="#{bean.breadcrumbModel}" homeIcon="pi pi-home" />
|
||||
|
||||
Usage with inline items:
|
||||
<fr:breadcrumb homeIcon="pi pi-home">
|
||||
<p:menuitem value="Accueil" url="#" />
|
||||
<p:menuitem value="Section" url="#" />
|
||||
</fr:breadcrumb>
|
||||
-->
|
||||
|
||||
<composite:interface>
|
||||
<composite:attribute name="id" required="false" type="java.lang.String" />
|
||||
<composite:attribute name="model" required="true" />
|
||||
<composite:attribute name="model" required="false" />
|
||||
<composite:attribute name="homeIcon" required="false" type="java.lang.String" default="pi pi-home" />
|
||||
<composite:attribute name="styleClass" required="false" type="java.lang.String" />
|
||||
<composite:attribute name="style" required="false" type="java.lang.String" />
|
||||
|
||||
<!-- Support for inline p:menuitem children -->
|
||||
<composite:insertChildren />
|
||||
</composite:interface>
|
||||
|
||||
<composite:implementation>
|
||||
@@ -26,6 +35,7 @@
|
||||
styleClass="#{cc.attrs.styleClass}"
|
||||
style="#{cc.attrs.style}">
|
||||
<p:menuitem value="" icon="#{cc.attrs.homeIcon}" outcome="/index" />
|
||||
<composite:insertChildren />
|
||||
</p:breadCrumb>
|
||||
</composite:implementation>
|
||||
|
||||
|
||||
@@ -6,10 +6,59 @@
|
||||
xmlns:p="http://primefaces.org/ui">
|
||||
|
||||
<!--
|
||||
Freya Button - Bouton simple avec style Freya
|
||||
═══════════════════════════════════════════════════════════
|
||||
Lions.dev Button - Bouton de navigation avec identité Lions.dev
|
||||
═══════════════════════════════════════════════════════════
|
||||
Version: 1.0.0
|
||||
Priority: HAUTE #2
|
||||
|
||||
Description:
|
||||
Bouton de navigation (p:button) avec styles Lions.dev.
|
||||
Utilisé pour la navigation entre pages (outcome/href).
|
||||
Pour les actions Ajax, utilisez fr:commandButton.
|
||||
|
||||
Fonctionnalités:
|
||||
- Gradients Lions.dev élégants
|
||||
- Animations hover (translateY + shadow elevation)
|
||||
- Focus ring accessible (WCAG 2.1 AA)
|
||||
- Support complet des variantes (solid, outlined, text, link, rounded, raised)
|
||||
- Support des tailles (sm, base, lg)
|
||||
- Badge support
|
||||
|
||||
Usage:
|
||||
<fr:button value="Cliquer" icon="pi pi-check" severity="success" />
|
||||
<fr:button value="Aller au Dashboard"
|
||||
icon="pi pi-home"
|
||||
outcome="/dashboard"
|
||||
severity="primary"
|
||||
size="lg" />
|
||||
|
||||
Exemples:
|
||||
|
||||
1. Navigation simple:
|
||||
<fr:button value="Accueil" icon="pi pi-home" outcome="/index" />
|
||||
|
||||
2. Lien externe:
|
||||
<fr:button value="Documentation" icon="pi pi-book" href="https://docs.lions.dev" target="_blank" />
|
||||
|
||||
3. Bouton outlined large:
|
||||
<fr:button value="En savoir plus" severity="info" outlined="true" size="lg" outcome="/about" />
|
||||
|
||||
4. Bouton premium rounded:
|
||||
<fr:button value="Upgrade Premium" severity="help" rounded="true" raised="true" outcome="/pricing" />
|
||||
|
||||
Severity disponibles:
|
||||
- primary (Lions Blue #2D9BEF - défaut)
|
||||
- secondary (Gray)
|
||||
- success (Green #22BB69)
|
||||
- info (Cyan #00BCD4)
|
||||
- warning (Orange #FF9800)
|
||||
- help (Lions Gold #FFC700 - premium)
|
||||
- danger (Red #F44336)
|
||||
|
||||
Tailles disponibles:
|
||||
- sm (32px height)
|
||||
- base (40px height - défaut)
|
||||
- lg (48px height)
|
||||
-->
|
||||
|
||||
<composite:interface>
|
||||
@@ -27,6 +76,10 @@
|
||||
|
||||
<!-- Severity: primary, secondary, success, info, warning, help, danger -->
|
||||
<composite:attribute name="severity" required="false" type="java.lang.String" />
|
||||
|
||||
<!-- Size: sm, base, lg -->
|
||||
<composite:attribute name="size" required="false" type="java.lang.String" default="base" />
|
||||
|
||||
<!-- Variants: text, outlined, link -->
|
||||
<composite:attribute name="text" required="false" type="java.lang.Boolean" default="false" />
|
||||
<composite:attribute name="outlined" required="false" type="java.lang.Boolean" default="false" />
|
||||
@@ -46,7 +99,7 @@
|
||||
outcome="#{cc.attrs.outcome}"
|
||||
href="#{cc.attrs.href}"
|
||||
target="#{cc.attrs.target}"
|
||||
styleClass="#{cc.attrs.styleClass} #{not empty cc.attrs.severity ? 'ui-button-'.concat(cc.attrs.severity) : ''} #{cc.attrs.text ? 'ui-button-text' : ''} #{cc.attrs.outlined ? 'ui-button-outlined' : ''} #{cc.attrs.link ? 'ui-button-link' : ''} #{cc.attrs.rounded ? 'ui-button-rounded' : ''} #{cc.attrs.raised ? 'ui-button-raised' : ''}"
|
||||
styleClass="#{cc.attrs.styleClass} #{not empty cc.attrs.severity ? 'ui-button-'.concat(cc.attrs.severity) : ''} #{not empty cc.attrs.size and cc.attrs.size ne 'base' ? 'ui-button-'.concat(cc.attrs.size) : ''} #{cc.attrs.text ? 'ui-button-text' : ''} #{cc.attrs.outlined ? 'ui-button-outlined' : ''} #{cc.attrs.link ? 'ui-button-link' : ''} #{cc.attrs.rounded ? 'ui-button-rounded' : ''} #{cc.attrs.raised ? 'ui-button-raised' : ''}"
|
||||
style="#{cc.attrs.style}"
|
||||
title="#{cc.attrs.title}"
|
||||
badge="#{cc.attrs.badge}"
|
||||
|
||||
@@ -7,12 +7,76 @@
|
||||
xmlns:f="http://xmlns.jcp.org/jsf/core">
|
||||
|
||||
<!--
|
||||
Freya Card - Conteneur avec titre et contenu stylisé
|
||||
═══════════════════════════════════════════════════════════
|
||||
Lions.dev Card - Conteneur carte avec identité Lions.dev
|
||||
═══════════════════════════════════════════════════════════
|
||||
Version: 1.0.0 | Priority: CRITIQUE #9
|
||||
|
||||
Conteneur élégant pour regrouper du contenu avec header/footer optionnels.
|
||||
Styles Lions.dev avec shadow, border-radius et hover effects.
|
||||
|
||||
Usage:
|
||||
<fr:card title="Informations utilisateur" icon="pi pi-user">
|
||||
<fr:card title="Informations" subtitle="Détails utilisateur" icon="pi pi-user">
|
||||
<p>Contenu de la carte...</p>
|
||||
<f:facet name="footer">
|
||||
<fr:commandButton value="Enregistrer" severity="success" />
|
||||
</f:facet>
|
||||
</fr:card>
|
||||
|
||||
Exemples:
|
||||
|
||||
1. Card simple avec titre:
|
||||
<fr:card title="Statistiques" icon="pi pi-chart-bar">
|
||||
<p>45 utilisateurs actifs</p>
|
||||
</fr:card>
|
||||
|
||||
2. Card avec header/footer personnalisés:
|
||||
<fr:card>
|
||||
<f:facet name="header">
|
||||
<div class="flex justify-content-between">
|
||||
<h5>Dashboard</h5>
|
||||
<fr:commandButton icon="pi pi-refresh" text="true" />
|
||||
</div>
|
||||
</f:facet>
|
||||
<p>Contenu...</p>
|
||||
<f:facet name="footer">
|
||||
<fr:button value="Voir plus" severity="primary" />
|
||||
</f:facet>
|
||||
</fr:card>
|
||||
|
||||
3. Card avec actions dans le header:
|
||||
<fr:card title="Projet Alpha" icon="pi pi-folder">
|
||||
<f:facet name="actions">
|
||||
<fr:commandButton icon="pi pi-pencil" text="true" size="sm" />
|
||||
<fr:commandButton icon="pi pi-trash" text="true" severity="danger" size="sm" />
|
||||
</f:facet>
|
||||
<p>Description du projet...</p>
|
||||
</fr:card>
|
||||
|
||||
Attributs disponibles:
|
||||
- title: Titre de la carte
|
||||
- subtitle: Sous-titre (affiché sous le titre)
|
||||
- icon: Icône PrimeIcons dans le header
|
||||
- toggleable: true/false - Permet de plier/déplier (utiliser panel pour cela)
|
||||
- collapsed: true/false - État initial plié
|
||||
- styleClass: Classes CSS additionnelles (card-hover, card-clickable, card-outlined, etc.)
|
||||
|
||||
Facets disponibles:
|
||||
- header: Contenu personnalisé du header
|
||||
- footer: Contenu du footer
|
||||
- actions: Actions dans le header (boutons, etc.)
|
||||
|
||||
Classes utilitaires:
|
||||
- card-hover: Effet hover avec translateY
|
||||
- card-clickable: Carte cliquable (curseur pointer)
|
||||
- card-compact: Padding réduit
|
||||
- card-large: Padding augmenté
|
||||
- card-outlined: Sans background, bordure seulement
|
||||
- card-elevated: Shadow XL
|
||||
- card-flat: Pas de shadow
|
||||
- card-border-top: Bordure colorée en haut
|
||||
- card-border-left: Bordure colorée à gauche
|
||||
- card-primary/success/info/warning/danger/help: Couleur de bordure
|
||||
-->
|
||||
|
||||
<composite:interface>
|
||||
|
||||
@@ -6,14 +6,63 @@
|
||||
xmlns:p="http://primefaces.org/ui">
|
||||
|
||||
<!--
|
||||
Freya CommandButton - Bouton d'action avec style Freya
|
||||
═══════════════════════════════════════════════════════════
|
||||
Lions.dev CommandButton - Bouton d'action avec identité Lions.dev
|
||||
═══════════════════════════════════════════════════════════
|
||||
Version: 1.0.0
|
||||
Priority: CRITIQUE #1
|
||||
|
||||
Fonctionnalités:
|
||||
- Gradients Lions.dev élégants
|
||||
- Animations hover (translateY + shadow elevation)
|
||||
- Focus ring accessible (WCAG 2.1 AA)
|
||||
- Support complet des variantes (solid, outlined, text, link, rounded, raised)
|
||||
- Support des tailles (sm, base, lg)
|
||||
- States (loading, disabled)
|
||||
- Badge support
|
||||
|
||||
Usage:
|
||||
<fr:commandButton value="Sauvegarder"
|
||||
icon="pi pi-save"
|
||||
action="#{bean.save}"
|
||||
<fr:commandButton value="Sauvegarder"
|
||||
icon="pi pi-save"
|
||||
action="#{bean.save}"
|
||||
update="@form"
|
||||
severity="success" />
|
||||
severity="success"
|
||||
size="lg"
|
||||
raised="true" />
|
||||
|
||||
Exemples de variantes:
|
||||
|
||||
1. Bouton solid primary (défaut):
|
||||
<fr:commandButton value="Primary Action" severity="primary" />
|
||||
|
||||
2. Bouton outlined success:
|
||||
<fr:commandButton value="Save" severity="success" outlined="true" />
|
||||
|
||||
3. Bouton text danger:
|
||||
<fr:commandButton value="Delete" severity="danger" text="true" />
|
||||
|
||||
4. Bouton premium (gold):
|
||||
<fr:commandButton value="Upgrade" severity="help" raised="true" />
|
||||
|
||||
5. Bouton rounded large:
|
||||
<fr:commandButton value="Submit" size="lg" rounded="true" />
|
||||
|
||||
6. Icon-only button:
|
||||
<fr:commandButton icon="pi pi-search" title="Rechercher" />
|
||||
|
||||
Severity disponibles:
|
||||
- primary (Lions Blue #2D9BEF - défaut)
|
||||
- secondary (Gray)
|
||||
- success (Green #22BB69)
|
||||
- info (Cyan #00BCD4)
|
||||
- warning (Orange #FF9800)
|
||||
- help (Lions Gold #FFC700 - premium)
|
||||
- danger (Red #F44336)
|
||||
|
||||
Tailles disponibles:
|
||||
- sm (32px height)
|
||||
- base (40px height - défaut)
|
||||
- lg (48px height)
|
||||
-->
|
||||
|
||||
<composite:interface>
|
||||
@@ -41,6 +90,10 @@
|
||||
|
||||
<!-- Severity: primary, secondary, success, info, warning, help, danger -->
|
||||
<composite:attribute name="severity" required="false" type="java.lang.String" />
|
||||
|
||||
<!-- Size: sm, base, lg -->
|
||||
<composite:attribute name="size" required="false" type="java.lang.String" default="base" />
|
||||
|
||||
<!-- Variants -->
|
||||
<composite:attribute name="text" required="false" type="java.lang.Boolean" default="false" />
|
||||
<composite:attribute name="outlined" required="false" type="java.lang.Boolean" default="false" />
|
||||
@@ -52,6 +105,13 @@
|
||||
|
||||
<!-- Action source -->
|
||||
<composite:actionSource name="button" targets="commandButton" />
|
||||
|
||||
<!-- AJAX Behaviors Support -->
|
||||
<composite:clientBehavior name="action" event="action" targets="commandButton" default="true" />
|
||||
<composite:clientBehavior name="click" event="click" targets="commandButton" />
|
||||
|
||||
<!-- Support for child content (HTML inside the button) -->
|
||||
<composite:insertChildren />
|
||||
</composite:interface>
|
||||
|
||||
<composite:implementation>
|
||||
@@ -71,13 +131,15 @@
|
||||
global="#{cc.attrs.global}"
|
||||
validateClient="#{cc.attrs.validateClient}"
|
||||
immediate="#{cc.attrs.immediate}"
|
||||
styleClass="#{cc.attrs.styleClass} #{not empty cc.attrs.severity ? 'ui-button-'.concat(cc.attrs.severity) : ''} #{cc.attrs.text ? 'ui-button-text' : ''} #{cc.attrs.outlined ? 'ui-button-outlined' : ''} #{cc.attrs.link ? 'ui-button-link' : ''} #{cc.attrs.rounded ? 'ui-button-rounded' : ''} #{cc.attrs.raised ? 'ui-button-raised' : ''}"
|
||||
styleClass="#{cc.attrs.styleClass} #{not empty cc.attrs.severity ? 'ui-button-'.concat(cc.attrs.severity) : ''} #{not empty cc.attrs.size and cc.attrs.size ne 'base' ? 'ui-button-'.concat(cc.attrs.size) : ''} #{cc.attrs.text ? 'ui-button-text' : ''} #{cc.attrs.outlined ? 'ui-button-outlined' : ''} #{cc.attrs.link ? 'ui-button-link' : ''} #{cc.attrs.rounded ? 'ui-button-rounded' : ''} #{cc.attrs.raised ? 'ui-button-raised' : ''}"
|
||||
style="#{cc.attrs.style}"
|
||||
title="#{cc.attrs.title}"
|
||||
type="#{cc.attrs.type}"
|
||||
widgetVar="#{cc.attrs.widgetVar}"
|
||||
badge="#{cc.attrs.badge}"
|
||||
badgeSeverity="#{cc.attrs.badgeSeverity}" />
|
||||
badgeSeverity="#{cc.attrs.badgeSeverity}">
|
||||
<composite:insertChildren />
|
||||
</p:commandButton>
|
||||
</composite:implementation>
|
||||
|
||||
</html>
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE html>
|
||||
<html xmlns="http://www.w3.org/1999/xhtml"
|
||||
xmlns:composite="http://xmlns.jcp.org/jsf/composite"
|
||||
xmlns:h="http://xmlns.jcp.org/jsf/html"
|
||||
xmlns:p="http://primefaces.org/ui">
|
||||
<composite:interface>
|
||||
<composite:attribute name="id" required="false" type="java.lang.String" />
|
||||
<composite:attribute name="header" required="false" type="java.lang.String" default="Confirmation" />
|
||||
<composite:attribute name="message" required="false" type="java.lang.String" />
|
||||
<composite:attribute name="severity" required="false" type="java.lang.String" default="alert" />
|
||||
<composite:attribute name="icon" required="false" type="java.lang.String" />
|
||||
<composite:attribute name="widgetVar" required="false" type="java.lang.String" />
|
||||
<composite:attribute name="closable" required="false" type="java.lang.Boolean" default="true" />
|
||||
<composite:attribute name="visible" required="false" type="java.lang.Boolean" default="false" />
|
||||
<composite:attribute name="appendTo" required="false" type="java.lang.String" default="@(body)" />
|
||||
<composite:attribute name="styleClass" required="false" type="java.lang.String" />
|
||||
<composite:attribute name="global" required="false" type="java.lang.Boolean" default="false" />
|
||||
</composite:interface>
|
||||
<composite:implementation>
|
||||
<p:confirmDialog id="confirmDialog"
|
||||
header="#{cc.attrs.header}"
|
||||
message="#{cc.attrs.message}"
|
||||
severity="#{cc.attrs.severity}"
|
||||
icon="#{cc.attrs.icon}"
|
||||
widgetVar="#{cc.attrs.widgetVar}"
|
||||
closable="#{cc.attrs.closable}"
|
||||
visible="#{cc.attrs.visible}"
|
||||
appendTo="#{cc.attrs.appendTo}"
|
||||
styleClass="#{cc.attrs.styleClass}"
|
||||
global="#{cc.attrs.global}" />
|
||||
</composite:implementation>
|
||||
</html>
|
||||
@@ -7,18 +7,177 @@
|
||||
xmlns:f="http://xmlns.jcp.org/jsf/core">
|
||||
|
||||
<!--
|
||||
Freya DataTable - DataTable avec style Freya et fonctionnalités courantes
|
||||
═══════════════════════════════════════════════════════════
|
||||
Lions.dev DataTable - Tableau de données avec tri/filtre/pagination
|
||||
═══════════════════════════════════════════════════════════
|
||||
Version: 1.0.0 | Priority: CRITIQUE #16
|
||||
|
||||
Tableau de données interactif avec tri, filtrage, pagination et sélection.
|
||||
Styles Lions.dev avec header coloré, striped rows et interactions fluides.
|
||||
|
||||
Usage:
|
||||
<fr:dataTable id="users"
|
||||
value="#{bean.users}"
|
||||
var="user"
|
||||
paginator="true"
|
||||
rows="10"
|
||||
globalFilter="true">
|
||||
<p:column headerText="Nom">#{user.nom}</p:column>
|
||||
<fr:dataTable id="users" value="#{bean.users}" var="user" paginator="true" rows="10">
|
||||
<p:column headerText="Nom" sortBy="#{user.nom}" filterBy="#{user.nom}">#{user.nom}</p:column>
|
||||
<p:column headerText="Email">#{user.email}</p:column>
|
||||
</fr:dataTable>
|
||||
|
||||
Exemples:
|
||||
|
||||
1. Tableau simple avec pagination:
|
||||
<fr:dataTable id="users"
|
||||
value="#{bean.users}"
|
||||
var="user"
|
||||
paginator="true"
|
||||
rows="10"
|
||||
rowsPerPageTemplate="5,10,25,50">
|
||||
<p:column headerText="Nom" sortBy="#{user.nom}">#{user.nom}</p:column>
|
||||
<p:column headerText="Email">#{user.email}</p:column>
|
||||
<p:column headerText="Téléphone">#{user.telephone}</p:column>
|
||||
</fr:dataTable>
|
||||
|
||||
2. Tableau avec filtre global:
|
||||
<fr:dataTable id="products"
|
||||
value="#{bean.products}"
|
||||
var="product"
|
||||
paginator="true"
|
||||
rows="15"
|
||||
globalFilter="true"
|
||||
globalFilterPlaceholder="Rechercher un produit..."
|
||||
widgetVar="productsTable">
|
||||
<p:column headerText="Référence" sortBy="#{product.ref}">#{product.ref}</p:column>
|
||||
<p:column headerText="Nom" sortBy="#{product.nom}" filterBy="#{product.nom}">#{product.nom}</p:column>
|
||||
<p:column headerText="Prix" sortBy="#{product.prix}">#{product.prix} FCFA</p:column>
|
||||
</fr:dataTable>
|
||||
|
||||
3. Tableau avec sélection multiple:
|
||||
<fr:dataTable id="clients"
|
||||
value="#{bean.clients}"
|
||||
var="client"
|
||||
selection="#{bean.selectedClients}"
|
||||
selectionMode="multiple"
|
||||
rowKey="#{client.id}"
|
||||
paginator="true"
|
||||
rows="20">
|
||||
<p:column selectionMode="multiple" style="width:3rem" />
|
||||
<p:column headerText="Code">#{client.code}</p:column>
|
||||
<p:column headerText="Nom">#{client.nom}</p:column>
|
||||
<p:column headerText="Ville">#{client.ville}</p:column>
|
||||
</fr:dataTable>
|
||||
|
||||
4. Tableau avec colonnes triables et filtrables:
|
||||
<fr:dataTable id="orders"
|
||||
value="#{bean.orders}"
|
||||
var="order"
|
||||
paginator="true"
|
||||
rows="10"
|
||||
sortMode="multiple">
|
||||
<p:column headerText="N° Commande" sortBy="#{order.numero}" filterBy="#{order.numero}">
|
||||
#{order.numero}
|
||||
</p:column>
|
||||
<p:column headerText="Client" sortBy="#{order.client}" filterBy="#{order.client}">
|
||||
#{order.client}
|
||||
</p:column>
|
||||
<p:column headerText="Date" sortBy="#{order.date}" filterBy="#{order.date}">
|
||||
<h:outputText value="#{order.date}">
|
||||
<f:convertDateTime pattern="dd/MM/yyyy" />
|
||||
</h:outputText>
|
||||
</p:column>
|
||||
<p:column headerText="Montant" sortBy="#{order.montant}">
|
||||
#{order.montant} FCFA
|
||||
</p:column>
|
||||
</fr:dataTable>
|
||||
|
||||
5. Tableau avec colonnes redimensionnables:
|
||||
<fr:dataTable id="employees"
|
||||
value="#{bean.employees}"
|
||||
var="emp"
|
||||
resizableColumns="true"
|
||||
reorderableColumns="true"
|
||||
paginator="true"
|
||||
rows="25">
|
||||
<p:column headerText="Matricule">#{emp.matricule}</p:column>
|
||||
<p:column headerText="Nom complet">#{emp.nom} #{emp.prenom}</p:column>
|
||||
<p:column headerText="Département">#{emp.departement}</p:column>
|
||||
<p:column headerText="Salaire">#{emp.salaire} FCFA</p:column>
|
||||
</fr:dataTable>
|
||||
|
||||
6. Tableau scrollable avec hauteur fixe:
|
||||
<fr:dataTable id="logs"
|
||||
value="#{bean.logs}"
|
||||
var="log"
|
||||
scrollable="true"
|
||||
scrollHeight="400px">
|
||||
<p:column headerText="Timestamp">#{log.timestamp}</p:column>
|
||||
<p:column headerText="Niveau">#{log.niveau}</p:column>
|
||||
<p:column headerText="Message">#{log.message}</p:column>
|
||||
</fr:dataTable>
|
||||
|
||||
Attributs disponibles:
|
||||
- value: Collection de données (List, LazyDataModel, etc.)
|
||||
- var: Variable d'itération pour chaque ligne
|
||||
- paginator: true/false - Activer la pagination (défaut: true)
|
||||
- rows: Nombre de lignes par page (défaut: 10)
|
||||
- rowsPerPageTemplate: Options pagination "5,10,25,50" (défaut)
|
||||
- paginatorPosition: Position - "top", "bottom", "both" (défaut: bottom)
|
||||
- selection: Variable pour stocker la sélection
|
||||
- selectionMode: "single" ou "multiple" - Mode de sélection
|
||||
- rowKey: Expression EL pour identifier les lignes (requis pour sélection)
|
||||
- globalFilter: true/false - Afficher le champ de recherche global
|
||||
- globalFilterPlaceholder: Texte du champ de recherche (défaut: "Rechercher...")
|
||||
- sortMode: "single" ou "multiple" - Mode de tri (défaut: single)
|
||||
- resizableColumns: true/false - Colonnes redimensionnables
|
||||
- reorderableColumns: true/false - Colonnes réordonnables
|
||||
- scrollable: true/false - Tableau scrollable
|
||||
- scrollHeight: Hauteur scroll (ex: "400px")
|
||||
- lazy: true/false - Chargement lazy (avec LazyDataModel)
|
||||
- rowStyleClass: Expression EL pour classe CSS de ligne
|
||||
- emptyMessage: Message si vide (défaut: "Aucun enregistrement trouvé")
|
||||
- stripedRows: true/false - Lignes alternées (défaut: true)
|
||||
- showGridlines: true/false - Afficher les bordures (défaut: false)
|
||||
- size: "small", "normal", "large" - Taille des cellules
|
||||
- widgetVar: Variable JS pour contrôle programmatique
|
||||
- styleClass, style: Classes et styles CSS
|
||||
|
||||
Facets disponibles:
|
||||
- header: Contenu personnalisé du header
|
||||
|
||||
Events AJAX disponibles:
|
||||
- rowSelect, rowUnselect, rowDblselect: Sélection de lignes
|
||||
- rowSelectCheckbox, rowUnselectCheckbox: Sélection par checkbox
|
||||
- toggleSelect: Sélection "Tout/Rien"
|
||||
- page: Changement de page
|
||||
- sort: Tri
|
||||
- filter: Filtrage
|
||||
- rowEdit, rowEditInit, rowEditCancel: Édition en ligne
|
||||
- colReorder, colResize: Réorganisation/redimensionnement colonnes
|
||||
|
||||
Colonnes p:column:
|
||||
- headerText: Texte du header
|
||||
- sortBy: Expression EL pour le tri
|
||||
- filterBy: Expression EL pour le filtre
|
||||
- filterMatchMode: "contains", "startsWith", "endsWith", "equals", etc.
|
||||
- selectionMode: "multiple" ou "single" pour colonne de sélection
|
||||
- style, styleClass: Styles de la colonne
|
||||
|
||||
Exemple AJAX:
|
||||
<fr:dataTable id="users" value="#{bean.users}" var="user"
|
||||
selection="#{bean.selectedUser}" selectionMode="single" rowKey="#{user.id}">
|
||||
<p:ajax event="rowSelect" listener="#{bean.onRowSelect}" update="userDetail" />
|
||||
<p:column headerText="Nom">#{user.nom}</p:column>
|
||||
</fr:dataTable>
|
||||
|
||||
Contrôle JavaScript (avec widgetVar):
|
||||
PF('usersTable').filter(); // Appliquer les filtres
|
||||
PF('usersTable').clearFilters(); // Effacer les filtres
|
||||
PF('usersTable').unselectAllRows(); // Désélectionner tout
|
||||
|
||||
Notes:
|
||||
- Pagination automatique avec template français
|
||||
- Tri multi-colonnes avec Shift+Click
|
||||
- Filtrage en temps réel sur chaque colonne
|
||||
- Sélection avec checkbox ou clic sur ligne
|
||||
- Support du lazy loading pour grandes quantités de données
|
||||
- Responsive avec scroll horizontal automatique sur mobile
|
||||
-->
|
||||
|
||||
<composite:interface>
|
||||
@@ -59,6 +218,22 @@
|
||||
<!-- Facets -->
|
||||
<composite:facet name="header" />
|
||||
|
||||
<!-- AJAX Behaviors Support -->
|
||||
<composite:clientBehavior name="rowSelect" event="rowSelect" targets="dataTable" />
|
||||
<composite:clientBehavior name="rowUnselect" event="rowUnselect" targets="dataTable" />
|
||||
<composite:clientBehavior name="rowDblselect" event="rowDblselect" targets="dataTable" />
|
||||
<composite:clientBehavior name="rowSelectCheckbox" event="rowSelectCheckbox" targets="dataTable" />
|
||||
<composite:clientBehavior name="rowUnselectCheckbox" event="rowUnselectCheckbox" targets="dataTable" />
|
||||
<composite:clientBehavior name="toggleSelect" event="toggleSelect" targets="dataTable" />
|
||||
<composite:clientBehavior name="page" event="page" targets="dataTable" />
|
||||
<composite:clientBehavior name="sort" event="sort" targets="dataTable" />
|
||||
<composite:clientBehavior name="filter" event="filter" targets="dataTable" />
|
||||
<composite:clientBehavior name="rowEdit" event="rowEdit" targets="dataTable" />
|
||||
<composite:clientBehavior name="rowEditInit" event="rowEditInit" targets="dataTable" />
|
||||
<composite:clientBehavior name="rowEditCancel" event="rowEditCancel" targets="dataTable" />
|
||||
<composite:clientBehavior name="colReorder" event="colReorder" targets="dataTable" />
|
||||
<composite:clientBehavior name="colResize" event="colResize" targets="dataTable" />
|
||||
|
||||
<!-- Content insertion for columns -->
|
||||
<composite:insertChildren />
|
||||
</composite:interface>
|
||||
|
||||
@@ -7,21 +7,178 @@
|
||||
xmlns:f="http://xmlns.jcp.org/jsf/core">
|
||||
|
||||
<!--
|
||||
Freya DataView - DataView avec style Freya pour affichage liste/grille
|
||||
═══════════════════════════════════════════════════════════
|
||||
Lions.dev DataView - Affichage de données en grille/liste
|
||||
═══════════════════════════════════════════════════════════
|
||||
Version: 1.0.0 | Priority: CRITIQUE #17
|
||||
|
||||
Affichage flexible de données avec mode liste et grille interchangeables.
|
||||
Styles Lions.dev avec basculement layout, pagination et cartes élégantes.
|
||||
|
||||
Usage:
|
||||
<fr:dataView id="products"
|
||||
value="#{bean.products}"
|
||||
var="product"
|
||||
layout="grid"
|
||||
paginator="true"
|
||||
rows="12">
|
||||
<fr:dataView id="products" value="#{bean.products}" var="product" layout="grid" paginator="true" rows="9">
|
||||
<f:facet name="grid">
|
||||
<div class="col-12 md:col-4">
|
||||
<div class="card">#{product.name}</div>
|
||||
</div>
|
||||
</f:facet>
|
||||
</fr:dataView>
|
||||
|
||||
Exemples:
|
||||
|
||||
1. DataView avec basculement liste/grille:
|
||||
<fr:dataView id="products"
|
||||
value="#{bean.products}"
|
||||
var="product"
|
||||
layout="grid"
|
||||
paginator="true"
|
||||
rows="12"
|
||||
widgetVar="productsView">
|
||||
<f:facet name="header">
|
||||
<h5>Catalogue Produits (#{bean.products.size()} articles)</h5>
|
||||
</f:facet>
|
||||
|
||||
<f:facet name="list">
|
||||
<div class="flex align-items-center gap-4 p-4">
|
||||
<img src="#{product.image}" alt="#{product.nom}" style="width:100px" />
|
||||
<div class="flex-1">
|
||||
<h6 class="mb-2">#{product.nom}</h6>
|
||||
<p class="text-secondary">#{product.description}</p>
|
||||
<strong>#{product.prix} FCFA</strong>
|
||||
</div>
|
||||
<fr:commandButton value="Ajouter" icon="pi pi-shopping-cart" severity="success" />
|
||||
</div>
|
||||
</f:facet>
|
||||
|
||||
<f:facet name="grid">
|
||||
<div class="col-12 md:col-6 lg:col-4">
|
||||
<fr:card title="#{product.nom}" icon="pi pi-tag">
|
||||
<img src="#{product.image}" alt="#{product.nom}" style="width:100%" />
|
||||
<p>#{product.description}</p>
|
||||
<f:facet name="footer">
|
||||
<strong>#{product.prix} FCFA</strong>
|
||||
<fr:commandButton value="Ajouter" icon="pi pi-shopping-cart" severity="success" size="sm" />
|
||||
</f:facet>
|
||||
</fr:card>
|
||||
</div>
|
||||
</f:facet>
|
||||
</fr:dataView>
|
||||
|
||||
2. DataView en mode grille uniquement (sans basculement):
|
||||
<fr:dataView id="team"
|
||||
value="#{bean.employees}"
|
||||
var="emp"
|
||||
layout="grid"
|
||||
paginator="true"
|
||||
rows="6"
|
||||
rowsPerPageTemplate="3,6,9,12">
|
||||
<f:facet name="grid">
|
||||
<div class="col-12 md:col-6 lg:col-4">
|
||||
<fr:card styleClass="card-hover text-center">
|
||||
<img src="#{emp.avatar}" alt="#{emp.nom}" class="border-circle mb-3" style="width:120px;height:120px" />
|
||||
<h5>#{emp.nom} #{emp.prenom}</h5>
|
||||
<p class="text-secondary">#{emp.poste}</p>
|
||||
<p><i class="pi pi-envelope mr-2"></i>#{emp.email}</p>
|
||||
</fr:card>
|
||||
</div>
|
||||
</f:facet>
|
||||
</fr:dataView>
|
||||
|
||||
3. DataView en mode liste avec pagination:
|
||||
<fr:dataView id="orders"
|
||||
value="#{bean.orders}"
|
||||
var="order"
|
||||
layout="list"
|
||||
paginator="true"
|
||||
rows="10">
|
||||
<f:facet name="list">
|
||||
<div class="flex justify-content-between align-items-center p-4 border-bottom-1 surface-border">
|
||||
<div>
|
||||
<h6 class="mb-1">Commande ##{order.numero}</h6>
|
||||
<p class="text-secondary mb-0">
|
||||
Client: #{order.client} • Date:
|
||||
<h:outputText value="#{order.date}">
|
||||
<f:convertDateTime pattern="dd/MM/yyyy" />
|
||||
</h:outputText>
|
||||
</p>
|
||||
</div>
|
||||
<div class="text-right">
|
||||
<strong class="text-xl">#{order.montant} FCFA</strong>
|
||||
<br/>
|
||||
<span class="badge badge-#{order.statut == 'Livré' ? 'success' : 'warning'}">
|
||||
#{order.statut}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</f:facet>
|
||||
</fr:dataView>
|
||||
|
||||
4. DataView avec lazy loading:
|
||||
<fr:dataView id="articles"
|
||||
value="#{bean.lazyArticles}"
|
||||
var="article"
|
||||
layout="grid"
|
||||
paginator="true"
|
||||
rows="15"
|
||||
lazy="true">
|
||||
<f:facet name="grid">
|
||||
<div class="col-12 md:col-4">
|
||||
<fr:card title="#{article.titre}">
|
||||
<p>#{article.resume}</p>
|
||||
<small class="text-secondary">
|
||||
Par #{article.auteur} • #{article.date}
|
||||
</small>
|
||||
<f:facet name="footer">
|
||||
<fr:commandButton value="Lire plus" icon="pi pi-arrow-right" text="true" />
|
||||
</f:facet>
|
||||
</fr:card>
|
||||
</div>
|
||||
</f:facet>
|
||||
</fr:dataView>
|
||||
|
||||
Attributs disponibles:
|
||||
- value: Collection de données (List, LazyDataModel, etc.)
|
||||
- var: Variable d'itération pour chaque élément
|
||||
- layout: "list" ou "grid" - Mode d'affichage initial (défaut: list)
|
||||
- gridIcon: Icône bouton grille (défaut: "pi pi-th-large")
|
||||
- listIcon: Icône bouton liste (défaut: "pi pi-bars")
|
||||
- paginator: true/false - Activer la pagination (défaut: true)
|
||||
- rows: Nombre d'éléments par page (défaut: 9)
|
||||
- rowsPerPageTemplate: Options pagination "3,6,9,12" (défaut)
|
||||
- paginatorPosition: Position - "top", "bottom", "both" (défaut: bottom)
|
||||
- lazy: true/false - Chargement lazy (avec LazyDataModel)
|
||||
- emptyMessage: Message si vide (défaut: "Aucun enregistrement trouvé")
|
||||
- widgetVar: Variable JS pour contrôle programmatique
|
||||
- styleClass, style: Classes et styles CSS
|
||||
|
||||
Facets disponibles:
|
||||
- header: Contenu personnalisé du header (titre, compteur, etc.)
|
||||
- list: Template pour le mode liste
|
||||
- grid: Template pour le mode grille
|
||||
|
||||
Structure des facets:
|
||||
- Facet "list": Contient le template d'un élément en mode liste (largeur 100%)
|
||||
- Facet "grid": Contient le template d'un élément en mode grille avec classes de colonnes
|
||||
Utilisez les classes PrimeFlex: col-12 md:col-6 lg:col-4 (grille 3 colonnes sur desktop)
|
||||
|
||||
Exemples de layouts grille:
|
||||
- 2 colonnes: col-12 md:col-6
|
||||
- 3 colonnes: col-12 md:col-6 lg:col-4
|
||||
- 4 colonnes: col-12 sm:col-6 md:col-4 lg:col-3
|
||||
- 6 colonnes: col-12 sm:col-4 md:col-3 lg:col-2
|
||||
|
||||
Contrôle JavaScript (avec widgetVar):
|
||||
PF('productsView').switchToListLayout(); // Passer en mode liste
|
||||
PF('productsView').switchToGridLayout(); // Passer en mode grille
|
||||
|
||||
Notes:
|
||||
- Basculement liste/grille avec boutons dans le header
|
||||
- Mode grille: affichage en cartes flexibles
|
||||
- Mode liste: affichage en lignes complètes
|
||||
- Pagination automatique avec template français
|
||||
- Responsive: grille s'adapte automatiquement sur mobile
|
||||
- Utiliser fr:card dans les facets pour un style cohérent
|
||||
- Support du lazy loading pour grandes quantités de données
|
||||
-->
|
||||
|
||||
<composite:interface>
|
||||
|
||||
257
runtime/src/main/resources/META-INF/resources/freya/dialog.xhtml
Normal file
257
runtime/src/main/resources/META-INF/resources/freya/dialog.xhtml
Normal file
@@ -0,0 +1,257 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE html>
|
||||
<html xmlns="http://www.w3.org/1999/xhtml"
|
||||
xmlns:composite="http://xmlns.jcp.org/jsf/composite"
|
||||
xmlns:h="http://xmlns.jcp.org/jsf/html"
|
||||
xmlns:p="http://primefaces.org/ui"
|
||||
xmlns:f="http://xmlns.jcp.org/jsf/core">
|
||||
|
||||
<!--
|
||||
═══════════════════════════════════════════════════════════
|
||||
Lions.dev Dialog - Boîtes de dialogue modales
|
||||
═══════════════════════════════════════════════════════════
|
||||
Version: 1.0.0 | Priority: CRITIQUE #21
|
||||
|
||||
Boîtes de dialogue modales avec header, content et footer personnalisables.
|
||||
Styles Lions.dev avec backdrop blur, animations fluides et header coloré.
|
||||
|
||||
Usage:
|
||||
<fr:dialog header="Titre" widgetVar="dlg">
|
||||
<p:outputLabel value="Contenu..." />
|
||||
<f:facet name="footer">
|
||||
<fr:commandButton value="OK" onclick="PF('dlg').hide()" />
|
||||
</f:facet>
|
||||
</fr:dialog>
|
||||
|
||||
Exemples:
|
||||
|
||||
1. Dialog simple avec footer:
|
||||
<fr:dialog header="Informations utilisateur" widgetVar="userDialog" modal="true" width="600">
|
||||
<p:panelGrid columns="2">
|
||||
<p:outputLabel value="Nom:" />
|
||||
<p:outputLabel value="#{user.nom}" />
|
||||
<p:outputLabel value="Email:" />
|
||||
<p:outputLabel value="#{user.email}" />
|
||||
</p:panelGrid>
|
||||
<f:facet name="footer">
|
||||
<fr:commandButton value="Fermer" onclick="PF('userDialog').hide()" severity="secondary" />
|
||||
</f:facet>
|
||||
</fr:dialog>
|
||||
|
||||
2. Dialog avec formulaire:
|
||||
<fr:dialog header="Nouveau client" widgetVar="newClientDialog" modal="true" width="500">
|
||||
<fr:fieldInput id="nom" label="Nom" value="#{bean.client.nom}" required="true" />
|
||||
<fr:fieldInput id="email" label="Email" value="#{bean.client.email}" required="true" />
|
||||
<fr:fieldInput id="tel" label="Téléphone" value="#{bean.client.telephone}" />
|
||||
<f:facet name="footer">
|
||||
<fr:commandButton value="Enregistrer" action="#{bean.saveClient}"
|
||||
update="clientsTable" oncomplete="PF('newClientDialog').hide()"
|
||||
severity="success" icon="pi pi-check" />
|
||||
<fr:commandButton value="Annuler" onclick="PF('newClientDialog').hide()"
|
||||
severity="secondary" outlined="true" />
|
||||
</f:facet>
|
||||
</fr:dialog>
|
||||
|
||||
3. Dialog redimensionnable et déplaçable:
|
||||
<fr:dialog header="Paramètres" widgetVar="settingsDialog"
|
||||
draggable="true" resizable="true" minWidth="400" minHeight="300"
|
||||
width="700" height="500">
|
||||
<p:tabView>
|
||||
<p:tab title="Général">
|
||||
<!-- Contenu onglet Général -->
|
||||
</p:tab>
|
||||
<p:tab title="Avancé">
|
||||
<!-- Contenu onglet Avancé -->
|
||||
</p:tab>
|
||||
</p:tabView>
|
||||
</fr:dialog>
|
||||
|
||||
4. Dialog maximisable:
|
||||
<fr:dialog header="Détails commande" widgetVar="orderDialog"
|
||||
modal="true" maximizable="true" width="800">
|
||||
<fr:dataTable value="#{bean.orderItems}" var="item">
|
||||
<p:column headerText="Produit">#{item.produit}</p:column>
|
||||
<p:column headerText="Quantité">#{item.quantite}</p:column>
|
||||
<p:column headerText="Prix">#{item.prix} FCFA</p:column>
|
||||
</fr:dataTable>
|
||||
<f:facet name="footer">
|
||||
<fr:commandButton value="Imprimer" icon="pi pi-print" />
|
||||
<fr:commandButton value="Fermer" onclick="PF('orderDialog').hide()" severity="secondary" />
|
||||
</f:facet>
|
||||
</fr:dialog>
|
||||
|
||||
5. Dialog avec position personnalisée:
|
||||
<fr:dialog header="Notification" widgetVar="notifDialog"
|
||||
position="top" modal="false" showEffect="fade" hideEffect="fade">
|
||||
<p:outputLabel value="Une nouvelle mise à jour est disponible." />
|
||||
<f:facet name="footer">
|
||||
<fr:commandButton value="Télécharger" severity="primary" />
|
||||
<fr:commandButton value="Plus tard" onclick="PF('notifDialog').hide()" text="true" />
|
||||
</f:facet>
|
||||
</fr:dialog>
|
||||
|
||||
Attributs disponibles:
|
||||
- header: Titre du dialog
|
||||
- widgetVar: Variable JS pour contrôle programmatique (REQUIS)
|
||||
- modal: true/false - Dialog modal avec backdrop (défaut: false)
|
||||
- visible: true/false - Visible au chargement (défaut: false)
|
||||
- width: Largeur (px, %, auto) - défaut: auto
|
||||
- height: Hauteur (px, %, auto) - défaut: auto
|
||||
- minWidth: Largeur minimale en px (défaut: 150)
|
||||
- minHeight: Hauteur minimale en px (défaut: 150)
|
||||
- draggable: true/false - Déplaçable par le header (défaut: false)
|
||||
- resizable: true/false - Redimensionnable (défaut: false)
|
||||
- maximizable: true/false - Bouton maximiser (défaut: false)
|
||||
- minimizable: true/false - Bouton minimiser (défaut: false)
|
||||
- closable: true/false - Bouton fermer (défaut: true)
|
||||
- closeOnEscape: true/false - Fermer avec Échap (défaut: false)
|
||||
- position: Position - "center", "top", "bottom", "left", "right" (défaut: center)
|
||||
- showEffect: Effet d'apparition - "fade", "slide", "explode", etc.
|
||||
- hideEffect: Effet de disparition - "fade", "slide", "explode", etc.
|
||||
- appendTo: Sélecteur de l'élément parent (défaut: @(body))
|
||||
- responsive: true/false - Responsive sur mobile (défaut: true)
|
||||
- styleClass: Classes CSS additionnelles
|
||||
- style: Styles CSS inline
|
||||
|
||||
Facets disponibles:
|
||||
- header: Header personnalisé (remplace l'attribut header)
|
||||
- footer: Footer avec boutons d'action
|
||||
|
||||
Contrôle JavaScript (widgetVar):
|
||||
```javascript
|
||||
// Afficher le dialog
|
||||
PF('dlg').show();
|
||||
|
||||
// Masquer le dialog
|
||||
PF('dlg').hide();
|
||||
|
||||
// Basculer l'affichage
|
||||
PF('dlg').toggle();
|
||||
|
||||
// Maximiser
|
||||
PF('dlg').maximize();
|
||||
|
||||
// Minimiser
|
||||
PF('dlg').minimize();
|
||||
|
||||
// Restaurer
|
||||
PF('dlg').restore();
|
||||
```
|
||||
|
||||
Events AJAX disponibles:
|
||||
- open: Déclenché à l'ouverture
|
||||
- close: Déclenché à la fermeture
|
||||
- maximize: Déclenché à la maximisation
|
||||
- minimize: Déclenché à la minimisation
|
||||
- move: Déclenché au déplacement
|
||||
- resize: Déclenché au redimensionnement
|
||||
|
||||
Exemple avec AJAX:
|
||||
<fr:dialog header="Confirmation" widgetVar="confirmDialog">
|
||||
<p:ajax event="close" listener="#{bean.onDialogClose}" />
|
||||
<p:outputLabel value="Êtes-vous sûr ?" />
|
||||
</fr:dialog>
|
||||
|
||||
Positions disponibles:
|
||||
- center: Centre de l'écran (défaut)
|
||||
- top: En haut centré
|
||||
- bottom: En bas centré
|
||||
- left: À gauche centré
|
||||
- right: À droite centré
|
||||
- top-left, top-right, bottom-left, bottom-right: Coins
|
||||
|
||||
Notes:
|
||||
- widgetVar est REQUIS pour contrôler le dialog via JS
|
||||
- Modal true bloque l'interaction avec la page (backdrop)
|
||||
- Backdrop avec blur 3px pour effet moderne
|
||||
- Animation fade-in/fade-out fluide
|
||||
- Header avec gradient bleu Lions.dev
|
||||
- Footer avec gradient gris
|
||||
- Responsive: max 95vw/95vh sur mobile
|
||||
- Support drag & drop du header si draggable="true"
|
||||
- Redimensionnable par coin inférieur droit si resizable="true"
|
||||
- Échap pour fermer si closeOnEscape="true"
|
||||
- Support ARIA pour accessibilité
|
||||
- Focus trap automatique en mode modal
|
||||
-->
|
||||
|
||||
<composite:interface>
|
||||
<composite:attribute name="id" required="false" type="java.lang.String" />
|
||||
<composite:attribute name="header" required="false" type="java.lang.String" />
|
||||
<composite:attribute name="widgetVar" required="true" type="java.lang.String" />
|
||||
<composite:attribute name="modal" required="false" type="java.lang.Boolean" default="false" />
|
||||
<composite:attribute name="visible" required="false" type="java.lang.Boolean" default="false" />
|
||||
<composite:attribute name="width" required="false" type="java.lang.String" default="auto" />
|
||||
<composite:attribute name="height" required="false" type="java.lang.String" default="auto" />
|
||||
<composite:attribute name="minWidth" required="false" type="java.lang.Integer" default="150" />
|
||||
<composite:attribute name="minHeight" required="false" type="java.lang.Integer" default="150" />
|
||||
<composite:attribute name="draggable" required="false" type="java.lang.Boolean" default="false" />
|
||||
<composite:attribute name="resizable" required="false" type="java.lang.Boolean" default="false" />
|
||||
<composite:attribute name="maximizable" required="false" type="java.lang.Boolean" default="false" />
|
||||
<composite:attribute name="minimizable" required="false" type="java.lang.Boolean" default="false" />
|
||||
<composite:attribute name="closable" required="false" type="java.lang.Boolean" default="true" />
|
||||
<composite:attribute name="closeOnEscape" required="false" type="java.lang.Boolean" default="false" />
|
||||
<composite:attribute name="position" required="false" type="java.lang.String" default="center" />
|
||||
<composite:attribute name="showEffect" required="false" type="java.lang.String" />
|
||||
<composite:attribute name="hideEffect" required="false" type="java.lang.String" />
|
||||
<composite:attribute name="appendTo" required="false" type="java.lang.String" default="@(body)" />
|
||||
<composite:attribute name="responsive" required="false" type="java.lang.Boolean" default="true" />
|
||||
<composite:attribute name="styleClass" required="false" type="java.lang.String" />
|
||||
<composite:attribute name="style" required="false" type="java.lang.String" />
|
||||
|
||||
<!-- Facets -->
|
||||
<composite:facet name="header" />
|
||||
<composite:facet name="footer" />
|
||||
|
||||
<!-- AJAX Behaviors Support -->
|
||||
<composite:clientBehavior name="open" event="open" targets="dialog" />
|
||||
<composite:clientBehavior name="close" event="close" targets="dialog" />
|
||||
<composite:clientBehavior name="maximize" event="maximize" targets="dialog" />
|
||||
<composite:clientBehavior name="minimize" event="minimize" targets="dialog" />
|
||||
<composite:clientBehavior name="move" event="move" targets="dialog" />
|
||||
<composite:clientBehavior name="resize" event="resize" targets="dialog" />
|
||||
|
||||
<!-- Content insertion -->
|
||||
<composite:insertChildren />
|
||||
</composite:interface>
|
||||
|
||||
<composite:implementation>
|
||||
<p:dialog id="dialog"
|
||||
header="#{cc.attrs.header}"
|
||||
widgetVar="#{cc.attrs.widgetVar}"
|
||||
modal="#{cc.attrs.modal}"
|
||||
visible="#{cc.attrs.visible}"
|
||||
width="#{cc.attrs.width}"
|
||||
height="#{cc.attrs.height}"
|
||||
minWidth="#{cc.attrs.minWidth}"
|
||||
minHeight="#{cc.attrs.minHeight}"
|
||||
draggable="#{cc.attrs.draggable}"
|
||||
resizable="#{cc.attrs.resizable}"
|
||||
maximizable="#{cc.attrs.maximizable}"
|
||||
minimizable="#{cc.attrs.minimizable}"
|
||||
closable="#{cc.attrs.closable}"
|
||||
closeOnEscape="#{cc.attrs.closeOnEscape}"
|
||||
position="#{cc.attrs.position}"
|
||||
showEffect="#{cc.attrs.showEffect}"
|
||||
hideEffect="#{cc.attrs.hideEffect}"
|
||||
appendTo="#{cc.attrs.appendTo}"
|
||||
responsive="#{cc.attrs.responsive}"
|
||||
styleClass="#{cc.attrs.styleClass}"
|
||||
style="#{cc.attrs.style}">
|
||||
|
||||
<!-- Custom header facet -->
|
||||
<f:facet name="header" rendered="#{not empty cc.facets.header}">
|
||||
<composite:renderFacet name="header" />
|
||||
</f:facet>
|
||||
|
||||
<!-- Content -->
|
||||
<composite:insertChildren />
|
||||
|
||||
<!-- Footer facet -->
|
||||
<f:facet name="footer" rendered="#{not empty cc.facets.footer}">
|
||||
<composite:renderFacet name="footer" />
|
||||
</f:facet>
|
||||
</p:dialog>
|
||||
</composite:implementation>
|
||||
|
||||
</html>
|
||||
@@ -6,14 +6,96 @@
|
||||
xmlns:p="http://primefaces.org/ui">
|
||||
|
||||
<!--
|
||||
Freya Field AutoComplete - Pattern field + label + autoComplete + message
|
||||
═══════════════════════════════════════════════════════════
|
||||
Lions.dev Field AutoComplete - Champ avec suggestions
|
||||
═══════════════════════════════════════════════════════════
|
||||
Version: 1.0.0 | Priority: CRITIQUE #13
|
||||
|
||||
Champ texte avec auto-complétion dynamique et suggestions en temps réel.
|
||||
Styles Lions.dev avec dropdown, sélection multiple et recherche serveur.
|
||||
|
||||
Usage:
|
||||
<fr:fieldAutoComplete id="ville"
|
||||
label="Ville"
|
||||
value="#{bean.ville}"
|
||||
completeMethod="#{bean.completeVille}"
|
||||
dropdown="true" />
|
||||
<fr:fieldAutoComplete id="ville" label="Ville" value="#{bean.ville}" completeMethod="#{bean.completeVille}" />
|
||||
|
||||
Exemples:
|
||||
|
||||
1. AutoComplete simple avec dropdown:
|
||||
<fr:fieldAutoComplete id="ville"
|
||||
label="Ville"
|
||||
value="#{bean.ville}"
|
||||
completeMethod="#{bean.completeVille}"
|
||||
dropdown="true"
|
||||
placeholder="Saisissez une ville..." />
|
||||
|
||||
2. Sélection multiple (tags):
|
||||
<fr:fieldAutoComplete id="competences"
|
||||
label="Compétences"
|
||||
value="#{bean.competences}"
|
||||
completeMethod="#{bean.completeCompetences}"
|
||||
multiple="true"
|
||||
placeholder="Ajoutez vos compétences..."
|
||||
size="lg" />
|
||||
|
||||
3. AutoComplete avec objets complexes:
|
||||
<fr:fieldAutoComplete id="utilisateur"
|
||||
label="Utilisateur"
|
||||
value="#{bean.selectedUser}"
|
||||
completeMethod="#{bean.completeUsers}"
|
||||
var="user"
|
||||
itemLabel="#{user.nom}"
|
||||
itemValue="#{user}"
|
||||
converter="userConverter" />
|
||||
|
||||
4. Force selection (seulement valeurs de la liste):
|
||||
<fr:fieldAutoComplete id="pays"
|
||||
label="Pays"
|
||||
value="#{bean.pays}"
|
||||
completeMethod="#{bean.completePays}"
|
||||
forceSelection="true"
|
||||
dropdown="true"
|
||||
size="sm" />
|
||||
|
||||
Attributs disponibles:
|
||||
- label: Libellé du champ
|
||||
- value: Valeur liée au bean
|
||||
- completeMethod: Méthode backing bean (List complete(String query))
|
||||
- var: Variable d'itération pour objets complexes
|
||||
- itemLabel: Expression EL pour le libellé (si objets complexes)
|
||||
- itemValue: Expression EL pour la valeur (si objets complexes)
|
||||
- required: true/false - Champ obligatoire
|
||||
- disabled: true/false - Champ désactivé
|
||||
- readonly: true/false - Lecture seule
|
||||
- placeholder: Texte d'aide
|
||||
- dropdown: true/false - Bouton dropdown pour afficher toutes les suggestions
|
||||
- multiple: true/false - Sélection multiple (mode tags)
|
||||
- minQueryLength: Nombre min de caractères avant recherche (défaut: 1)
|
||||
- queryDelay: Délai en ms avant recherche (défaut: 300)
|
||||
- maxResults: Nombre max de résultats affichés (défaut: 10)
|
||||
- forceSelection: true/false - N'accepter que les valeurs de la liste
|
||||
- scrollHeight: Hauteur max du panel de suggestions en px (défaut: 200)
|
||||
- converter: Converter JSF pour objets complexes
|
||||
- size: sm | base (défaut) | lg - Taille du champ
|
||||
- styleClass: Classes CSS additionnelles
|
||||
|
||||
Méthode completeMethod (backing bean):
|
||||
```java
|
||||
public List<String> completeVille(String query) {
|
||||
return villeService.findByNom(query);
|
||||
}
|
||||
|
||||
// Pour objets complexes:
|
||||
public List<User> completeUsers(String query) {
|
||||
return userService.searchByName(query);
|
||||
}
|
||||
```
|
||||
|
||||
Notes:
|
||||
- Recherche déclenchée après minQueryLength caractères
|
||||
- Délai queryDelay pour éviter requêtes excessives
|
||||
- Support du clavier (flèches, Enter, Esc)
|
||||
- Mode multiple: affiche les sélections en tags bleus
|
||||
- forceSelection: empêche saisie libre
|
||||
- dropdown: permet d'afficher toutes les suggestions
|
||||
-->
|
||||
|
||||
<composite:interface>
|
||||
@@ -40,6 +122,7 @@
|
||||
<composite:attribute name="forceSelection" required="false" type="java.lang.Boolean" default="false" />
|
||||
<composite:attribute name="scrollHeight" required="false" type="java.lang.Integer" default="200" />
|
||||
<composite:attribute name="converter" required="false" />
|
||||
<composite:attribute name="size" required="false" type="java.lang.String" default="base" />
|
||||
|
||||
<!-- Binding for input component -->
|
||||
<composite:editableValueHolder name="input" targets="autoCompleteComponent" />
|
||||
@@ -47,7 +130,7 @@
|
||||
|
||||
<composite:implementation>
|
||||
<!-- Freya field pattern -->
|
||||
<div class="field #{cc.attrs.styleClass}">
|
||||
<div class="field #{not empty cc.attrs.size and cc.attrs.size ne 'base' ? 'field-'.concat(cc.attrs.size) : ''} #{cc.attrs.styleClass}">
|
||||
<!-- Label with required indicator -->
|
||||
<p:outputLabel for="autoCompleteComponent"
|
||||
value="#{cc.attrs.label}"
|
||||
|
||||
@@ -6,14 +6,80 @@
|
||||
xmlns:p="http://primefaces.org/ui">
|
||||
|
||||
<!--
|
||||
Freya Field Calendar - Pattern field + label + datePicker + message
|
||||
═══════════════════════════════════════════════════════════
|
||||
Lions.dev Field Calendar - Champ date avec calendrier popup
|
||||
═══════════════════════════════════════════════════════════
|
||||
Version: 1.0.0 | Priority: CRITIQUE #12
|
||||
|
||||
Sélecteur de date avec popup calendrier interactif.
|
||||
Styles Lions.dev avec icône calendrier, min/max date et formats personnalisés.
|
||||
|
||||
Usage:
|
||||
<fr:fieldCalendar id="birthdate"
|
||||
label="Date de naissance"
|
||||
value="#{bean.birthdate}"
|
||||
required="true"
|
||||
showIcon="true" />
|
||||
<fr:fieldCalendar id="birthdate" label="Date de naissance" value="#{bean.birthdate}" showIcon="true" />
|
||||
|
||||
Exemples:
|
||||
|
||||
1. Date de naissance avec icône:
|
||||
<fr:fieldCalendar id="birthdate"
|
||||
label="Date de naissance"
|
||||
value="#{bean.birthdate}"
|
||||
showIcon="true"
|
||||
yearRange="c-100:c+0"
|
||||
required="true" />
|
||||
|
||||
2. Date avec format personnalisé:
|
||||
<fr:fieldCalendar id="eventDate"
|
||||
label="Date de l'événement"
|
||||
value="#{bean.eventDate}"
|
||||
pattern="dd/MM/yyyy"
|
||||
showIcon="true"
|
||||
size="lg" />
|
||||
|
||||
3. Date avec min/max (période):
|
||||
<fr:fieldCalendar id="rendezvous"
|
||||
label="Date du rendez-vous"
|
||||
value="#{bean.rendezvous}"
|
||||
mindate="#{bean.today}"
|
||||
maxdate="#{bean.maxDate}"
|
||||
showIcon="true" />
|
||||
|
||||
4. Saisie manuelle sans icône:
|
||||
<fr:fieldCalendar id="dateManuelle"
|
||||
label="Date"
|
||||
value="#{bean.date}"
|
||||
showIcon="false"
|
||||
size="sm" />
|
||||
|
||||
Attributs disponibles:
|
||||
- label: Libellé du champ
|
||||
- value: Valeur liée au bean (java.util.Date ou LocalDate)
|
||||
- required: true/false - Champ obligatoire
|
||||
- disabled: true/false - Champ désactivé
|
||||
- readonly: true/false - Lecture seule
|
||||
- showIcon: true/false - Afficher icône calendrier (défaut: true)
|
||||
- pattern: Format de la date (dd/MM/yyyy, MM/dd/yyyy, etc.)
|
||||
- yearRange: Plage d'années ("2000:2030", "c-100:c+0" pour 100 ans avant aujourd'hui)
|
||||
- mindate: Date minimale acceptée (java.util.Date)
|
||||
- maxdate: Date maximale acceptée (java.util.Date)
|
||||
- size: sm | base (défaut) | lg - Taille du champ
|
||||
- styleClass: Classes CSS additionnelles
|
||||
|
||||
Events AJAX disponibles:
|
||||
- dateSelect: Déclenché quand une date est sélectionnée dans le calendrier
|
||||
- change: Déclenché au changement de valeur
|
||||
- viewChange: Déclenché au changement de mois/année
|
||||
- close: Déclenché à la fermeture du calendrier
|
||||
|
||||
Exemple avec AJAX:
|
||||
<fr:fieldCalendar id="date" label="Date" value="#{bean.date}">
|
||||
<p:ajax event="dateSelect" listener="#{bean.onDateSelect}" update="resultPanel" />
|
||||
</fr:fieldCalendar>
|
||||
|
||||
Notes:
|
||||
- Format par défaut selon la locale du navigateur
|
||||
- Navigation mois/année avec flèches
|
||||
- Support du clavier (flèches, Enter, Esc)
|
||||
- Popup responsive sur mobile
|
||||
-->
|
||||
|
||||
<composite:interface>
|
||||
@@ -29,15 +95,24 @@
|
||||
<composite:attribute name="yearRange" required="false" type="java.lang.String" />
|
||||
<composite:attribute name="mindate" required="false" />
|
||||
<composite:attribute name="maxdate" required="false" />
|
||||
<composite:attribute name="size" required="false" type="java.lang.String" default="base" />
|
||||
<composite:attribute name="styleClass" required="false" type="java.lang.String" />
|
||||
|
||||
<!-- Binding for calendar component -->
|
||||
<composite:editableValueHolder name="calendar" targets="calendarComponent" />
|
||||
|
||||
<!-- AJAX Behaviors Support -->
|
||||
<composite:clientBehavior name="dateSelect" event="dateSelect" targets="calendarComponent" />
|
||||
<composite:clientBehavior name="change" event="change" targets="calendarComponent" />
|
||||
<composite:clientBehavior name="blur" event="blur" targets="calendarComponent" />
|
||||
<composite:clientBehavior name="valueChange" event="valueChange" targets="calendarComponent" />
|
||||
<composite:clientBehavior name="viewChange" event="viewChange" targets="calendarComponent" />
|
||||
<composite:clientBehavior name="close" event="close" targets="calendarComponent" />
|
||||
</composite:interface>
|
||||
|
||||
<composite:implementation>
|
||||
<!-- Freya field pattern -->
|
||||
<div class="field #{cc.attrs.styleClass}">
|
||||
<div class="field #{not empty cc.attrs.size and cc.attrs.size ne 'base' ? 'field-'.concat(cc.attrs.size) : ''} #{cc.attrs.styleClass}">
|
||||
<!-- Label with required indicator -->
|
||||
<p:outputLabel for="calendarComponent"
|
||||
value="#{cc.attrs.label}"
|
||||
|
||||
@@ -4,48 +4,168 @@
|
||||
xmlns:composite="http://xmlns.jcp.org/jsf/composite"
|
||||
xmlns:h="http://xmlns.jcp.org/jsf/html"
|
||||
xmlns:p="http://primefaces.org/ui">
|
||||
|
||||
<!--
|
||||
Freya Field Checkbox - Pattern field + checkbox + label
|
||||
═══════════════════════════════════════════════════════════
|
||||
Lions.dev Field Checkbox - Case à cocher avec label
|
||||
═══════════════════════════════════════════════════════════
|
||||
Version: 1.0.0 | Priority: CRITIQUE #28
|
||||
|
||||
Case à cocher (checkbox) avec label intégré.
|
||||
Styles Lions.dev avec animations, gradient bleu et transitions fluides.
|
||||
|
||||
Usage:
|
||||
<fr:fieldCheckbox id="acceptTerms"
|
||||
label="J'accepte les conditions"
|
||||
value="#{bean.acceptTerms}"
|
||||
label="J'accepte les conditions générales"
|
||||
value="#{userBean.acceptTerms}"
|
||||
required="true" />
|
||||
|
||||
Exemples:
|
||||
|
||||
1. Checkbox simple:
|
||||
<fr:fieldCheckbox id="newsletter"
|
||||
label="S'abonner à la newsletter"
|
||||
value="#{userBean.newsletter}" />
|
||||
|
||||
2. Checkbox requis:
|
||||
<fr:fieldCheckbox id="terms"
|
||||
label="J'accepte les conditions d'utilisation"
|
||||
value="#{orderBean.termsAccepted}"
|
||||
required="true" />
|
||||
|
||||
3. Checkbox désactivé:
|
||||
<fr:fieldCheckbox id="premium"
|
||||
label="Compte premium (non disponible)"
|
||||
value="#{userBean.premium}"
|
||||
disabled="true" />
|
||||
|
||||
4. Checkbox avec taille personnalisée:
|
||||
<fr:fieldCheckbox id="remember"
|
||||
label="Se souvenir de moi"
|
||||
value="#{authBean.rememberMe}"
|
||||
size="lg" />
|
||||
|
||||
5. Checkbox avec AJAX:
|
||||
<fr:fieldCheckbox id="notifications"
|
||||
label="Activer les notifications"
|
||||
value="#{settingsBean.notificationsEnabled}">
|
||||
<p:ajax event="change"
|
||||
update="notifSettings"
|
||||
listener="#{settingsBean.onNotificationToggle}" />
|
||||
</fr:fieldCheckbox>
|
||||
|
||||
6. Checkbox avec texte d'aide:
|
||||
<fr:fieldCheckbox id="twoFactor"
|
||||
label="Authentification à deux facteurs"
|
||||
value="#{securityBean.twoFactorEnabled}"
|
||||
helpText="Ajoutez une couche de sécurité supplémentaire à votre compte" />
|
||||
|
||||
Attributs disponibles:
|
||||
- id: Identifiant du composant
|
||||
- label: Texte du label affiché à côté de la checkbox
|
||||
- value: Valeur booléenne bindée (true/false)
|
||||
- required: true/false - Champ obligatoire (défaut: false)
|
||||
- disabled: true/false - Désactiver la checkbox (défaut: false)
|
||||
- size: sm | base (défaut) | lg - Taille de la checkbox
|
||||
- helpText: Texte d'aide affiché sous la checkbox
|
||||
- styleClass: Classes CSS additionnelles
|
||||
- style: Styles CSS inline
|
||||
|
||||
AJAX Events:
|
||||
- change: Déclenché quand la valeur change
|
||||
- valueChange: Déclenché lors du changement de valeur (legacy)
|
||||
|
||||
Exemples de backing bean:
|
||||
```java
|
||||
@Named
|
||||
@ViewScoped
|
||||
public class UserBean {
|
||||
private boolean acceptTerms;
|
||||
private boolean newsletter;
|
||||
|
||||
// Getters et setters
|
||||
public boolean isAcceptTerms() {
|
||||
return acceptTerms;
|
||||
}
|
||||
|
||||
public void setAcceptTerms(boolean acceptTerms) {
|
||||
this.acceptTerms = acceptTerms;
|
||||
}
|
||||
|
||||
public boolean isNewsletter() {
|
||||
return newsletter;
|
||||
}
|
||||
|
||||
public void setNewsletter(boolean newsletter) {
|
||||
this.newsletter = newsletter;
|
||||
if (newsletter) {
|
||||
System.out.println("User subscribed to newsletter");
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Validation:
|
||||
```java
|
||||
// Checkbox requis
|
||||
@NotNull(message = "Vous devez accepter les conditions")
|
||||
private Boolean acceptTerms;
|
||||
```
|
||||
|
||||
Notes de style:
|
||||
- Checkbox avec gradient bleu Lions.dev quand coché
|
||||
- Animation de check icon au clic
|
||||
- Border bleu au hover
|
||||
- Focus ring pour accessibilité
|
||||
- Label cliquable (toggle la checkbox)
|
||||
- Taille personnalisable (sm, base, lg)
|
||||
- État disabled avec opacity réduite
|
||||
- Support des messages d'erreur sous la checkbox
|
||||
|
||||
Accessibilité:
|
||||
- Label associé correctement via for/id
|
||||
- Support clavier (Space pour toggle)
|
||||
- Focus ring visible
|
||||
- Attribut required pour lecteurs d'écran
|
||||
- Indicateur visuel obligatoire (*)
|
||||
-->
|
||||
|
||||
<composite:interface>
|
||||
<!-- Standard attributes -->
|
||||
<composite:attribute name="id" required="false" type="java.lang.String" />
|
||||
<composite:attribute name="label" required="false" type="java.lang.String" />
|
||||
<composite:attribute name="value" required="false" type="java.lang.Boolean" />
|
||||
<composite:attribute name="required" required="false" type="java.lang.Boolean" default="false" />
|
||||
<composite:attribute name="disabled" required="false" type="java.lang.Boolean" default="false" />
|
||||
<composite:attribute name="size" required="false" type="java.lang.String" default="base" />
|
||||
<composite:attribute name="helpText" required="false" type="java.lang.String" />
|
||||
<composite:attribute name="styleClass" required="false" type="java.lang.String" />
|
||||
<composite:attribute name="style" required="false" type="java.lang.String" />
|
||||
|
||||
<!-- Binding for checkbox component -->
|
||||
<composite:editableValueHolder name="checkbox" targets="checkboxComponent" />
|
||||
<composite:editableValueHolder name="checkbox" targets="checkbox" />
|
||||
|
||||
<composite:clientBehavior name="change" event="change" targets="checkbox" />
|
||||
<composite:clientBehavior name="valueChange" event="valueChange" targets="checkbox" />
|
||||
</composite:interface>
|
||||
|
||||
<composite:implementation>
|
||||
<!-- Freya field pattern for checkbox -->
|
||||
<div class="field-checkbox #{cc.attrs.styleClass}">
|
||||
<!-- Checkbox component -->
|
||||
<p:selectBooleanCheckbox id="checkboxComponent"
|
||||
<div class="field field-checkbox #{not empty cc.attrs.size and cc.attrs.size ne 'base' ? 'field-'.concat(cc.attrs.size) : ''} #{cc.attrs.styleClass}"
|
||||
style="#{cc.attrs.style}">
|
||||
|
||||
<p:selectBooleanCheckbox id="checkbox"
|
||||
value="#{cc.attrs.value}"
|
||||
required="#{cc.attrs.required}"
|
||||
disabled="#{cc.attrs.disabled}" />
|
||||
|
||||
<!-- Label -->
|
||||
<p:outputLabel for="@previous"
|
||||
<p:outputLabel for="checkbox"
|
||||
value="#{cc.attrs.label}"
|
||||
rendered="#{not empty cc.attrs.label}">
|
||||
<h:outputText value=" *" styleClass="p-error" rendered="#{cc.attrs.required}" />
|
||||
</p:outputLabel>
|
||||
|
||||
<!-- Validation message -->
|
||||
<p:message for="checkboxComponent" />
|
||||
<small class="field-help" rendered="#{not empty cc.attrs.helpText}">
|
||||
#{cc.attrs.helpText}
|
||||
</small>
|
||||
|
||||
<p:message for="checkbox" styleClass="field-error" />
|
||||
</div>
|
||||
</composite:implementation>
|
||||
|
||||
|
||||
@@ -6,14 +6,121 @@
|
||||
xmlns:p="http://primefaces.org/ui">
|
||||
|
||||
<!--
|
||||
Freya Field FileUpload - Pattern field + label + fileUpload + message
|
||||
═══════════════════════════════════════════════════════════
|
||||
Lions.dev Field FileUpload - Upload de fichiers avec drag & drop
|
||||
═══════════════════════════════════════════════════════════
|
||||
Version: 1.0.0 | Priority: CRITIQUE #15
|
||||
|
||||
Champ d'upload de fichiers avec drag & drop, validation et aperçu.
|
||||
Styles Lions.dev avec zone de dépôt stylisée, barre de progression et gestion multi-fichiers.
|
||||
|
||||
Usage:
|
||||
<fr:fieldFileUpload id="document"
|
||||
label="Document"
|
||||
listener="#{bean.handleUpload}"
|
||||
allowTypes="/(\.|\/)(pdf|doc|docx)$/"
|
||||
fileLimit="3" />
|
||||
<fr:fieldFileUpload id="document" label="Document" listener="#{bean.handleUpload}" allowTypes="/(\.|\/)(pdf|doc|docx)$/" />
|
||||
|
||||
Exemples:
|
||||
|
||||
1. Upload simple avec drag & drop:
|
||||
<fr:fieldFileUpload id="document"
|
||||
label="Document PDF"
|
||||
listener="#{bean.handleFileUpload}"
|
||||
allowTypes="/(\.|\/)(pdf)$/"
|
||||
sizeLimit="10485760"
|
||||
auto="false"
|
||||
dragDropSupport="true" />
|
||||
|
||||
2. Upload multiple de fichiers:
|
||||
<fr:fieldFileUpload id="photos"
|
||||
label="Photos (max 5)"
|
||||
listener="#{bean.handlePhotos}"
|
||||
multiple="true"
|
||||
fileLimit="5"
|
||||
allowTypes="/(\.|\/)(jpg|jpeg|png|gif)$/"
|
||||
sizeLimit="5242880"
|
||||
size="lg" />
|
||||
|
||||
3. Upload automatique (sans bouton):
|
||||
<fr:fieldFileUpload id="avatar"
|
||||
label="Photo de profil"
|
||||
listener="#{bean.handleAvatar}"
|
||||
auto="true"
|
||||
allowTypes="/(\.|\/)(jpg|jpeg|png)$/"
|
||||
sizeLimit="2097152"
|
||||
update="avatarPreview"
|
||||
size="sm" />
|
||||
|
||||
4. Upload avec validation stricte:
|
||||
<fr:fieldFileUpload id="fichierConfig"
|
||||
label="Fichier de configuration"
|
||||
listener="#{bean.handleConfig}"
|
||||
allowTypes="/(\.|\/)(xml|json|yml)$/"
|
||||
fileLimit="1"
|
||||
sizeLimit="1048576"
|
||||
required="true"
|
||||
dragDropSupport="false" />
|
||||
|
||||
Attributs disponibles:
|
||||
- label: Libellé du champ
|
||||
- listener: Méthode backing bean (void handleFileUpload(FileUploadEvent event))
|
||||
- required: true/false - Champ obligatoire
|
||||
- disabled: true/false - Champ désactivé
|
||||
- mode: Mode d'upload - "advanced" (défaut avec drag & drop) ou "simple"
|
||||
- multiple: true/false - Upload de plusieurs fichiers
|
||||
- auto: true/false - Upload automatique à la sélection (sans bouton "Envoyer")
|
||||
- allowTypes: Regex des types de fichiers acceptés ("/(\.|\/)(pdf|doc|docx)$/")
|
||||
- sizeLimit: Taille max par fichier en octets (ex: 10485760 = 10 MB)
|
||||
- fileLimit: Nombre max de fichiers (si multiple="true")
|
||||
- dragDropSupport: true/false - Activer drag & drop (défaut: true)
|
||||
- chooseLabel: Texte bouton "Choisir" (défaut: "Choisir")
|
||||
- uploadLabel: Texte bouton "Envoyer" (défaut: "Envoyer")
|
||||
- cancelLabel: Texte bouton "Annuler" (défaut: "Annuler")
|
||||
- chooseIcon: Icône bouton choisir (défaut: "pi pi-plus")
|
||||
- uploadIcon: Icône bouton envoyer (défaut: "pi pi-upload")
|
||||
- cancelIcon: Icône bouton annuler (défaut: "pi pi-times")
|
||||
- update: IDs des composants à mettre à jour après upload
|
||||
- size: sm | base (défaut) | lg - Taille du champ
|
||||
- styleClass: Classes CSS additionnelles
|
||||
|
||||
Méthode listener (backing bean):
|
||||
```java
|
||||
public void handleFileUpload(FileUploadEvent event) {
|
||||
UploadedFile file = event.getFile();
|
||||
String fileName = file.getFileName();
|
||||
long fileSize = file.getSize();
|
||||
String contentType = file.getContentType();
|
||||
|
||||
try (InputStream input = file.getInputStream()) {
|
||||
// Traiter le fichier...
|
||||
Files.copy(input, Paths.get("/uploads/" + fileName));
|
||||
|
||||
FacesContext.getCurrentInstance().addMessage(null,
|
||||
new FacesMessage("Succès", fileName + " téléchargé avec succès."));
|
||||
} catch (IOException e) {
|
||||
FacesContext.getCurrentInstance().addMessage(null,
|
||||
new FacesMessage(FacesMessage.SEVERITY_ERROR, "Erreur", "Échec du téléchargement."));
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Tailles de fichiers courantes:
|
||||
- 1 MB = 1048576 octets
|
||||
- 5 MB = 5242880 octets
|
||||
- 10 MB = 10485760 octets
|
||||
- 50 MB = 52428800 octets
|
||||
|
||||
Types MIME courants:
|
||||
- PDF: /(\.|\/)(pdf)$/
|
||||
- Images: /(\.|\/)(jpg|jpeg|png|gif|webp)$/
|
||||
- Documents: /(\.|\/)(pdf|doc|docx|xls|xlsx)$/
|
||||
- Archives: /(\.|\/)(zip|rar|7z|tar|gz)$/
|
||||
- Vidéos: /(\.|\/)(mp4|avi|mov|wmv)$/
|
||||
|
||||
Notes:
|
||||
- Mode "advanced": Interface complète avec drag & drop et barre de progression
|
||||
- Mode "simple": Bouton simple sans interface avancée
|
||||
- Validation automatique des types et tailles avant upload
|
||||
- Messages d'erreur automatiques si fichier invalide
|
||||
- Barre de progression affichée pendant l'upload
|
||||
- Support du drag & drop natif HTML5
|
||||
-->
|
||||
|
||||
<composite:interface>
|
||||
@@ -40,11 +147,12 @@
|
||||
<composite:attribute name="chooseIcon" required="false" type="java.lang.String" default="pi pi-plus" />
|
||||
<composite:attribute name="uploadIcon" required="false" type="java.lang.String" default="pi pi-upload" />
|
||||
<composite:attribute name="cancelIcon" required="false" type="java.lang.String" default="pi pi-times" />
|
||||
<composite:attribute name="size" required="false" type="java.lang.String" default="base" />
|
||||
</composite:interface>
|
||||
|
||||
<composite:implementation>
|
||||
<!-- Freya field pattern -->
|
||||
<div class="field #{cc.attrs.styleClass}">
|
||||
<div class="field #{not empty cc.attrs.size and cc.attrs.size ne 'base' ? 'field-'.concat(cc.attrs.size) : ''} #{cc.attrs.styleClass}">
|
||||
<!-- Label with required indicator -->
|
||||
<p:outputLabel for="uploadComponent"
|
||||
value="#{cc.attrs.label}"
|
||||
|
||||
@@ -6,14 +6,73 @@
|
||||
xmlns:p="http://primefaces.org/ui">
|
||||
|
||||
<!--
|
||||
Freya Field Input - Pattern field + label + input + message
|
||||
═══════════════════════════════════════════════════════════
|
||||
Lions.dev Field Input - Champ de saisie texte avec identité Lions.dev
|
||||
═══════════════════════════════════════════════════════════
|
||||
Version: 1.0.0
|
||||
Priority: CRITIQUE #5
|
||||
|
||||
Description:
|
||||
Champ de saisie texte (InputText) avec label, validation et messages d'erreur.
|
||||
Suit le pattern Field de PrimeFaces avec styles Lions.dev.
|
||||
Parfait pour nom, prénom, email, etc.
|
||||
|
||||
Fonctionnalités:
|
||||
- Styles Lions.dev élégants avec bordures et focus ring
|
||||
- Focus ring accessible (WCAG 2.1 AA)
|
||||
- Support des tailles (sm, base, lg)
|
||||
- États visuels (normal, focus, error, success, disabled)
|
||||
- Messages de validation intégrés
|
||||
- Label avec indicateur required (*)
|
||||
|
||||
Usage:
|
||||
<fr:fieldInput id="nom"
|
||||
label="Nom"
|
||||
value="#{bean.nom}"
|
||||
label="Nom complet"
|
||||
value="#{bean.user.nom}"
|
||||
required="true"
|
||||
placeholder="Entrez votre nom" />
|
||||
placeholder="Ex: Jean Dupont"
|
||||
size="base" />
|
||||
|
||||
Exemples:
|
||||
|
||||
1. Champ simple requis:
|
||||
<fr:fieldInput label="Email" value="#{bean.email}" required="true"
|
||||
placeholder="email@example.com" />
|
||||
|
||||
2. Champ large avec max length:
|
||||
<fr:fieldInput label="Titre" value="#{bean.title}" size="lg"
|
||||
maxlength="100" placeholder="Titre du document" />
|
||||
|
||||
3. Champ petit en lecture seule:
|
||||
<fr:fieldInput label="ID" value="#{bean.id}" size="sm" readonly="true" />
|
||||
|
||||
4. Champ désactivé:
|
||||
<fr:fieldInput label="Statut" value="#{bean.status}" disabled="true" />
|
||||
|
||||
5. Avec validation Ajax:
|
||||
<fr:fieldInput label="Nom d'utilisateur" value="#{bean.username}" required="true">
|
||||
<p:ajax event="blur" update="@this" listener="#{bean.validateUsername}" />
|
||||
</fr:fieldInput>
|
||||
|
||||
Tailles disponibles:
|
||||
- sm (32px height) - Pour champs compacts
|
||||
- base (44px height - défaut) - Taille standard
|
||||
- lg (52px height) - Pour champs importants
|
||||
|
||||
Attributs disponibles:
|
||||
- id: Identifiant du champ
|
||||
- label: Label du champ (affiché au-dessus)
|
||||
- value: Valeur bindée au bean
|
||||
- required: true/false - Affiche * rouge si required
|
||||
- placeholder: Texte d'aide dans le champ
|
||||
- disabled: true/false - Champ désactivé
|
||||
- readonly: true/false - Champ en lecture seule
|
||||
- maxlength: Nombre max de caractères
|
||||
- size: sm|base|lg - Taille du champ
|
||||
- styleClass: Classes CSS additionnelles
|
||||
|
||||
Events Ajax supportés:
|
||||
- keyup, keydown, blur, focus, change, valueChange
|
||||
-->
|
||||
|
||||
<composite:interface>
|
||||
@@ -26,15 +85,24 @@
|
||||
<composite:attribute name="disabled" required="false" type="java.lang.Boolean" default="false" />
|
||||
<composite:attribute name="readonly" required="false" type="java.lang.Boolean" default="false" />
|
||||
<composite:attribute name="maxlength" required="false" type="java.lang.Integer" />
|
||||
<composite:attribute name="size" required="false" type="java.lang.String" default="base" />
|
||||
<composite:attribute name="styleClass" required="false" type="java.lang.String" />
|
||||
|
||||
<!-- Binding for input component -->
|
||||
<composite:editableValueHolder name="input" targets="inputComponent" />
|
||||
|
||||
<!-- AJAX Behaviors Support -->
|
||||
<composite:clientBehavior name="keyup" event="keyup" targets="inputComponent" />
|
||||
<composite:clientBehavior name="keydown" event="keydown" targets="inputComponent" />
|
||||
<composite:clientBehavior name="blur" event="blur" targets="inputComponent" />
|
||||
<composite:clientBehavior name="focus" event="focus" targets="inputComponent" />
|
||||
<composite:clientBehavior name="change" event="change" targets="inputComponent" />
|
||||
<composite:clientBehavior name="valueChange" event="valueChange" targets="inputComponent" />
|
||||
</composite:interface>
|
||||
|
||||
<composite:implementation>
|
||||
<!-- Freya field pattern -->
|
||||
<div class="field #{cc.attrs.styleClass}">
|
||||
<!-- Lions.dev field pattern -->
|
||||
<div class="field #{not empty cc.attrs.size and cc.attrs.size ne 'base' ? 'field-'.concat(cc.attrs.size) : ''} #{cc.attrs.styleClass}">
|
||||
<!-- Label with required indicator -->
|
||||
<p:outputLabel for="inputComponent"
|
||||
value="#{cc.attrs.label}"
|
||||
|
||||
@@ -7,15 +7,98 @@
|
||||
xmlns:f="http://xmlns.jcp.org/jsf/core">
|
||||
|
||||
<!--
|
||||
Freya Field MultiSelect - Pattern field + label + selectCheckboxMenu + message
|
||||
═══════════════════════════════════════════════════════════
|
||||
Lions.dev Field MultiSelect - Sélection multiple avec checkboxes
|
||||
═══════════════════════════════════════════════════════════
|
||||
Version: 1.0.0 | Priority: CRITIQUE #14
|
||||
|
||||
Champ de sélection multiple avec checkboxes dans un menu déroulant.
|
||||
Styles Lions.dev avec filtre de recherche, "Tout sélectionner" et compteur.
|
||||
|
||||
Usage:
|
||||
<fr:fieldMultiSelect id="roles"
|
||||
label="Rôles"
|
||||
value="#{bean.selectedRoles}"
|
||||
filter="true">
|
||||
<fr:fieldMultiSelect id="roles" label="Rôles" value="#{bean.selectedRoles}" filter="true">
|
||||
<f:selectItems value="#{bean.availableRoles}" />
|
||||
</fr:fieldMultiSelect>
|
||||
|
||||
Exemples:
|
||||
|
||||
1. MultiSelect simple avec filtre:
|
||||
<fr:fieldMultiSelect id="roles"
|
||||
label="Rôles utilisateur"
|
||||
value="#{bean.selectedRoles}"
|
||||
filter="true"
|
||||
required="true">
|
||||
<f:selectItems value="#{bean.availableRoles}" var="role"
|
||||
itemLabel="#{role.nom}" itemValue="#{role.id}" />
|
||||
</fr:fieldMultiSelect>
|
||||
|
||||
2. MultiSelect avec objets complexes:
|
||||
<fr:fieldMultiSelect id="departements"
|
||||
label="Départements"
|
||||
value="#{bean.selectedDepts}"
|
||||
filter="true"
|
||||
filterMatchMode="contains">
|
||||
<f:selectItems value="#{bean.allDepartements}" var="dept"
|
||||
itemLabel="#{dept.code} - #{dept.nom}" itemValue="#{dept}" />
|
||||
</fr:fieldMultiSelect>
|
||||
|
||||
3. MultiSelect compact:
|
||||
<fr:fieldMultiSelect id="tags"
|
||||
label="Tags"
|
||||
value="#{bean.selectedTags}"
|
||||
filter="true"
|
||||
size="sm">
|
||||
<f:selectItem itemLabel="Java" itemValue="java" />
|
||||
<f:selectItem itemLabel="JavaScript" itemValue="js" />
|
||||
<f:selectItem itemLabel="Python" itemValue="python" />
|
||||
<f:selectItem itemLabel="C#" itemValue="csharp" />
|
||||
</fr:fieldMultiSelect>
|
||||
|
||||
4. MultiSelect large avec groupes:
|
||||
<fr:fieldMultiSelect id="permissions"
|
||||
label="Permissions"
|
||||
value="#{bean.selectedPermissions}"
|
||||
filter="true"
|
||||
size="lg">
|
||||
<f:selectItems value="#{bean.allPermissions}" var="perm"
|
||||
itemLabel="#{perm.libelle}" itemValue="#{perm.code}" />
|
||||
</fr:fieldMultiSelect>
|
||||
|
||||
Attributs disponibles:
|
||||
- label: Libellé du champ
|
||||
- value: Collection des valeurs sélectionnées (List, Set, etc.)
|
||||
- required: true/false - Champ obligatoire
|
||||
- disabled: true/false - Champ désactivé
|
||||
- filter: true/false - Activer la recherche (défaut: false)
|
||||
- filterMatchMode: Mode de recherche - "contains", "startsWith", "endsWith" (défaut: contains)
|
||||
- multiple: true/false - Toujours true pour ce composant (défaut: true)
|
||||
- size: sm | base (défaut) | lg - Taille du champ
|
||||
- styleClass: Classes CSS additionnelles
|
||||
|
||||
Contenu (f:selectItems ou f:selectItem):
|
||||
- Utiliser <f:selectItems> pour une collection dynamique
|
||||
- Utiliser <f:selectItem> pour des valeurs fixes
|
||||
|
||||
Exemples de f:selectItems:
|
||||
```xhtml
|
||||
<!-- Liste simple de strings -->
|
||||
<f:selectItems value="#{bean.options}" />
|
||||
|
||||
<!-- Liste d'objets avec var -->
|
||||
<f:selectItems value="#{bean.users}" var="user"
|
||||
itemLabel="#{user.nom}" itemValue="#{user.id}" />
|
||||
|
||||
<!-- Map -->
|
||||
<f:selectItems value="#{bean.optionsMap}" />
|
||||
```
|
||||
|
||||
Notes:
|
||||
- Affiche un compteur "X sur Y sélectionnés"
|
||||
- Case "Tout sélectionner / Tout désélectionner" en header
|
||||
- Filtre de recherche en temps réel (si filter="true")
|
||||
- Scroll automatique si plus de 10 éléments
|
||||
- Support du clavier (flèches, Espace, Enter, Esc)
|
||||
- Affichage responsive sur mobile
|
||||
-->
|
||||
|
||||
<composite:interface>
|
||||
@@ -28,6 +111,7 @@
|
||||
<composite:attribute name="filter" required="false" type="java.lang.Boolean" default="false" />
|
||||
<composite:attribute name="filterMatchMode" required="false" type="java.lang.String" default="contains" />
|
||||
<composite:attribute name="multiple" required="false" type="java.lang.Boolean" default="true" />
|
||||
<composite:attribute name="size" required="false" type="java.lang.String" default="base" />
|
||||
<composite:attribute name="styleClass" required="false" type="java.lang.String" />
|
||||
|
||||
<!-- Binding for select component -->
|
||||
@@ -39,7 +123,7 @@
|
||||
|
||||
<composite:implementation>
|
||||
<!-- Freya field pattern -->
|
||||
<div class="field #{cc.attrs.styleClass}">
|
||||
<div class="field #{not empty cc.attrs.size and cc.attrs.size ne 'base' ? 'field-'.concat(cc.attrs.size) : ''} #{cc.attrs.styleClass}">
|
||||
<!-- Label with required indicator -->
|
||||
<p:outputLabel for="selectComponent"
|
||||
value="#{cc.attrs.label}"
|
||||
|
||||
@@ -6,22 +6,79 @@
|
||||
xmlns:p="http://primefaces.org/ui">
|
||||
|
||||
<!--
|
||||
Freya Field Number - Pattern field + label + inputNumber + message
|
||||
═══════════════════════════════════════════════════════════
|
||||
Lions.dev Field Number - Champ numérique avec spinners
|
||||
═══════════════════════════════════════════════════════════
|
||||
Version: 1.0.0 | Priority: CRITIQUE #11
|
||||
|
||||
Champ numérique avec boutons +/- (spinners), formatage et validation.
|
||||
Styles Lions.dev avec symboles monétaires, min/max et précision décimale.
|
||||
|
||||
Usage:
|
||||
<fr:fieldNumber id="prix"
|
||||
label="Prix"
|
||||
value="#{bean.prix}"
|
||||
symbol=" FCFA"
|
||||
symbolPosition="s"
|
||||
decimalPlaces="0" />
|
||||
<fr:fieldNumber id="prix" label="Prix" value="#{bean.prix}" symbol=" FCFA" symbolPosition="s" decimalPlaces="0" />
|
||||
|
||||
With min/max:
|
||||
<fr:fieldNumber id="pourcentage"
|
||||
label="Pourcentage"
|
||||
value="#{bean.pourcentage}"
|
||||
minValue="0"
|
||||
maxValue="100" />
|
||||
Exemples:
|
||||
|
||||
1. Champ prix avec symbole monétaire:
|
||||
<fr:fieldNumber id="prix"
|
||||
label="Prix unitaire"
|
||||
value="#{bean.prix}"
|
||||
symbol=" FCFA"
|
||||
symbolPosition="s"
|
||||
decimalPlaces="0"
|
||||
minValue="0" />
|
||||
|
||||
2. Pourcentage avec min/max:
|
||||
<fr:fieldNumber id="pourcentage"
|
||||
label="Pourcentage de remise"
|
||||
value="#{bean.pourcentage}"
|
||||
minValue="0"
|
||||
maxValue="100"
|
||||
symbol="%"
|
||||
symbolPosition="s" />
|
||||
|
||||
3. Champ décimal (2 chiffres):
|
||||
<fr:fieldNumber id="montant"
|
||||
label="Montant"
|
||||
value="#{bean.montant}"
|
||||
decimalPlaces="2"
|
||||
symbol=" €"
|
||||
symbolPosition="s"
|
||||
size="lg" />
|
||||
|
||||
4. Quantité simple:
|
||||
<fr:fieldNumber id="quantite"
|
||||
label="Quantité"
|
||||
value="#{bean.quantite}"
|
||||
decimalPlaces="0"
|
||||
minValue="1"
|
||||
required="true"
|
||||
size="sm" />
|
||||
|
||||
Attributs disponibles:
|
||||
- label: Libellé du champ
|
||||
- value: Valeur liée au bean
|
||||
- required: true/false - Champ obligatoire
|
||||
- disabled: true/false - Champ désactivé
|
||||
- readonly: true/false - Lecture seule
|
||||
- placeholder: Texte d'aide
|
||||
- symbol: Symbole monétaire/unité (€, $, FCFA, %, etc.)
|
||||
- symbolPosition: Position du symbole - "p" (prefix) ou "s" (suffix)
|
||||
- decimalPlaces: Nombre de décimales (0, 2, 3, etc.)
|
||||
- minValue: Valeur minimale acceptée
|
||||
- maxValue: Valeur maximale acceptée
|
||||
- size: sm | base (défaut) | lg - Taille du champ
|
||||
- styleClass: Classes CSS additionnelles
|
||||
|
||||
Position du symbole:
|
||||
- "p" (prefix): Avant le nombre (€ 1000)
|
||||
- "s" (suffix): Après le nombre (1000 FCFA)
|
||||
|
||||
Notes:
|
||||
- Les spinners (+/-) apparaissent automatiquement
|
||||
- Validation automatique min/max avec message d'erreur
|
||||
- Support du clavier (flèches haut/bas)
|
||||
- Formatage automatique selon locale
|
||||
-->
|
||||
|
||||
<composite:interface>
|
||||
@@ -46,6 +103,7 @@
|
||||
-->
|
||||
<composite:attribute name="minValue" required="false" type="java.lang.String" default="-999999999999" />
|
||||
<composite:attribute name="maxValue" required="false" type="java.lang.String" default="999999999999" />
|
||||
<composite:attribute name="size" required="false" type="java.lang.String" default="base" />
|
||||
|
||||
<!-- Binding for input component -->
|
||||
<composite:editableValueHolder name="input" targets="inputComponent" />
|
||||
@@ -53,7 +111,7 @@
|
||||
|
||||
<composite:implementation>
|
||||
<!-- Freya field pattern -->
|
||||
<div class="field #{cc.attrs.styleClass}">
|
||||
<div class="field #{not empty cc.attrs.size and cc.attrs.size ne 'base' ? 'field-'.concat(cc.attrs.size) : ''} #{cc.attrs.styleClass}">
|
||||
<!-- Label with required indicator -->
|
||||
<p:outputLabel for="inputComponent"
|
||||
value="#{cc.attrs.label}"
|
||||
|
||||
@@ -6,14 +6,19 @@
|
||||
xmlns:p="http://primefaces.org/ui">
|
||||
|
||||
<!--
|
||||
Freya Field Password - Pattern field + label + password + message
|
||||
═══════════════════════════════════════════════════════════
|
||||
Lions.dev Field Password - Champ mot de passe Lions.dev
|
||||
═══════════════════════════════════════════════════════════
|
||||
Version: 1.0.0 | Priority: CRITIQUE #8
|
||||
|
||||
Champ mot de passe avec indicateur de force, toggle mask et validation.
|
||||
Styles Lions.dev avec barre de force colorée (weak/medium/strong).
|
||||
|
||||
Usage:
|
||||
<fr:fieldPassword id="password"
|
||||
label="Mot de passe"
|
||||
value="#{bean.password}"
|
||||
required="true"
|
||||
feedback="true" />
|
||||
<fr:fieldPassword id="pwd" label="Mot de passe" value="#{bean.pwd}" feedback="true" toggleMask="true" />
|
||||
|
||||
Tailles: sm | base (défaut) | lg
|
||||
Attributs: feedback (true/false), toggleMask (true/false), maxlength, disabled, size
|
||||
-->
|
||||
|
||||
<composite:interface>
|
||||
@@ -28,15 +33,24 @@
|
||||
<composite:attribute name="feedback" required="false" type="java.lang.Boolean" default="true" />
|
||||
<composite:attribute name="toggleMask" required="false" type="java.lang.Boolean" default="true" />
|
||||
<composite:attribute name="maxlength" required="false" type="java.lang.Integer" />
|
||||
<composite:attribute name="size" required="false" type="java.lang.String" default="base" />
|
||||
<composite:attribute name="styleClass" required="false" type="java.lang.String" />
|
||||
|
||||
<!-- Binding for password component -->
|
||||
<composite:editableValueHolder name="password" targets="passwordComponent" />
|
||||
|
||||
<!-- AJAX Behaviors Support -->
|
||||
<composite:clientBehavior name="keyup" event="keyup" targets="passwordComponent" />
|
||||
<composite:clientBehavior name="keydown" event="keydown" targets="passwordComponent" />
|
||||
<composite:clientBehavior name="blur" event="blur" targets="passwordComponent" />
|
||||
<composite:clientBehavior name="focus" event="focus" targets="passwordComponent" />
|
||||
<composite:clientBehavior name="change" event="change" targets="passwordComponent" />
|
||||
<composite:clientBehavior name="valueChange" event="valueChange" targets="passwordComponent" />
|
||||
</composite:interface>
|
||||
|
||||
<composite:implementation>
|
||||
<!-- Freya field pattern -->
|
||||
<div class="field #{cc.attrs.styleClass}">
|
||||
<div class="field #{not empty cc.attrs.size and cc.attrs.size ne 'base' ? 'field-'.concat(cc.attrs.size) : ''} #{cc.attrs.styleClass}">
|
||||
<!-- Label with required indicator -->
|
||||
<p:outputLabel for="passwordComponent"
|
||||
value="#{cc.attrs.label}"
|
||||
|
||||
@@ -5,58 +5,215 @@
|
||||
xmlns:h="http://xmlns.jcp.org/jsf/html"
|
||||
xmlns:p="http://primefaces.org/ui"
|
||||
xmlns:f="http://xmlns.jcp.org/jsf/core">
|
||||
|
||||
<!--
|
||||
Freya Field Radio - Pattern field + label + selectOneRadio + message
|
||||
═══════════════════════════════════════════════════════════
|
||||
Lions.dev Field Radio - Boutons radio avec label
|
||||
═══════════════════════════════════════════════════════════
|
||||
Version: 1.0.0 | Priority: CRITIQUE #29
|
||||
|
||||
Groupe de boutons radio (choix unique) avec label intégré.
|
||||
Styles Lions.dev avec animations, gradient bleu et transitions fluides.
|
||||
|
||||
Usage:
|
||||
<fr:fieldRadio id="civilite"
|
||||
label="Civilité"
|
||||
value="#{bean.civilite}">
|
||||
<f:selectItem itemLabel="M." itemValue="M" />
|
||||
<f:selectItem itemLabel="Mme" itemValue="MME" />
|
||||
<fr:fieldRadio id="gender"
|
||||
label="Genre"
|
||||
value="#{userBean.gender}">
|
||||
<f:selectItem itemLabel="Homme" itemValue="M" />
|
||||
<f:selectItem itemLabel="Femme" itemValue="F" />
|
||||
</fr:fieldRadio>
|
||||
|
||||
Exemples:
|
||||
|
||||
1. Radio basique:
|
||||
<fr:fieldRadio id="civilite"
|
||||
label="Civilité"
|
||||
value="#{userBean.civilite}">
|
||||
<f:selectItem itemLabel="M." itemValue="M" />
|
||||
<f:selectItem itemLabel="Mme" itemValue="MME" />
|
||||
<f:selectItem itemLabel="Mlle" itemValue="MLLE" />
|
||||
</fr:fieldRadio>
|
||||
|
||||
2. Radio avec layout horizontal (par défaut):
|
||||
<fr:fieldRadio id="satisfaction"
|
||||
label="Niveau de satisfaction"
|
||||
value="#{surveyBean.satisfaction}"
|
||||
layout="lineDirection">
|
||||
<f:selectItem itemLabel="Très satisfait" itemValue="5" />
|
||||
<f:selectItem itemLabel="Satisfait" itemValue="4" />
|
||||
<f:selectItem itemLabel="Neutre" itemValue="3" />
|
||||
<f:selectItem itemLabel="Insatisfait" itemValue="2" />
|
||||
<f:selectItem itemLabel="Très insatisfait" itemValue="1" />
|
||||
</fr:fieldRadio>
|
||||
|
||||
3. Radio avec layout vertical:
|
||||
<fr:fieldRadio id="plan"
|
||||
label="Choisissez votre plan"
|
||||
value="#{subscriptionBean.plan}"
|
||||
layout="pageDirection">
|
||||
<f:selectItem itemLabel="Gratuit" itemValue="FREE" />
|
||||
<f:selectItem itemLabel="Standard (9.99€/mois)" itemValue="STANDARD" />
|
||||
<f:selectItem itemLabel="Premium (19.99€/mois)" itemValue="PREMIUM" />
|
||||
<f:selectItem itemLabel="Entreprise (contactez-nous)" itemValue="ENTERPRISE" />
|
||||
</fr:fieldRadio>
|
||||
|
||||
4. Radio requis:
|
||||
<fr:fieldRadio id="acceptPolicy"
|
||||
label="Acceptez-vous notre politique de confidentialité ?"
|
||||
value="#{userBean.policyAccepted}"
|
||||
required="true">
|
||||
<f:selectItem itemLabel="Oui, j'accepte" itemValue="true" />
|
||||
<f:selectItem itemLabel="Non, je refuse" itemValue="false" />
|
||||
</fr:fieldRadio>
|
||||
|
||||
5. Radio avec AJAX:
|
||||
<fr:fieldRadio id="paymentMethod"
|
||||
label="Mode de paiement"
|
||||
value="#{orderBean.paymentMethod}">
|
||||
<f:selectItem itemLabel="Carte bancaire" itemValue="CARD" />
|
||||
<f:selectItem itemLabel="PayPal" itemValue="PAYPAL" />
|
||||
<f:selectItem itemLabel="Virement" itemValue="TRANSFER" />
|
||||
<p:ajax event="change"
|
||||
update="paymentDetails"
|
||||
listener="#{orderBean.onPaymentMethodChange}" />
|
||||
</fr:fieldRadio>
|
||||
|
||||
6. Radio avec texte d'aide:
|
||||
<fr:fieldRadio id="deliveryMode"
|
||||
label="Mode de livraison"
|
||||
value="#{orderBean.deliveryMode}"
|
||||
helpText="Choisissez le mode de livraison qui vous convient">
|
||||
<f:selectItem itemLabel="Standard (3-5 jours)" itemValue="STANDARD" />
|
||||
<f:selectItem itemLabel="Express (24h)" itemValue="EXPRESS" />
|
||||
<f:selectItem itemLabel="Retrait en magasin" itemValue="PICKUP" />
|
||||
</fr:fieldRadio>
|
||||
|
||||
Attributs disponibles:
|
||||
- id: Identifiant du composant
|
||||
- label: Label affiché au-dessus du groupe de radio
|
||||
- value: Valeur bindée (objet sélectionné)
|
||||
- required: true/false - Champ obligatoire (défaut: false)
|
||||
- disabled: true/false - Désactiver tous les radios (défaut: false)
|
||||
- layout: "lineDirection" (horizontal) | "pageDirection" (vertical) - Défaut: lineDirection
|
||||
- size: sm | base (défaut) | lg - Taille des boutons radio
|
||||
- helpText: Texte d'aide affiché sous le groupe
|
||||
- styleClass: Classes CSS additionnelles
|
||||
- style: Styles CSS inline
|
||||
|
||||
Insertion de choix:
|
||||
Utilisez <f:selectItem> ou <f:selectItems> comme enfants:
|
||||
- <f:selectItem itemLabel="Label" itemValue="value" />
|
||||
- <f:selectItems value="#{bean.options}" />
|
||||
|
||||
AJAX Events:
|
||||
- change: Déclenché quand la sélection change
|
||||
- valueChange: Déclenché lors du changement de valeur
|
||||
|
||||
Exemples de backing bean:
|
||||
```java
|
||||
@Named
|
||||
@ViewScoped
|
||||
public class UserBean {
|
||||
private String civilite;
|
||||
private String gender;
|
||||
private List<SelectItem> civiliteOptions;
|
||||
|
||||
@PostConstruct
|
||||
public void init() {
|
||||
civiliteOptions = new ArrayList<>();
|
||||
civiliteOptions.add(new SelectItem("M", "M."));
|
||||
civiliteOptions.add(new SelectItem("MME", "Mme"));
|
||||
civiliteOptions.add(new SelectItem("MLLE", "Mlle"));
|
||||
}
|
||||
|
||||
// Getters et setters
|
||||
public String getCivilite() {
|
||||
return civilite;
|
||||
}
|
||||
|
||||
public void setCivilite(String civilite) {
|
||||
this.civilite = civilite;
|
||||
}
|
||||
|
||||
public List<SelectItem> getCiviliteOptions() {
|
||||
return civiliteOptions;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Validation:
|
||||
```java
|
||||
@NotNull(message = "Veuillez sélectionner une option")
|
||||
private String civilite;
|
||||
```
|
||||
|
||||
Layout options:
|
||||
- lineDirection: Boutons disposés horizontalement (par défaut)
|
||||
- pageDirection: Boutons disposés verticalement
|
||||
- grid: Grille responsive (2 colonnes sur mobile, 3+ sur desktop)
|
||||
- custom: Layout personnalisé avec CSS
|
||||
|
||||
Notes de style:
|
||||
- Radio avec gradient bleu Lions.dev quand sélectionné
|
||||
- Animation du point central au clic
|
||||
- Border bleu au hover
|
||||
- Focus ring pour accessibilité
|
||||
- Label cliquable
|
||||
- Taille personnalisable (sm, base, lg)
|
||||
- État disabled avec opacity réduite
|
||||
- Support des messages d'erreur
|
||||
|
||||
Accessibilité:
|
||||
- Labels associés correctement
|
||||
- Support clavier (flèches pour naviguer, Space/Enter pour sélectionner)
|
||||
- Focus ring visible
|
||||
- Attribut required pour lecteurs d'écran
|
||||
- Indicateur visuel obligatoire (*)
|
||||
- ARIA roles automatiques
|
||||
-->
|
||||
|
||||
<composite:interface>
|
||||
<!-- Standard attributes -->
|
||||
<composite:attribute name="id" required="false" type="java.lang.String" />
|
||||
<composite:attribute name="label" required="false" type="java.lang.String" />
|
||||
<composite:attribute name="value" required="false" />
|
||||
<composite:attribute name="required" required="false" type="java.lang.Boolean" default="false" />
|
||||
<composite:attribute name="disabled" required="false" type="java.lang.Boolean" default="false" />
|
||||
<composite:attribute name="layout" required="false" type="java.lang.String" default="lineDirection" />
|
||||
<composite:attribute name="size" required="false" type="java.lang.String" default="base" />
|
||||
<composite:attribute name="helpText" required="false" type="java.lang.String" />
|
||||
<composite:attribute name="styleClass" required="false" type="java.lang.String" />
|
||||
<composite:attribute name="style" required="false" type="java.lang.String" />
|
||||
|
||||
<!-- Binding for select component -->
|
||||
<composite:editableValueHolder name="select" targets="selectComponent" />
|
||||
<composite:editableValueHolder name="select" targets="select" />
|
||||
|
||||
<composite:clientBehavior name="change" event="change" targets="select" />
|
||||
<composite:clientBehavior name="valueChange" event="valueChange" targets="select" />
|
||||
|
||||
<!-- Insertion point for select items -->
|
||||
<composite:insertChildren />
|
||||
</composite:interface>
|
||||
|
||||
<composite:implementation>
|
||||
<!-- Freya field pattern -->
|
||||
<div class="field #{cc.attrs.styleClass}">
|
||||
<!-- Label with required indicator -->
|
||||
<p:outputLabel for="selectComponent"
|
||||
<div class="field #{not empty cc.attrs.size and cc.attrs.size ne 'base' ? 'field-'.concat(cc.attrs.size) : ''} #{cc.attrs.styleClass}"
|
||||
style="#{cc.attrs.style}">
|
||||
|
||||
<p:outputLabel for="select"
|
||||
value="#{cc.attrs.label}"
|
||||
rendered="#{not empty cc.attrs.label}">
|
||||
<h:outputText value=" *" styleClass="p-error" rendered="#{cc.attrs.required}" />
|
||||
</p:outputLabel>
|
||||
|
||||
<!-- SelectOneRadio component -->
|
||||
<p:selectOneRadio id="selectComponent"
|
||||
<p:selectOneRadio id="select"
|
||||
value="#{cc.attrs.value}"
|
||||
required="#{cc.attrs.required}"
|
||||
disabled="#{cc.attrs.disabled}"
|
||||
layout="#{cc.attrs.layout}">
|
||||
<!-- Insert child f:selectItem(s) -->
|
||||
<composite:insertChildren />
|
||||
</p:selectOneRadio>
|
||||
|
||||
<!-- Validation message -->
|
||||
<p:message for="selectComponent" />
|
||||
<small class="field-help" rendered="#{not empty cc.attrs.helpText}">
|
||||
#{cc.attrs.helpText}
|
||||
</small>
|
||||
|
||||
<p:message for="select" styleClass="field-error" />
|
||||
</div>
|
||||
</composite:implementation>
|
||||
|
||||
|
||||
@@ -4,53 +4,224 @@
|
||||
xmlns:composite="http://xmlns.jcp.org/jsf/composite"
|
||||
xmlns:h="http://xmlns.jcp.org/jsf/html"
|
||||
xmlns:p="http://primefaces.org/ui">
|
||||
|
||||
<!--
|
||||
Freya Field Rating - Pattern field + rating + label
|
||||
═══════════════════════════════════════════════════════════
|
||||
Lions.dev Field Rating - Notation par étoiles
|
||||
═══════════════════════════════════════════════════════════
|
||||
Version: 1.0.0 | Priority: CRITIQUE #33
|
||||
|
||||
Système de notation par étoiles (ou icônes personnalisées) avec label.
|
||||
Styles Lions.dev avec étoiles gold, animations hover et transitions élégantes.
|
||||
|
||||
Usage:
|
||||
<fr:fieldRating id="note"
|
||||
label="Note"
|
||||
value="#{bean.note}" />
|
||||
label="Votre note"
|
||||
value="#{reviewBean.rating}"
|
||||
stars="5" />
|
||||
|
||||
Exemples:
|
||||
|
||||
1. Rating basique (5 étoiles):
|
||||
<fr:fieldRating id="productRating"
|
||||
label="Notez ce produit"
|
||||
value="#{productBean.rating}"
|
||||
stars="5" />
|
||||
|
||||
2. Rating 10 étoiles:
|
||||
<fr:fieldRating id="movieRating"
|
||||
label="Note du film"
|
||||
value="#{movieBean.rating}"
|
||||
stars="10" />
|
||||
|
||||
3. Rating sans bouton cancel:
|
||||
<fr:fieldRating id="satisfaction"
|
||||
label="Niveau de satisfaction"
|
||||
value="#{surveyBean.satisfaction}"
|
||||
cancel="false" />
|
||||
|
||||
4. Rating en lecture seule:
|
||||
<fr:fieldRating id="avgRating"
|
||||
label="Note moyenne"
|
||||
value="#{productBean.avgRating}"
|
||||
readonly="true"
|
||||
stars="5" />
|
||||
|
||||
5. Rating avec AJAX:
|
||||
<fr:fieldRating id="userRating"
|
||||
label="Votre évaluation"
|
||||
value="#{reviewBean.userRating}">
|
||||
<p:ajax event="rate"
|
||||
update="ratingPanel"
|
||||
listener="#{reviewBean.onRate}" />
|
||||
</fr:fieldRating>
|
||||
|
||||
6. Rating avec taille personnalisée:
|
||||
<fr:fieldRating id="qualityRating"
|
||||
label="Qualité du service"
|
||||
value="#{feedbackBean.quality}"
|
||||
size="lg"
|
||||
helpText="Cliquez sur les étoiles pour noter" />
|
||||
|
||||
7. Rating requis:
|
||||
<fr:fieldRating id="mandatoryRating"
|
||||
label="Évaluation obligatoire"
|
||||
value="#{formBean.rating}"
|
||||
required="true"
|
||||
cancel="false" />
|
||||
|
||||
8. Rating avec hearts (coeurs):
|
||||
<fr:fieldRating id="favoriteLevel"
|
||||
label="Niveau de favori"
|
||||
value="#{userBean.favoriteLevel}"
|
||||
variant="hearts"
|
||||
stars="5" />
|
||||
|
||||
Attributs disponibles:
|
||||
- id: Identifiant du composant
|
||||
- label: Label affiché au-dessus du rating
|
||||
- value: Valeur numérique bindée (Integer, 0-stars)
|
||||
- stars: Nombre d'étoiles affichées (défaut: 5)
|
||||
- cancel: true/false - Afficher bouton annuler (défaut: true)
|
||||
- readonly: true/false - Mode lecture seule (défaut: false)
|
||||
- disabled: true/false - Désactiver le rating (défaut: false)
|
||||
- size: sm | base (défaut) | lg - Taille des étoiles
|
||||
- variant: stars (défaut) | hearts - Type d'icône
|
||||
- required: true/false - Champ obligatoire (défaut: false)
|
||||
- helpText: Texte d'aide affiché sous le rating
|
||||
- styleClass: Classes CSS additionnelles
|
||||
- style: Styles CSS inline
|
||||
|
||||
AJAX Events:
|
||||
- rate: Déclenché quand l'utilisateur note
|
||||
- cancel: Déclenché quand l'utilisateur annule sa note
|
||||
|
||||
Exemples de backing bean:
|
||||
```java
|
||||
@Named
|
||||
@ViewScoped
|
||||
public class ReviewBean {
|
||||
private Integer rating;
|
||||
private Integer productRating = 0;
|
||||
|
||||
public void onRate() {
|
||||
System.out.println("User rated: " + rating + " stars");
|
||||
// Sauvegarder la note en base de données
|
||||
}
|
||||
|
||||
public void onCancelRating() {
|
||||
System.out.println("User cancelled rating");
|
||||
rating = null;
|
||||
}
|
||||
|
||||
// Getters et setters
|
||||
public Integer getRating() {
|
||||
return rating;
|
||||
}
|
||||
|
||||
public void setRating(Integer rating) {
|
||||
this.rating = rating;
|
||||
}
|
||||
|
||||
public Integer getProductRating() {
|
||||
return productRating;
|
||||
}
|
||||
|
||||
public void setProductRating(Integer productRating) {
|
||||
this.productRating = productRating;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Validation:
|
||||
```java
|
||||
@NotNull(message = "Veuillez noter le produit")
|
||||
@Min(value = 1, message = "Veuillez donner au moins 1 étoile")
|
||||
@Max(value = 5, message = "Maximum 5 étoiles")
|
||||
private Integer rating;
|
||||
```
|
||||
|
||||
Variants disponibles:
|
||||
- stars: Étoiles gold traditionnelles (défaut)
|
||||
- hearts: Coeurs rouges pour favoris/amour
|
||||
|
||||
Cas d'usage recommandés:
|
||||
- Notes de produits: 5 étoiles avec cancel
|
||||
- Satisfaction client: 5 étoiles sans cancel
|
||||
- Notes de films/séries: 10 étoiles
|
||||
- Affichage moyenne: readonly=true
|
||||
- Niveau de favori: hearts variant
|
||||
|
||||
Bouton Cancel:
|
||||
- Permet de remettre à zéro la notation
|
||||
- Icône croix rouge à gauche des étoiles
|
||||
- Utile pour permettre à l'utilisateur de revenir sur sa note
|
||||
- Désactiver avec cancel="false" si la note est obligatoire
|
||||
|
||||
Notes de style:
|
||||
- Étoiles gold Lions.dev (#FFC700) quand actives
|
||||
- Étoiles grises quand inactives
|
||||
- Hover: Étoiles jaunes avec scale 1.15
|
||||
- Active: Étoiles avec text-shadow gold
|
||||
- Bouton cancel rouge avec hover effect
|
||||
- Tailles étoiles: sm (18px), base (24px), lg (32px)
|
||||
- État readonly: pas de hover, curseur default
|
||||
- État disabled: opacity réduite, curseur not-allowed
|
||||
|
||||
Accessibilité:
|
||||
- Label associé correctement
|
||||
- Support clavier (flèches pour naviguer, Enter pour sélectionner)
|
||||
- Focus ring visible
|
||||
- Attribut role="radiogroup"
|
||||
- Attributs aria-label sur chaque étoile
|
||||
- État readonly clairement indiqué
|
||||
- Valeur annoncée aux lecteurs d'écran
|
||||
-->
|
||||
|
||||
<composite:interface>
|
||||
<!-- Standard attributes -->
|
||||
<composite:attribute name="id" required="false" type="java.lang.String" />
|
||||
<composite:attribute name="label" required="false" type="java.lang.String" />
|
||||
<composite:attribute name="value" required="false" />
|
||||
<composite:attribute name="required" required="false" type="java.lang.Boolean" default="false" />
|
||||
<composite:attribute name="disabled" required="false" type="java.lang.Boolean" default="false" />
|
||||
<composite:attribute name="readonly" required="false" type="java.lang.Boolean" default="false" />
|
||||
<composite:attribute name="stars" required="false" type="java.lang.Integer" default="5" />
|
||||
<composite:attribute name="cancel" required="false" type="java.lang.Boolean" default="true" />
|
||||
<composite:attribute name="readonly" required="false" type="java.lang.Boolean" default="false" />
|
||||
<composite:attribute name="disabled" required="false" type="java.lang.Boolean" default="false" />
|
||||
<composite:attribute name="size" required="false" type="java.lang.String" default="base" />
|
||||
<composite:attribute name="variant" required="false" type="java.lang.String" default="stars" />
|
||||
<composite:attribute name="required" required="false" type="java.lang.Boolean" default="false" />
|
||||
<composite:attribute name="helpText" required="false" type="java.lang.String" />
|
||||
<composite:attribute name="styleClass" required="false" type="java.lang.String" />
|
||||
<composite:attribute name="style" required="false" type="java.lang.String" />
|
||||
|
||||
<!-- Binding for rating component -->
|
||||
<composite:editableValueHolder name="rating" targets="ratingComponent" />
|
||||
<composite:editableValueHolder name="rating" targets="rating" />
|
||||
|
||||
<composite:clientBehavior name="rate" event="rate" targets="rating" />
|
||||
<composite:clientBehavior name="cancel" event="cancel" targets="rating" />
|
||||
</composite:interface>
|
||||
|
||||
<composite:implementation>
|
||||
<!-- Freya field pattern -->
|
||||
<div class="field #{cc.attrs.styleClass}">
|
||||
<!-- Label with required indicator -->
|
||||
<p:outputLabel for="ratingComponent"
|
||||
<div class="field #{not empty cc.attrs.size and cc.attrs.size ne 'base' ? 'field-'.concat(cc.attrs.size) : ''} #{cc.attrs.styleClass}"
|
||||
style="#{cc.attrs.style}">
|
||||
|
||||
<p:outputLabel for="rating"
|
||||
value="#{cc.attrs.label}"
|
||||
rendered="#{not empty cc.attrs.label}">
|
||||
<h:outputText value=" *" styleClass="p-error" rendered="#{cc.attrs.required}" />
|
||||
</p:outputLabel>
|
||||
|
||||
<!-- Rating component -->
|
||||
<p:rating id="ratingComponent"
|
||||
<p:rating id="rating"
|
||||
value="#{cc.attrs.value}"
|
||||
required="#{cc.attrs.required}"
|
||||
disabled="#{cc.attrs.disabled}"
|
||||
readonly="#{cc.attrs.readonly}"
|
||||
stars="#{cc.attrs.stars}"
|
||||
cancel="#{cc.attrs.cancel}" />
|
||||
cancel="#{cc.attrs.cancel}"
|
||||
readonly="#{cc.attrs.readonly}"
|
||||
disabled="#{cc.attrs.disabled}"
|
||||
required="#{cc.attrs.required}"
|
||||
styleClass="#{not empty cc.attrs.size and cc.attrs.size ne 'base' ? 'ui-rating-'.concat(cc.attrs.size) : ''} #{cc.attrs.variant eq 'hearts' ? 'ui-rating-hearts' : ''}" />
|
||||
|
||||
<!-- Validation message -->
|
||||
<p:message for="ratingComponent" />
|
||||
<small class="field-help" rendered="#{not empty cc.attrs.helpText}">
|
||||
#{cc.attrs.helpText}
|
||||
</small>
|
||||
|
||||
<p:message for="rating" styleClass="field-error" />
|
||||
</div>
|
||||
</composite:implementation>
|
||||
|
||||
|
||||
@@ -7,17 +7,22 @@
|
||||
xmlns:f="http://xmlns.jcp.org/jsf/core">
|
||||
|
||||
<!--
|
||||
Freya Field Select - Pattern field + label + selectOneMenu + message
|
||||
═══════════════════════════════════════════════════════════
|
||||
Lions.dev Field Select - Liste déroulante avec identité Lions.dev
|
||||
═══════════════════════════════════════════════════════════
|
||||
Version: 1.0.0 | Priority: CRITIQUE #6
|
||||
|
||||
Champ de sélection (dropdown) avec filtrage optionnel, label et validation.
|
||||
Styles Lions.dev avec focus ring accessible et états visuels clairs.
|
||||
|
||||
Usage:
|
||||
<fr:fieldSelect id="category"
|
||||
label="Catégorie"
|
||||
value="#{bean.category}"
|
||||
required="true"
|
||||
filter="true">
|
||||
<f:selectItem itemLabel="Sélectionnez..." itemValue="" />
|
||||
<f:selectItems value="#{bean.categories}" />
|
||||
<fr:fieldSelect id="pays" label="Pays" value="#{bean.pays}" required="true" filter="true">
|
||||
<f:selectItem itemLabel="Choisir..." itemValue="" />
|
||||
<f:selectItems value="#{bean.listePays}" />
|
||||
</fr:fieldSelect>
|
||||
|
||||
Tailles: sm (32px) | base (44px - défaut) | lg (52px)
|
||||
Attributs: filter, filterMatchMode, disabled, required, size
|
||||
-->
|
||||
|
||||
<composite:interface>
|
||||
@@ -29,18 +34,26 @@
|
||||
<composite:attribute name="disabled" required="false" type="java.lang.Boolean" default="false" />
|
||||
<composite:attribute name="filter" required="false" type="java.lang.Boolean" default="false" />
|
||||
<composite:attribute name="filterMatchMode" required="false" type="java.lang.String" default="contains" />
|
||||
<composite:attribute name="size" required="false" type="java.lang.String" default="base" />
|
||||
<composite:attribute name="styleClass" required="false" type="java.lang.String" />
|
||||
|
||||
<!-- Binding for select component -->
|
||||
<composite:editableValueHolder name="select" targets="selectComponent" />
|
||||
|
||||
<!-- AJAX Behaviors Support -->
|
||||
<composite:clientBehavior name="change" event="change" targets="selectComponent" />
|
||||
<composite:clientBehavior name="blur" event="blur" targets="selectComponent" />
|
||||
<composite:clientBehavior name="focus" event="focus" targets="selectComponent" />
|
||||
<composite:clientBehavior name="valueChange" event="valueChange" targets="selectComponent" />
|
||||
<composite:clientBehavior name="itemSelect" event="itemSelect" targets="selectComponent" />
|
||||
|
||||
<!-- Insertion point for select items -->
|
||||
<composite:insertChildren />
|
||||
</composite:interface>
|
||||
|
||||
<composite:implementation>
|
||||
<!-- Freya field pattern -->
|
||||
<div class="field #{cc.attrs.styleClass}">
|
||||
<div class="field #{not empty cc.attrs.size and cc.attrs.size ne 'base' ? 'field-'.concat(cc.attrs.size) : ''} #{cc.attrs.styleClass}">
|
||||
<!-- Label with required indicator -->
|
||||
<p:outputLabel for="selectComponent"
|
||||
value="#{cc.attrs.label}"
|
||||
|
||||
@@ -4,28 +4,192 @@
|
||||
xmlns:composite="http://xmlns.jcp.org/jsf/composite"
|
||||
xmlns:h="http://xmlns.jcp.org/jsf/html"
|
||||
xmlns:p="http://primefaces.org/ui">
|
||||
|
||||
<!--
|
||||
Freya Field Slider - Pattern field + label + slider + message
|
||||
═══════════════════════════════════════════════════════════
|
||||
Lions.dev Field Slider - Curseur de sélection numérique
|
||||
═══════════════════════════════════════════════════════════
|
||||
Version: 1.0.0 | Priority: CRITIQUE #32
|
||||
|
||||
Curseur (slider) pour sélection de valeur numérique avec handle personnalisé.
|
||||
Styles Lions.dev avec gradient bleu, animations fluides et transitions élégantes.
|
||||
|
||||
Usage:
|
||||
<fr:fieldSlider id="volume"
|
||||
label="Volume"
|
||||
value="#{bean.volume}"
|
||||
value="#{settingsBean.volume}"
|
||||
minValue="0"
|
||||
maxValue="100" />
|
||||
|
||||
Exemples:
|
||||
|
||||
1. Slider basique (0-100):
|
||||
<fr:fieldSlider id="brightness"
|
||||
label="Luminosité"
|
||||
value="#{settingsBean.brightness}"
|
||||
minValue="0"
|
||||
maxValue="100" />
|
||||
|
||||
2. Slider avec step personnalisé:
|
||||
<fr:fieldSlider id="price"
|
||||
label="Prix maximum"
|
||||
value="#{filterBean.maxPrice}"
|
||||
minValue="0"
|
||||
maxValue="1000"
|
||||
step="50"
|
||||
displayTemplate="{value}€" />
|
||||
|
||||
3. Slider vertical:
|
||||
<fr:fieldSlider id="temperature"
|
||||
label="Température"
|
||||
value="#{climateBean.temperature}"
|
||||
minValue="15"
|
||||
maxValue="30"
|
||||
orientation="vertical"
|
||||
displayTemplate="{value}°C" />
|
||||
|
||||
4. Slider avec AJAX:
|
||||
<fr:fieldSlider id="zoom"
|
||||
label="Zoom"
|
||||
value="#{editorBean.zoomLevel}"
|
||||
minValue="50"
|
||||
maxValue="200"
|
||||
step="10">
|
||||
<p:ajax event="slideEnd"
|
||||
update="preview"
|
||||
listener="#{editorBean.onZoomChange}" />
|
||||
</fr:fieldSlider>
|
||||
|
||||
5. Range slider (2 valeurs):
|
||||
<fr:fieldSlider id="ageRange"
|
||||
label="Tranche d'âge"
|
||||
value="#{filterBean.ageRange}"
|
||||
minValue="18"
|
||||
maxValue="65"
|
||||
range="true" />
|
||||
|
||||
6. Slider avec variant success:
|
||||
<fr:fieldSlider id="progress"
|
||||
label="Progression"
|
||||
value="#{taskBean.progress}"
|
||||
minValue="0"
|
||||
maxValue="100"
|
||||
variant="success"
|
||||
displayTemplate="{value}%" />
|
||||
|
||||
7. Slider avec taille personnalisée:
|
||||
<fr:fieldSlider id="quality"
|
||||
label="Qualité"
|
||||
value="#{exportBean.quality}"
|
||||
minValue="1"
|
||||
maxValue="10"
|
||||
size="lg"
|
||||
helpText="1 = Basse qualité, 10 = Haute qualité" />
|
||||
|
||||
Attributs disponibles:
|
||||
- id: Identifiant du composant
|
||||
- label: Label affiché au-dessus du slider
|
||||
- value: Valeur numérique bindée (Integer)
|
||||
- minValue: Valeur minimale (défaut: 0)
|
||||
- maxValue: Valeur maximale (défaut: 100)
|
||||
- step: Incrément de valeur (défaut: 1)
|
||||
- animate: true/false - Animation fluide (défaut: true)
|
||||
- displayTemplate: Format d'affichage (défaut: "{value}")
|
||||
- range: true/false - Slider avec 2 handles (défaut: false)
|
||||
- orientation: "horizontal" | "vertical" - Défaut: horizontal
|
||||
- variant: primary (défaut) | success | danger | warning - Couleur du track
|
||||
- size: sm | base (défaut) | lg - Taille du slider
|
||||
- disabled: true/false - Désactiver le slider (défaut: false)
|
||||
- helpText: Texte d'aide affiché sous le slider
|
||||
- styleClass: Classes CSS additionnelles
|
||||
- style: Styles CSS inline
|
||||
|
||||
AJAX Events:
|
||||
- slide: Déclenché pendant le déplacement
|
||||
- slideEnd: Déclenché à la fin du déplacement (recommandé)
|
||||
|
||||
Exemples de backing bean:
|
||||
```java
|
||||
@Named
|
||||
@ViewScoped
|
||||
public class SettingsBean {
|
||||
private int volume = 50;
|
||||
private int brightness = 80;
|
||||
private int[] ageRange = {25, 45}; // Pour range slider
|
||||
|
||||
public void onVolumeChange() {
|
||||
System.out.println("Volume changed to: " + volume);
|
||||
}
|
||||
|
||||
// Getters et setters
|
||||
public int getVolume() {
|
||||
return volume;
|
||||
}
|
||||
|
||||
public void setVolume(int volume) {
|
||||
this.volume = volume;
|
||||
}
|
||||
|
||||
public int getBrightness() {
|
||||
return brightness;
|
||||
}
|
||||
|
||||
public void setBrightness(int brightness) {
|
||||
this.brightness = brightness;
|
||||
}
|
||||
|
||||
public int[] getAgeRange() {
|
||||
return ageRange;
|
||||
}
|
||||
|
||||
public void setAgeRange(int[] ageRange) {
|
||||
this.ageRange = ageRange;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Display Templates:
|
||||
- {value}: Affiche la valeur brute (ex: "50")
|
||||
- {value}%: Affiche en pourcentage (ex: "50%")
|
||||
- {value}€: Affiche avec symbole euro (ex: "50€")
|
||||
- {value}°C: Affiche en degrés (ex: "50°C")
|
||||
- Prix: {value}€: Texte avec valeur (ex: "Prix: 50€")
|
||||
|
||||
Variants disponibles:
|
||||
- primary: Gradient bleu Lions.dev (défaut)
|
||||
- success: Gradient vert pour progression positive
|
||||
- danger: Gradient rouge pour valeurs critiques
|
||||
- warning: Gradient orange pour avertissements
|
||||
|
||||
Cas d'usage recommandés:
|
||||
- Volume, luminosité, zoom: 0-100
|
||||
- Prix, budget: Avec step et displayTemplate
|
||||
- Température: Avec orientation vertical et symbole °C
|
||||
- Progression: Variant success avec %
|
||||
- Filtres de recherche: Range slider pour intervalles
|
||||
|
||||
Notes de style:
|
||||
- Track avec gradient bleu Lions.dev
|
||||
- Handle circulaire avec border bleu
|
||||
- Hover: Handle agrandi avec shadow
|
||||
- Active: Handle encore plus grand (effet grab)
|
||||
- Background track gris clair
|
||||
- Tailles handle: sm (16px), base (20px), lg (24px)
|
||||
- État disabled avec opacity réduite
|
||||
- Transition fluide des valeurs
|
||||
|
||||
Accessibilité:
|
||||
- Label associé correctement
|
||||
- Support clavier (flèches pour ajuster)
|
||||
- Focus ring visible sur handle
|
||||
- Attribut role="slider"
|
||||
- Attributs aria-valuemin, aria-valuemax, aria-valuenow
|
||||
- Template d'affichage pour clarté
|
||||
-->
|
||||
|
||||
<composite:interface>
|
||||
<!-- Standard attributes -->
|
||||
<composite:attribute name="id" required="false" type="java.lang.String" />
|
||||
<composite:attribute name="label" required="false" type="java.lang.String" />
|
||||
<composite:attribute name="value" required="false" />
|
||||
<composite:attribute name="required" required="false" type="java.lang.Boolean" default="false" />
|
||||
<composite:attribute name="disabled" required="false" type="java.lang.Boolean" default="false" />
|
||||
<composite:attribute name="styleClass" required="false" type="java.lang.String" />
|
||||
|
||||
<!-- Slider specific attributes -->
|
||||
<composite:attribute name="minValue" required="false" type="java.lang.Integer" default="0" />
|
||||
<composite:attribute name="maxValue" required="false" type="java.lang.Integer" default="100" />
|
||||
<composite:attribute name="step" required="false" type="java.lang.Integer" default="1" />
|
||||
@@ -33,23 +197,28 @@
|
||||
<composite:attribute name="displayTemplate" required="false" type="java.lang.String" default="{value}" />
|
||||
<composite:attribute name="range" required="false" type="java.lang.Boolean" default="false" />
|
||||
<composite:attribute name="orientation" required="false" type="java.lang.String" default="horizontal" />
|
||||
<composite:attribute name="variant" required="false" type="java.lang.String" default="primary" />
|
||||
<composite:attribute name="size" required="false" type="java.lang.String" default="base" />
|
||||
<composite:attribute name="disabled" required="false" type="java.lang.Boolean" default="false" />
|
||||
<composite:attribute name="helpText" required="false" type="java.lang.String" />
|
||||
<composite:attribute name="styleClass" required="false" type="java.lang.String" />
|
||||
<composite:attribute name="style" required="false" type="java.lang.String" />
|
||||
|
||||
<!-- Binding for input component -->
|
||||
<composite:editableValueHolder name="input" targets="sliderComponent" />
|
||||
<composite:editableValueHolder name="input" targets="hiddenInput" />
|
||||
|
||||
<composite:clientBehavior name="slide" event="slide" targets="slider" />
|
||||
<composite:clientBehavior name="slideEnd" event="slideEnd" targets="slider" />
|
||||
</composite:interface>
|
||||
|
||||
<composite:implementation>
|
||||
<!-- Freya field pattern -->
|
||||
<div class="field #{cc.attrs.styleClass}">
|
||||
<!-- Label with required indicator -->
|
||||
<p:outputLabel for="sliderComponent"
|
||||
value="#{cc.attrs.label}"
|
||||
rendered="#{not empty cc.attrs.label}">
|
||||
<h:outputText value=" *" styleClass="p-error" rendered="#{cc.attrs.required}" />
|
||||
</p:outputLabel>
|
||||
<div class="field #{not empty cc.attrs.size and cc.attrs.size ne 'base' ? 'field-'.concat(cc.attrs.size) : ''} #{cc.attrs.styleClass}"
|
||||
style="#{cc.attrs.style}">
|
||||
|
||||
<!-- Slider component -->
|
||||
<p:slider id="sliderComponent"
|
||||
<p:outputLabel for="slider"
|
||||
value="#{cc.attrs.label}"
|
||||
rendered="#{not empty cc.attrs.label}" />
|
||||
|
||||
<p:slider id="slider"
|
||||
for="hiddenInput"
|
||||
minValue="#{cc.attrs.minValue}"
|
||||
maxValue="#{cc.attrs.maxValue}"
|
||||
@@ -58,15 +227,17 @@
|
||||
displayTemplate="#{cc.attrs.displayTemplate}"
|
||||
range="#{cc.attrs.range}"
|
||||
orientation="#{cc.attrs.orientation}"
|
||||
disabled="#{cc.attrs.disabled}" />
|
||||
disabled="#{cc.attrs.disabled}"
|
||||
styleClass="ui-slider-#{cc.attrs.variant} #{not empty cc.attrs.size and cc.attrs.size ne 'base' ? 'ui-slider-'.concat(cc.attrs.size) : ''}" />
|
||||
|
||||
<!-- Hidden input for value binding -->
|
||||
<h:inputHidden id="hiddenInput" value="#{cc.attrs.value}" />
|
||||
|
||||
<!-- Validation message -->
|
||||
<p:message for="hiddenInput" />
|
||||
<small class="field-help" rendered="#{not empty cc.attrs.helpText}">
|
||||
#{cc.attrs.helpText}
|
||||
</small>
|
||||
|
||||
<p:message for="hiddenInput" styleClass="field-error" />
|
||||
</div>
|
||||
</composite:implementation>
|
||||
|
||||
</html>
|
||||
|
||||
|
||||
@@ -4,46 +4,208 @@
|
||||
xmlns:composite="http://xmlns.jcp.org/jsf/composite"
|
||||
xmlns:h="http://xmlns.jcp.org/jsf/html"
|
||||
xmlns:p="http://primefaces.org/ui">
|
||||
|
||||
<!--
|
||||
Freya Field Switch - Pattern field + switch + label
|
||||
═══════════════════════════════════════════════════════════
|
||||
Lions.dev Field Switch - Interrupteur toggle avec label
|
||||
═══════════════════════════════════════════════════════════
|
||||
Version: 1.0.0 | Priority: CRITIQUE #30
|
||||
|
||||
Interrupteur toggle (switch) avec label intégré et animation fluide.
|
||||
Styles Lions.dev avec gradients, animations slide et transitions élégantes.
|
||||
|
||||
Usage:
|
||||
<fr:fieldSwitch id="active"
|
||||
label="Activé"
|
||||
value="#{bean.active}" />
|
||||
<fr:fieldSwitch id="notifications"
|
||||
label="Activer les notifications"
|
||||
value="#{settingsBean.notificationsEnabled}" />
|
||||
|
||||
Exemples:
|
||||
|
||||
1. Switch basique:
|
||||
<fr:fieldSwitch id="darkMode"
|
||||
label="Mode sombre"
|
||||
value="#{settingsBean.darkMode}" />
|
||||
|
||||
2. Switch avec taille personnalisée:
|
||||
<fr:fieldSwitch id="autoSave"
|
||||
label="Sauvegarde automatique"
|
||||
value="#{editorBean.autoSave}"
|
||||
size="lg" />
|
||||
|
||||
3. Switch avec variant success:
|
||||
<fr:fieldSwitch id="accountActive"
|
||||
label="Compte actif"
|
||||
value="#{userBean.isActive}"
|
||||
variant="success" />
|
||||
|
||||
4. Switch avec AJAX:
|
||||
<fr:fieldSwitch id="emailNotif"
|
||||
label="Notifications par email"
|
||||
value="#{settingsBean.emailNotifications}">
|
||||
<p:ajax event="change"
|
||||
update="emailSettings"
|
||||
listener="#{settingsBean.onEmailNotifToggle}" />
|
||||
</fr:fieldSwitch>
|
||||
|
||||
5. Switch désactivé:
|
||||
<fr:fieldSwitch id="premiumFeature"
|
||||
label="Fonctionnalité premium (nécessite abonnement)"
|
||||
value="#{userBean.premiumEnabled}"
|
||||
disabled="true" />
|
||||
|
||||
6. Switch avec texte d'aide:
|
||||
<fr:fieldSwitch id="twoFactor"
|
||||
label="Authentification à deux facteurs"
|
||||
value="#{securityBean.twoFactorAuth}"
|
||||
helpText="Ajoutez une couche de sécurité supplémentaire à votre compte" />
|
||||
|
||||
7. Switch avec on/off labels:
|
||||
<fr:fieldSwitch id="publicProfile"
|
||||
label="Profil public"
|
||||
value="#{userBean.profilePublic}"
|
||||
onLabel="Public"
|
||||
offLabel="Privé" />
|
||||
|
||||
Attributs disponibles:
|
||||
- id: Identifiant du composant
|
||||
- label: Texte du label affiché à côté du switch
|
||||
- value: Valeur booléenne bindée (true/false)
|
||||
- required: true/false - Champ obligatoire (défaut: false)
|
||||
- disabled: true/false - Désactiver le switch (défaut: false)
|
||||
- size: sm | base (défaut) | lg - Taille du switch
|
||||
- variant: primary (défaut) | success | danger - Couleur quand activé
|
||||
- onLabel: Texte affiché quand ON
|
||||
- offLabel: Texte affiché quand OFF
|
||||
- helpText: Texte d'aide affiché sous le switch
|
||||
- styleClass: Classes CSS additionnelles
|
||||
- style: Styles CSS inline
|
||||
|
||||
AJAX Events:
|
||||
- change: Déclenché quand l'état change
|
||||
- valueChange: Déclenché lors du changement d'état
|
||||
|
||||
Exemples de backing bean:
|
||||
```java
|
||||
@Named
|
||||
@ViewScoped
|
||||
public class SettingsBean {
|
||||
private boolean notificationsEnabled = true;
|
||||
private boolean darkMode = false;
|
||||
private boolean emailNotifications = true;
|
||||
|
||||
public void onEmailNotifToggle() {
|
||||
if (emailNotifications) {
|
||||
System.out.println("Email notifications enabled");
|
||||
// Initialiser les préférences d'email
|
||||
} else {
|
||||
System.out.println("Email notifications disabled");
|
||||
}
|
||||
}
|
||||
|
||||
// Getters et setters
|
||||
public boolean isNotificationsEnabled() {
|
||||
return notificationsEnabled;
|
||||
}
|
||||
|
||||
public void setNotificationsEnabled(boolean notificationsEnabled) {
|
||||
this.notificationsEnabled = notificationsEnabled;
|
||||
}
|
||||
|
||||
public boolean isDarkMode() {
|
||||
return darkMode;
|
||||
}
|
||||
|
||||
public void setDarkMode(boolean darkMode) {
|
||||
this.darkMode = darkMode;
|
||||
}
|
||||
|
||||
public boolean isEmailNotifications() {
|
||||
return emailNotifications;
|
||||
}
|
||||
|
||||
public void setEmailNotifications(boolean emailNotifications) {
|
||||
this.emailNotifications = emailNotifications;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Validation:
|
||||
```java
|
||||
@AssertTrue(message = "Vous devez accepter les conditions")
|
||||
private boolean termsAccepted;
|
||||
```
|
||||
|
||||
Variants disponibles:
|
||||
- primary: Gradient bleu Lions.dev (défaut)
|
||||
- success: Gradient vert pour états actifs/validés
|
||||
- danger: Gradient rouge pour activation/désactivation critique
|
||||
|
||||
Notes de style:
|
||||
- Switch avec gradient bleu Lions.dev quand activé
|
||||
- Animation slide fluide du bouton
|
||||
- Background gris quand désactivé
|
||||
- Hover effect sur le background
|
||||
- Focus ring pour accessibilité
|
||||
- Label cliquable (toggle le switch)
|
||||
- Tailles: sm (36px), base (44px), lg (52px)
|
||||
- État disabled avec opacity réduite
|
||||
- Support des messages d'erreur
|
||||
|
||||
Différences avec Checkbox:
|
||||
- Switch: Pour des actions ON/OFF avec effet immédiat
|
||||
- Checkbox: Pour des choix multiples ou validation de formulaire
|
||||
|
||||
Accessibilité:
|
||||
- Label associé correctement
|
||||
- Support clavier (Space pour toggle)
|
||||
- Focus ring visible
|
||||
- Attribut role="switch"
|
||||
- Attribut aria-checked dynamique
|
||||
- Indicateur visuel d'état
|
||||
-->
|
||||
|
||||
<composite:interface>
|
||||
<!-- Standard attributes -->
|
||||
<composite:attribute name="id" required="false" type="java.lang.String" />
|
||||
<composite:attribute name="label" required="false" type="java.lang.String" />
|
||||
<composite:attribute name="value" required="false" />
|
||||
<composite:attribute name="value" required="false" type="java.lang.Boolean" />
|
||||
<composite:attribute name="required" required="false" type="java.lang.Boolean" default="false" />
|
||||
<composite:attribute name="disabled" required="false" type="java.lang.Boolean" default="false" />
|
||||
<composite:attribute name="size" required="false" type="java.lang.String" default="base" />
|
||||
<composite:attribute name="variant" required="false" type="java.lang.String" default="primary" />
|
||||
<composite:attribute name="onLabel" required="false" type="java.lang.String" />
|
||||
<composite:attribute name="offLabel" required="false" type="java.lang.String" />
|
||||
<composite:attribute name="helpText" required="false" type="java.lang.String" />
|
||||
<composite:attribute name="styleClass" required="false" type="java.lang.String" />
|
||||
<composite:attribute name="style" required="false" type="java.lang.String" />
|
||||
|
||||
<!-- Binding for switch component -->
|
||||
<composite:editableValueHolder name="switch" targets="switchComponent" />
|
||||
<composite:editableValueHolder name="switch" targets="switch" />
|
||||
|
||||
<composite:clientBehavior name="change" event="change" targets="switch" />
|
||||
<composite:clientBehavior name="valueChange" event="valueChange" targets="switch" />
|
||||
</composite:interface>
|
||||
|
||||
<composite:implementation>
|
||||
<!-- Freya field pattern for switches -->
|
||||
<div class="field-checkbox #{cc.attrs.styleClass}">
|
||||
<p:toggleSwitch id="switchComponent"
|
||||
value="#{cc.attrs.value}"
|
||||
required="#{cc.attrs.required}"
|
||||
disabled="#{cc.attrs.disabled}" />
|
||||
<div class="field field-checkbox #{not empty cc.attrs.size and cc.attrs.size ne 'base' ? 'field-'.concat(cc.attrs.size) : ''} #{cc.attrs.styleClass}"
|
||||
style="#{cc.attrs.style}">
|
||||
|
||||
<!-- Label -->
|
||||
<p:outputLabel for="switchComponent"
|
||||
<p:inputSwitch id="switch"
|
||||
value="#{cc.attrs.value}"
|
||||
required="#{cc.attrs.required}"
|
||||
disabled="#{cc.attrs.disabled}"
|
||||
onLabel="#{cc.attrs.onLabel}"
|
||||
offLabel="#{cc.attrs.offLabel}"
|
||||
styleClass="ui-inputswitch-#{cc.attrs.variant}" />
|
||||
|
||||
<p:outputLabel for="switch"
|
||||
value="#{cc.attrs.label}"
|
||||
rendered="#{not empty cc.attrs.label}">
|
||||
<h:outputText value=" *" styleClass="p-error" rendered="#{cc.attrs.required}" />
|
||||
</p:outputLabel>
|
||||
|
||||
<!-- Validation message -->
|
||||
<p:message for="switchComponent" />
|
||||
<small class="field-help" rendered="#{not empty cc.attrs.helpText}">
|
||||
#{cc.attrs.helpText}
|
||||
</small>
|
||||
|
||||
<p:message for="switch" styleClass="field-error" />
|
||||
</div>
|
||||
</composite:implementation>
|
||||
|
||||
|
||||
@@ -6,14 +6,19 @@
|
||||
xmlns:p="http://primefaces.org/ui">
|
||||
|
||||
<!--
|
||||
Freya Field Textarea - Pattern field + label + textarea + message
|
||||
═══════════════════════════════════════════════════════════
|
||||
Lions.dev Field Textarea - Zone de texte multiligne Lions.dev
|
||||
═══════════════════════════════════════════════════════════
|
||||
Version: 1.0.0 | Priority: CRITIQUE #7
|
||||
|
||||
Champ de saisie multiligne (Textarea) avec label, validation et auto-resize.
|
||||
Styles Lions.dev avec bordures élégantes et focus ring accessible.
|
||||
|
||||
Usage:
|
||||
<fr:fieldTextarea id="description"
|
||||
label="Description"
|
||||
value="#{bean.description}"
|
||||
rows="5"
|
||||
required="true" />
|
||||
<fr:fieldTextarea id="bio" label="Biographie" value="#{bean.bio}" rows="4" required="true" />
|
||||
|
||||
Tailles: sm | base (défaut) | lg
|
||||
Attributs: rows, cols, autoResize, maxlength, disabled, readonly, size
|
||||
-->
|
||||
|
||||
<composite:interface>
|
||||
@@ -29,15 +34,24 @@
|
||||
<composite:attribute name="cols" required="false" type="java.lang.Integer" />
|
||||
<composite:attribute name="autoResize" required="false" type="java.lang.Boolean" default="true" />
|
||||
<composite:attribute name="maxlength" required="false" type="java.lang.Integer" />
|
||||
<composite:attribute name="size" required="false" type="java.lang.String" default="base" />
|
||||
<composite:attribute name="styleClass" required="false" type="java.lang.String" />
|
||||
|
||||
<!-- Binding for textarea component -->
|
||||
<composite:editableValueHolder name="input" targets="textareaComponent" />
|
||||
|
||||
<!-- AJAX Behaviors Support -->
|
||||
<composite:clientBehavior name="keyup" event="keyup" targets="textareaComponent" />
|
||||
<composite:clientBehavior name="keydown" event="keydown" targets="textareaComponent" />
|
||||
<composite:clientBehavior name="blur" event="blur" targets="textareaComponent" />
|
||||
<composite:clientBehavior name="focus" event="focus" targets="textareaComponent" />
|
||||
<composite:clientBehavior name="change" event="change" targets="textareaComponent" />
|
||||
<composite:clientBehavior name="valueChange" event="valueChange" targets="textareaComponent" />
|
||||
</composite:interface>
|
||||
|
||||
<composite:implementation>
|
||||
<!-- Freya field pattern -->
|
||||
<div class="field #{cc.attrs.styleClass}">
|
||||
<div class="field #{not empty cc.attrs.size and cc.attrs.size ne 'base' ? 'field-'.concat(cc.attrs.size) : ''} #{cc.attrs.styleClass}">
|
||||
<!-- Label with required indicator -->
|
||||
<p:outputLabel for="textareaComponent"
|
||||
value="#{cc.attrs.label}"
|
||||
|
||||
@@ -4,61 +4,226 @@
|
||||
xmlns:composite="http://xmlns.jcp.org/jsf/composite"
|
||||
xmlns:h="http://xmlns.jcp.org/jsf/html"
|
||||
xmlns:p="http://primefaces.org/ui">
|
||||
|
||||
<!--
|
||||
Freya Field Toggle - Pattern field + selectBooleanButton + label
|
||||
═══════════════════════════════════════════════════════════
|
||||
Lions.dev Field Toggle - Bouton toggle ON/OFF avec label
|
||||
═══════════════════════════════════════════════════════════
|
||||
Version: 1.0.0 | Priority: CRITIQUE #31
|
||||
|
||||
Bouton toggle (selectBooleanButton) avec état ON/OFF, labels et icônes personnalisables.
|
||||
Styles Lions.dev avec gradient bleu, animations et transitions élégantes.
|
||||
|
||||
Usage:
|
||||
<fr:fieldToggle id="notif"
|
||||
label="Notifications"
|
||||
value="#{bean.notif}"
|
||||
onLabel="Oui"
|
||||
offLabel="Non"
|
||||
onIcon="pi pi-check"
|
||||
offIcon="pi pi-times" />
|
||||
<fr:fieldToggle id="active"
|
||||
label="Compte actif"
|
||||
value="#{userBean.isActive}"
|
||||
onLabel="Actif"
|
||||
offLabel="Inactif" />
|
||||
|
||||
Exemples:
|
||||
|
||||
1. Toggle basique:
|
||||
<fr:fieldToggle id="published"
|
||||
label="Publié"
|
||||
value="#{articleBean.published}"
|
||||
onLabel="Publié"
|
||||
offLabel="Brouillon" />
|
||||
|
||||
2. Toggle avec icônes:
|
||||
<fr:fieldToggle id="notifications"
|
||||
label="Notifications"
|
||||
value="#{settingsBean.notifications}"
|
||||
onLabel="ON"
|
||||
offLabel="OFF"
|
||||
onIcon="pi pi-check"
|
||||
offIcon="pi pi-times" />
|
||||
|
||||
3. Toggle avec AJAX:
|
||||
<fr:fieldToggle id="status"
|
||||
label="Statut"
|
||||
value="#{userBean.accountStatus}">
|
||||
<p:ajax event="change"
|
||||
update="statusPanel"
|
||||
listener="#{userBean.onStatusChange}" />
|
||||
</fr:fieldToggle>
|
||||
|
||||
4. Toggle désactivé:
|
||||
<fr:fieldToggle id="premium"
|
||||
label="Compte premium"
|
||||
value="#{userBean.premiumAccount}"
|
||||
disabled="true"
|
||||
onLabel="Premium"
|
||||
offLabel="Standard" />
|
||||
|
||||
5. Toggle avec taille personnalisée:
|
||||
<fr:fieldToggle id="featured"
|
||||
label="Article en vedette"
|
||||
value="#{articleBean.featured}"
|
||||
size="lg"
|
||||
onLabel="Oui"
|
||||
offLabel="Non" />
|
||||
|
||||
6. Toggle avec texte d'aide:
|
||||
<fr:fieldToggle id="public"
|
||||
label="Profil public"
|
||||
value="#{userBean.publicProfile}"
|
||||
helpText="Rendre votre profil visible par tous les utilisateurs"
|
||||
onLabel="Public"
|
||||
offLabel="Privé" />
|
||||
|
||||
7. Toggle Yes/No avec icônes:
|
||||
<fr:fieldToggle id="accept"
|
||||
label="Accepter les conditions"
|
||||
value="#{orderBean.termsAccepted}"
|
||||
onLabel="Oui"
|
||||
offLabel="Non"
|
||||
onIcon="pi pi-check-circle"
|
||||
offIcon="pi pi-times-circle"
|
||||
required="true" />
|
||||
|
||||
Attributs disponibles:
|
||||
- id: Identifiant du composant
|
||||
- label: Label affiché au-dessus du toggle
|
||||
- value: Valeur booléenne bindée (true/false)
|
||||
- onLabel: Texte affiché quand ON (défaut: "Oui")
|
||||
- offLabel: Texte affiché quand OFF (défaut: "Non")
|
||||
- onIcon: Icône PrimeIcons affichée quand ON
|
||||
- offIcon: Icône PrimeIcons affichée quand OFF
|
||||
- required: true/false - Champ obligatoire (défaut: false)
|
||||
- disabled: true/false - Désactiver le toggle (défaut: false)
|
||||
- size: sm | base (défaut) | lg - Taille du bouton
|
||||
- helpText: Texte d'aide affiché sous le toggle
|
||||
- styleClass: Classes CSS additionnelles
|
||||
- style: Styles CSS inline
|
||||
|
||||
AJAX Events:
|
||||
- change: Déclenché quand l'état change
|
||||
- valueChange: Déclenché lors du changement de valeur
|
||||
|
||||
Exemples de backing bean:
|
||||
```java
|
||||
@Named
|
||||
@ViewScoped
|
||||
public class ArticleBean {
|
||||
private boolean published = false;
|
||||
private boolean featured = false;
|
||||
|
||||
public void onPublishedToggle() {
|
||||
if (published) {
|
||||
System.out.println("Article published");
|
||||
// Envoyer notifications, indexer pour recherche, etc.
|
||||
} else {
|
||||
System.out.println("Article unpublished");
|
||||
}
|
||||
}
|
||||
|
||||
// Getters et setters
|
||||
public boolean isPublished() {
|
||||
return published;
|
||||
}
|
||||
|
||||
public void setPublished(boolean published) {
|
||||
this.published = published;
|
||||
}
|
||||
|
||||
public boolean isFeatured() {
|
||||
return featured;
|
||||
}
|
||||
|
||||
public void setFeatured(boolean featured) {
|
||||
this.featured = featured;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Validation:
|
||||
```java
|
||||
@AssertTrue(message = "Vous devez accepter les conditions")
|
||||
private boolean termsAccepted;
|
||||
```
|
||||
|
||||
Différences avec Switch:
|
||||
- Toggle Button: Bouton clickable avec labels ON/OFF visibles
|
||||
- Switch: Toggle slider minimaliste avec animation slide
|
||||
|
||||
Quand utiliser Toggle Button:
|
||||
- Quand les labels ON/OFF apportent de la clarté
|
||||
- Pour des actions business (Actif/Inactif, Publié/Brouillon)
|
||||
- Quand l'état doit être explicite pour l'utilisateur
|
||||
- Pour des formulaires où le contexte nécessite des labels
|
||||
|
||||
Icônes recommandées:
|
||||
- Check/Times: pi-check / pi-times
|
||||
- Check Circle/Times Circle: pi-check-circle / pi-times-circle
|
||||
- Thumbs Up/Down: pi-thumbs-up / pi-thumbs-down
|
||||
- Power: pi-power (ON/OFF général)
|
||||
- Lock/Unlock: pi-lock / pi-unlock
|
||||
|
||||
Notes de style:
|
||||
- Bouton avec gradient bleu Lions.dev quand ON (actif)
|
||||
- Bouton gris avec border quand OFF (inactif)
|
||||
- Hover effect sur border et background
|
||||
- Labels et icônes centrés dans le bouton
|
||||
- Focus ring pour accessibilité
|
||||
- Tailles: sm, base, lg
|
||||
- État disabled avec opacity réduite
|
||||
- Transition fluide entre états
|
||||
|
||||
Accessibilité:
|
||||
- Label associé correctement
|
||||
- Support clavier (Space/Enter pour toggle)
|
||||
- Focus ring visible
|
||||
- Attribut role="button"
|
||||
- Attribut aria-pressed dynamique
|
||||
- Labels ON/OFF pour clarté
|
||||
-->
|
||||
|
||||
<composite:interface>
|
||||
<!-- Standard attributes -->
|
||||
<composite:attribute name="id" required="false" type="java.lang.String" />
|
||||
<composite:attribute name="label" required="false" type="java.lang.String" />
|
||||
<composite:attribute name="value" required="false" />
|
||||
<composite:attribute name="required" required="false" type="java.lang.Boolean" default="false" />
|
||||
<composite:attribute name="disabled" required="false" type="java.lang.Boolean" default="false" />
|
||||
<composite:attribute name="styleClass" required="false" type="java.lang.String" />
|
||||
|
||||
<!-- Toggle specific attributes -->
|
||||
<composite:attribute name="value" required="false" type="java.lang.Boolean" />
|
||||
<composite:attribute name="onLabel" required="false" type="java.lang.String" default="Oui" />
|
||||
<composite:attribute name="offLabel" required="false" type="java.lang.String" default="Non" />
|
||||
<composite:attribute name="onIcon" required="false" type="java.lang.String" />
|
||||
<composite:attribute name="offIcon" required="false" type="java.lang.String" />
|
||||
<composite:attribute name="required" required="false" type="java.lang.Boolean" default="false" />
|
||||
<composite:attribute name="disabled" required="false" type="java.lang.Boolean" default="false" />
|
||||
<composite:attribute name="size" required="false" type="java.lang.String" default="base" />
|
||||
<composite:attribute name="helpText" required="false" type="java.lang.String" />
|
||||
<composite:attribute name="styleClass" required="false" type="java.lang.String" />
|
||||
<composite:attribute name="style" required="false" type="java.lang.String" />
|
||||
|
||||
<!-- Binding for toggle component -->
|
||||
<composite:editableValueHolder name="toggle" targets="toggleComponent" />
|
||||
<composite:editableValueHolder name="toggle" targets="toggle" />
|
||||
|
||||
<composite:clientBehavior name="change" event="change" targets="toggle" />
|
||||
<composite:clientBehavior name="valueChange" event="valueChange" targets="toggle" />
|
||||
</composite:interface>
|
||||
|
||||
<composite:implementation>
|
||||
<!-- Freya field pattern -->
|
||||
<div class="field #{cc.attrs.styleClass}">
|
||||
<!-- Label with required indicator -->
|
||||
<p:outputLabel for="toggleComponent"
|
||||
<div class="field #{not empty cc.attrs.size and cc.attrs.size ne 'base' ? 'field-'.concat(cc.attrs.size) : ''} #{cc.attrs.styleClass}"
|
||||
style="#{cc.attrs.style}">
|
||||
|
||||
<p:outputLabel for="toggle"
|
||||
value="#{cc.attrs.label}"
|
||||
rendered="#{not empty cc.attrs.label}">
|
||||
<h:outputText value=" *" styleClass="p-error" rendered="#{cc.attrs.required}" />
|
||||
</p:outputLabel>
|
||||
|
||||
<!-- SelectBooleanButton component -->
|
||||
<p:selectBooleanButton id="toggleComponent"
|
||||
<p:selectBooleanButton id="toggle"
|
||||
value="#{cc.attrs.value}"
|
||||
required="#{cc.attrs.required}"
|
||||
disabled="#{cc.attrs.disabled}"
|
||||
onLabel="#{cc.attrs.onLabel}"
|
||||
offLabel="#{cc.attrs.offLabel}"
|
||||
onIcon="#{cc.attrs.onIcon}"
|
||||
offIcon="#{cc.attrs.offIcon}" />
|
||||
offIcon="#{cc.attrs.offIcon}"
|
||||
styleClass="#{not empty cc.attrs.size and cc.attrs.size ne 'base' ? 'ui-togglebutton-'.concat(cc.attrs.size) : ''}" />
|
||||
|
||||
<!-- Validation message -->
|
||||
<p:message for="toggleComponent" />
|
||||
<small class="field-help" rendered="#{not empty cc.attrs.helpText}">
|
||||
#{cc.attrs.helpText}
|
||||
</small>
|
||||
|
||||
<p:message for="toggle" styleClass="field-error" />
|
||||
</div>
|
||||
</composite:implementation>
|
||||
|
||||
|
||||
@@ -6,10 +6,138 @@
|
||||
xmlns:p="http://primefaces.org/ui">
|
||||
|
||||
<!--
|
||||
Freya Growl - Notifications toast avec style Freya
|
||||
═══════════════════════════════════════════════════════════
|
||||
Lions.dev Growl - Notifications growl empilables (legacy)
|
||||
═══════════════════════════════════════════════════════════
|
||||
Version: 1.0.0 | Priority: CRITIQUE #20
|
||||
|
||||
Notifications growl empilables en coin d'écran (composant legacy).
|
||||
Styles Lions.dev avec gradients, ombres XL et animations fluides.
|
||||
|
||||
⚠️ Note: p:growl est déprécié dans PrimeFaces au profit de p:toast.
|
||||
Il est recommandé d'utiliser <fr:toast> pour les nouveaux projets.
|
||||
Ce composant est maintenu pour compatibilité avec le code existant.
|
||||
|
||||
Usage:
|
||||
<fr:growl id="growl" life="3000" sticky="false" />
|
||||
<fr:growl id="growl" />
|
||||
|
||||
Exemples:
|
||||
|
||||
1. Growl basique (coin supérieur droit):
|
||||
<fr:growl id="growl" />
|
||||
|
||||
2. Growl coin inférieur gauche avec durée personnalisée:
|
||||
<fr:growl id="growl" position="bottom-left" life="5000" />
|
||||
|
||||
3. Growl sticky (ne se ferme pas automatiquement):
|
||||
<fr:growl id="growl" sticky="true" />
|
||||
|
||||
4. Growl avec widgetVar pour contrôle JS:
|
||||
<fr:growl id="growl" widgetVar="growlWidget" position="top-center" />
|
||||
|
||||
5. Growl pour un composant spécifique:
|
||||
<fr:growl id="formGrowl" forId="userForm" position="bottom-right" />
|
||||
|
||||
Attributs disponibles:
|
||||
- id: Identifiant du composant
|
||||
- life: Durée d'affichage en ms (défaut: 3000ms = 3s)
|
||||
- sticky: true/false - Ne pas fermer automatiquement (défaut: false)
|
||||
- position: Position à l'écran (défaut: "top-right")
|
||||
- "top-right" (défaut)
|
||||
- "top-left"
|
||||
- "bottom-right"
|
||||
- "bottom-left"
|
||||
- "top-center"
|
||||
- "bottom-center"
|
||||
- "center"
|
||||
- showDetail: true/false - Afficher les détails (défaut: true)
|
||||
- showSummary: true/false - Afficher le résumé (défaut: true)
|
||||
- escape: true/false - Échapper le HTML (défaut: true)
|
||||
- forId: ID du composant ciblé
|
||||
- globalOnly: true/false - Messages globaux seulement (défaut: false)
|
||||
- widgetVar: Variable JS pour contrôle programmatique
|
||||
- styleClass: Classes CSS additionnelles
|
||||
- style: Styles CSS inline
|
||||
|
||||
Ajout de growl depuis le backing bean:
|
||||
```java
|
||||
// Growl info
|
||||
FacesContext.getCurrentInstance().addMessage(null,
|
||||
new FacesMessage(FacesMessage.SEVERITY_INFO,
|
||||
"Information",
|
||||
"Le traitement est en cours..."));
|
||||
|
||||
// Growl success
|
||||
FacesContext.getCurrentInstance().addMessage(null,
|
||||
new FacesMessage(FacesMessage.SEVERITY_INFO,
|
||||
"Succès",
|
||||
"Votre commande a été enregistrée avec succès."));
|
||||
|
||||
// Growl warning
|
||||
FacesContext.getCurrentInstance().addMessage(null,
|
||||
new FacesMessage(FacesMessage.SEVERITY_WARN,
|
||||
"Attention",
|
||||
"Votre session expirera dans 5 minutes."));
|
||||
|
||||
// Growl error
|
||||
FacesContext.getCurrentInstance().addMessage(null,
|
||||
new FacesMessage(FacesMessage.SEVERITY_ERROR,
|
||||
"Erreur",
|
||||
"Impossible de se connecter au serveur."));
|
||||
```
|
||||
|
||||
Contrôle JavaScript (avec widgetVar):
|
||||
```javascript
|
||||
// Afficher un growl programmatiquement
|
||||
PF('growlWidget').show({
|
||||
severity: 'info',
|
||||
summary: 'Notification',
|
||||
detail: 'Message personnalisé',
|
||||
life: 3000
|
||||
});
|
||||
|
||||
// Effacer tous les growl
|
||||
PF('growlWidget').clear();
|
||||
```
|
||||
|
||||
Types de severity:
|
||||
- info: Notification d'information (bleu cyan)
|
||||
- success: Opération réussie (vert)
|
||||
- warn: Avertissement (orange)
|
||||
- error: Erreur (rouge)
|
||||
|
||||
Positions disponibles:
|
||||
- top-right: Coin supérieur droit (défaut)
|
||||
- top-left: Coin supérieur gauche
|
||||
- bottom-right: Coin inférieur droit
|
||||
- bottom-left: Coin inférieur gauche
|
||||
- top-center: Centre en haut
|
||||
- bottom-center: Centre en bas
|
||||
- center: Centre de l'écran
|
||||
|
||||
Migration vers Toast:
|
||||
Pour migrer de <fr:growl> vers <fr:toast>, remplacez simplement:
|
||||
|
||||
Ancien code avec growl:
|
||||
<fr:growl id="growl" position="top-right" life="3000" />
|
||||
|
||||
Nouveau code avec toast:
|
||||
<fr:toast id="toast" position="top-right" life="3000" />
|
||||
|
||||
Les attributs et le code Java backend restent identiques.
|
||||
|
||||
Notes:
|
||||
- Empilage automatique de plusieurs notifications
|
||||
- Animation fade-in/fade-out fluide
|
||||
- Effet hover avec translateY
|
||||
- Bordure gauche colorée selon severity
|
||||
- Gradients subtils selon le type
|
||||
- Ombres XL pour effet de profondeur
|
||||
- Icônes automatiques selon severity
|
||||
- Bouton fermer avec rotation au hover
|
||||
- Responsive: pleine largeur sur mobile
|
||||
- Support ARIA pour accessibilité
|
||||
- Même API que p:toast pour migration facile
|
||||
-->
|
||||
|
||||
<composite:interface>
|
||||
|
||||
@@ -6,13 +6,65 @@
|
||||
xmlns:p="http://primefaces.org/ui">
|
||||
|
||||
<!--
|
||||
Freya LinkButton - Lien stylisé comme bouton
|
||||
═══════════════════════════════════════════════════════════
|
||||
Lions.dev LinkButton - Lien stylisé en bouton avec identité Lions.dev
|
||||
═══════════════════════════════════════════════════════════
|
||||
Version: 1.0.0
|
||||
Priority: HAUTE #3
|
||||
|
||||
Description:
|
||||
Lien de navigation (h:link/h:button) stylisé comme un bouton Lions.dev.
|
||||
Utilisé pour la navigation avec support des f:param et includeViewParams.
|
||||
Combine les avantages de h:link (SEO, bookmarkable) avec le style des boutons.
|
||||
|
||||
Fonctionnalités:
|
||||
- Gradients Lions.dev élégants
|
||||
- Animations hover (translateY + shadow elevation)
|
||||
- Focus ring accessible (WCAG 2.1 AA)
|
||||
- Support complet des variantes (solid, outlined, text, link, rounded, raised)
|
||||
- Support des tailles (sm, base, lg)
|
||||
- Support f:param pour paramètres URL
|
||||
|
||||
Usage:
|
||||
<fr:linkButton value="Voir détails"
|
||||
outcome="/details"
|
||||
<fr:linkButton value="Voir les détails"
|
||||
icon="pi pi-eye"
|
||||
severity="info" />
|
||||
outcome="/product-details"
|
||||
severity="info"
|
||||
size="lg">
|
||||
<f:param name="id" value="#{product.id}" />
|
||||
</fr:linkButton>
|
||||
|
||||
Exemples:
|
||||
|
||||
1. Navigation avec paramètres:
|
||||
<fr:linkButton value="Éditer" icon="pi pi-pencil" outcome="/edit" severity="primary">
|
||||
<f:param name="id" value="#{item.id}" />
|
||||
</fr:linkButton>
|
||||
|
||||
2. Lien externe:
|
||||
<fr:linkButton value="Documentation" icon="pi pi-book" href="https://docs.lions.dev"
|
||||
target="_blank" severity="info" outlined="true" />
|
||||
|
||||
3. Navigation avec fragment (anchor):
|
||||
<fr:linkButton value="Aller à la section" outcome="/page" fragment="section2" />
|
||||
|
||||
4. Bouton premium avec params:
|
||||
<fr:linkButton value="Upgrade Now" severity="help" rounded="true" raised="true"
|
||||
outcome="/pricing" includeViewParams="true" />
|
||||
|
||||
Severity disponibles:
|
||||
- primary (Lions Blue #2D9BEF - défaut)
|
||||
- secondary (Gray)
|
||||
- success (Green #22BB69)
|
||||
- info (Cyan #00BCD4)
|
||||
- warning (Orange #FF9800)
|
||||
- help (Lions Gold #FFC700 - premium)
|
||||
- danger (Red #F44336)
|
||||
|
||||
Tailles disponibles:
|
||||
- sm (32px height)
|
||||
- base (40px height - défaut)
|
||||
- lg (48px height)
|
||||
-->
|
||||
|
||||
<composite:interface>
|
||||
@@ -32,6 +84,10 @@
|
||||
|
||||
<!-- Severity: primary, secondary, success, info, warning, help, danger -->
|
||||
<composite:attribute name="severity" required="false" type="java.lang.String" />
|
||||
|
||||
<!-- Size: sm, base, lg -->
|
||||
<composite:attribute name="size" required="false" type="java.lang.String" default="base" />
|
||||
|
||||
<!-- Variants -->
|
||||
<composite:attribute name="text" required="false" type="java.lang.Boolean" default="false" />
|
||||
<composite:attribute name="outlined" required="false" type="java.lang.Boolean" default="false" />
|
||||
@@ -54,7 +110,7 @@
|
||||
target="#{cc.attrs.target}"
|
||||
includeViewParams="#{cc.attrs.includeViewParams}"
|
||||
fragment="#{cc.attrs.fragment}"
|
||||
styleClass="#{cc.attrs.styleClass} #{not empty cc.attrs.severity ? 'ui-button-'.concat(cc.attrs.severity) : ''} #{cc.attrs.text ? 'ui-button-text' : ''} #{cc.attrs.outlined ? 'ui-button-outlined' : ''} #{cc.attrs.link ? 'ui-button-link' : ''} #{cc.attrs.rounded ? 'ui-button-rounded' : ''} #{cc.attrs.raised ? 'ui-button-raised' : ''}"
|
||||
styleClass="#{cc.attrs.styleClass} #{not empty cc.attrs.severity ? 'ui-button-'.concat(cc.attrs.severity) : ''} #{not empty cc.attrs.size and cc.attrs.size ne 'base' ? 'ui-button-'.concat(cc.attrs.size) : ''} #{cc.attrs.text ? 'ui-button-text' : ''} #{cc.attrs.outlined ? 'ui-button-outlined' : ''} #{cc.attrs.link ? 'ui-button-link' : ''} #{cc.attrs.rounded ? 'ui-button-rounded' : ''} #{cc.attrs.raised ? 'ui-button-raised' : ''}"
|
||||
style="#{cc.attrs.style}"
|
||||
title="#{cc.attrs.title}">
|
||||
<composite:insertChildren />
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE html>
|
||||
<html xmlns="http://www.w3.org/1999/xhtml"
|
||||
xmlns:composite="http://xmlns.jcp.org/jsf/composite"
|
||||
xmlns:h="http://xmlns.jcp.org/jsf/html"
|
||||
xmlns:p="http://primefaces.org/ui">
|
||||
<!--
|
||||
═══════════════════════════════════════════════════════════
|
||||
Lions.dev Menu - Menu vertical de navigation
|
||||
═══════════════════════════════════════════════════════════
|
||||
Version: 1.0.0 | Priority: CRITIQUE #25
|
||||
Menu vertical avec sous-menus, icônes et styles Lions.dev.
|
||||
-->
|
||||
<composite:interface>
|
||||
<composite:attribute name="id" required="false" type="java.lang.String" />
|
||||
<composite:attribute name="model" required="true" />
|
||||
<composite:attribute name="style" required="false" type="java.lang.String" />
|
||||
<composite:attribute name="styleClass" required="false" type="java.lang.String" />
|
||||
<composite:attribute name="widgetVar" required="false" type="java.lang.String" />
|
||||
<composite:insertChildren />
|
||||
</composite:interface>
|
||||
<composite:implementation>
|
||||
<p:menu id="menu"
|
||||
model="#{cc.attrs.model}"
|
||||
style="#{cc.attrs.style}"
|
||||
styleClass="#{cc.attrs.styleClass}"
|
||||
widgetVar="#{cc.attrs.widgetVar}">
|
||||
<composite:insertChildren />
|
||||
</p:menu>
|
||||
</composite:implementation>
|
||||
</html>
|
||||
@@ -0,0 +1,36 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE html>
|
||||
<html xmlns="http://www.w3.org/1999/xhtml"
|
||||
xmlns:composite="http://xmlns.jcp.org/jsf/composite"
|
||||
xmlns:h="http://xmlns.jcp.org/jsf/html"
|
||||
xmlns:p="http://primefaces.org/ui"
|
||||
xmlns:f="http://xmlns.jcp.org/jsf/core">
|
||||
<!--
|
||||
═══════════════════════════════════════════════════════════
|
||||
Lions.dev Menubar - Barre de menu horizontale
|
||||
═══════════════════════════════════════════════════════════
|
||||
Version: 1.0.0 | Priority: CRITIQUE #26
|
||||
Menubar horizontal avec dropdowns, icônes et styles Lions.dev.
|
||||
-->
|
||||
<composite:interface>
|
||||
<composite:attribute name="id" required="false" type="java.lang.String" />
|
||||
<composite:attribute name="model" required="true" />
|
||||
<composite:attribute name="style" required="false" type="java.lang.String" />
|
||||
<composite:attribute name="styleClass" required="false" type="java.lang.String" />
|
||||
<composite:attribute name="widgetVar" required="false" type="java.lang.String" />
|
||||
<composite:facet name="options" />
|
||||
<composite:insertChildren />
|
||||
</composite:interface>
|
||||
<composite:implementation>
|
||||
<p:menubar id="menubar"
|
||||
model="#{cc.attrs.model}"
|
||||
style="#{cc.attrs.style}"
|
||||
styleClass="#{cc.attrs.styleClass}"
|
||||
widgetVar="#{cc.attrs.widgetVar}">
|
||||
<f:facet name="options" rendered="#{not empty cc.facets.options}">
|
||||
<composite:renderFacet name="options" />
|
||||
</f:facet>
|
||||
<composite:insertChildren />
|
||||
</p:menubar>
|
||||
</composite:implementation>
|
||||
</html>
|
||||
@@ -0,0 +1,119 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE html>
|
||||
<html xmlns="http://www.w3.org/1999/xhtml"
|
||||
xmlns:composite="http://xmlns.jcp.org/jsf/composite"
|
||||
xmlns:h="http://xmlns.jcp.org/jsf/html"
|
||||
xmlns:p="http://primefaces.org/ui">
|
||||
|
||||
<!--
|
||||
═══════════════════════════════════════════════════════════
|
||||
Lions.dev Messages - Affichage de messages FacesMessage
|
||||
═══════════════════════════════════════════════════════════
|
||||
Version: 1.0.0 | Priority: CRITIQUE #18
|
||||
|
||||
Affichage de messages info/succès/warning/erreur avec styles Lions.dev.
|
||||
Bordure colorée à gauche, icônes et bouton fermer.
|
||||
|
||||
Usage:
|
||||
<fr:messages id="msgs" />
|
||||
|
||||
Exemples:
|
||||
|
||||
1. Messages globaux (tous les messages):
|
||||
<fr:messages id="globalMessages" />
|
||||
|
||||
2. Messages pour un composant spécifique:
|
||||
<fr:messages id="formMessages" forId="userForm" />
|
||||
|
||||
3. Messages avec détails seulement:
|
||||
<fr:messages id="detailMessages" showSummary="false" showDetail="true" />
|
||||
|
||||
4. Messages non fermables (sticky):
|
||||
<fr:messages id="stickyMessages" closable="false" />
|
||||
|
||||
5. Messages compacts:
|
||||
<fr:messages id="compactMessages" styleClass="ui-messages-compact" />
|
||||
|
||||
Attributs disponibles:
|
||||
- id: Identifiant du composant
|
||||
- forId: ID du composant ciblé (affiche seulement les messages de ce composant)
|
||||
- showSummary: true/false - Afficher le résumé (défaut: true)
|
||||
- showDetail: true/false - Afficher les détails (défaut: true)
|
||||
- closable: true/false - Afficher le bouton fermer (défaut: true)
|
||||
- globalOnly: true/false - Afficher seulement les messages globaux (défaut: false)
|
||||
- redisplay: true/false - Réafficher les messages déjà affichés (défaut: true)
|
||||
- escape: true/false - Échapper le HTML (défaut: true)
|
||||
- styleClass: Classes CSS additionnelles (ui-messages-compact, ui-messages-large, ui-messages-inline)
|
||||
- style: Styles CSS inline
|
||||
|
||||
Ajout de messages depuis le backing bean:
|
||||
```java
|
||||
// Message global info
|
||||
FacesContext.getCurrentInstance().addMessage(null,
|
||||
new FacesMessage(FacesMessage.SEVERITY_INFO, "Information", "Opération réussie."));
|
||||
|
||||
// Message global success
|
||||
FacesContext.getCurrentInstance().addMessage(null,
|
||||
new FacesMessage(FacesMessage.SEVERITY_INFO, "Succès", "Enregistrement effectué."));
|
||||
|
||||
// Message global warning
|
||||
FacesContext.getCurrentInstance().addMessage(null,
|
||||
new FacesMessage(FacesMessage.SEVERITY_WARN, "Attention", "Vérifiez les données."));
|
||||
|
||||
// Message global error
|
||||
FacesContext.getCurrentInstance().addMessage(null,
|
||||
new FacesMessage(FacesMessage.SEVERITY_ERROR, "Erreur", "Échec de l'opération."));
|
||||
|
||||
// Message pour un composant spécifique
|
||||
FacesContext.getCurrentInstance().addMessage("userForm:email",
|
||||
new FacesMessage(FacesMessage.SEVERITY_ERROR, "Erreur", "Email invalide."));
|
||||
```
|
||||
|
||||
Types de messages (severity):
|
||||
- SEVERITY_INFO: Messages d'information (bleu cyan)
|
||||
- SEVERITY_WARN: Avertissements (orange)
|
||||
- SEVERITY_ERROR: Erreurs (rouge)
|
||||
- FacesMessage sans severity: Traité comme INFO
|
||||
|
||||
Classes utilitaires:
|
||||
- ui-messages-compact: Messages compacts (padding réduit)
|
||||
- ui-messages-large: Messages larges (padding augmenté)
|
||||
- ui-messages-inline: Messages inline (pas de margin)
|
||||
|
||||
Notes:
|
||||
- Affiche automatiquement les messages FacesMessage de la requête
|
||||
- Un message par severité (info, success, warning, error)
|
||||
- Bouton fermer avec animation de rotation au hover
|
||||
- Bordure colorée à gauche selon la severity
|
||||
- Icônes automatiques selon le type
|
||||
- Support ARIA pour accessibilité
|
||||
- Animations fade-in automatiques
|
||||
-->
|
||||
|
||||
<composite:interface>
|
||||
<composite:attribute name="id" required="false" type="java.lang.String" />
|
||||
<composite:attribute name="forId" required="false" type="java.lang.String" />
|
||||
<composite:attribute name="showSummary" required="false" type="java.lang.Boolean" default="true" />
|
||||
<composite:attribute name="showDetail" required="false" type="java.lang.Boolean" default="true" />
|
||||
<composite:attribute name="closable" required="false" type="java.lang.Boolean" default="true" />
|
||||
<composite:attribute name="globalOnly" required="false" type="java.lang.Boolean" default="false" />
|
||||
<composite:attribute name="redisplay" required="false" type="java.lang.Boolean" default="true" />
|
||||
<composite:attribute name="escape" required="false" type="java.lang.Boolean" default="true" />
|
||||
<composite:attribute name="styleClass" required="false" type="java.lang.String" />
|
||||
<composite:attribute name="style" required="false" type="java.lang.String" />
|
||||
</composite:interface>
|
||||
|
||||
<composite:implementation>
|
||||
<p:messages id="messages"
|
||||
for="#{cc.attrs.forId}"
|
||||
showSummary="#{cc.attrs.showSummary}"
|
||||
showDetail="#{cc.attrs.showDetail}"
|
||||
closable="#{cc.attrs.closable}"
|
||||
globalOnly="#{cc.attrs.globalOnly}"
|
||||
redisplay="#{cc.attrs.redisplay}"
|
||||
escape="#{cc.attrs.escape}"
|
||||
styleClass="#{cc.attrs.styleClass}"
|
||||
style="#{cc.attrs.style}" />
|
||||
</composite:implementation>
|
||||
|
||||
</html>
|
||||
@@ -0,0 +1,50 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE html>
|
||||
<html xmlns="http://www.w3.org/1999/xhtml"
|
||||
xmlns:composite="http://xmlns.jcp.org/jsf/composite"
|
||||
xmlns:h="http://xmlns.jcp.org/jsf/html"
|
||||
xmlns:p="http://primefaces.org/ui"
|
||||
xmlns:f="http://xmlns.jcp.org/jsf/core">
|
||||
<composite:interface>
|
||||
<composite:attribute name="id" required="false" type="java.lang.String" />
|
||||
<composite:attribute name="widgetVar" required="false" type="java.lang.String" />
|
||||
<composite:attribute name="showEvent" required="false" type="java.lang.String" default="click" />
|
||||
<composite:attribute name="hideEvent" required="false" type="java.lang.String" default="click" />
|
||||
<composite:attribute name="dismissable" required="false" type="java.lang.Boolean" default="true" />
|
||||
<composite:attribute name="showCloseIcon" required="false" type="java.lang.Boolean" default="false" />
|
||||
<composite:attribute name="dynamic" required="false" type="java.lang.Boolean" default="false" />
|
||||
<composite:attribute name="appendTo" required="false" type="java.lang.String" default="@(body)" />
|
||||
<composite:attribute name="my" required="false" type="java.lang.String" default="left top" />
|
||||
<composite:attribute name="at" required="false" type="java.lang.String" default="left bottom" />
|
||||
<composite:attribute name="styleClass" required="false" type="java.lang.String" />
|
||||
<composite:attribute name="style" required="false" type="java.lang.String" />
|
||||
<composite:attribute name="forComponent" required="false" type="java.lang.String" />
|
||||
<composite:facet name="header" />
|
||||
<composite:facet name="footer" />
|
||||
<composite:clientBehavior name="toggle" event="toggle" targets="overlayPanel" />
|
||||
<composite:insertChildren />
|
||||
</composite:interface>
|
||||
<composite:implementation>
|
||||
<p:overlayPanel id="overlayPanel"
|
||||
widgetVar="#{cc.attrs.widgetVar}"
|
||||
for="#{cc.attrs.forComponent}"
|
||||
showEvent="#{cc.attrs.showEvent}"
|
||||
hideEvent="#{cc.attrs.hideEvent}"
|
||||
dismissable="#{cc.attrs.dismissable}"
|
||||
showCloseIcon="#{cc.attrs.showCloseIcon}"
|
||||
dynamic="#{cc.attrs.dynamic}"
|
||||
appendTo="#{cc.attrs.appendTo}"
|
||||
my="#{cc.attrs.my}"
|
||||
at="#{cc.attrs.at}"
|
||||
styleClass="#{cc.attrs.styleClass}"
|
||||
style="#{cc.attrs.style}">
|
||||
<f:facet name="header" rendered="#{not empty cc.facets.header}">
|
||||
<composite:renderFacet name="header" />
|
||||
</f:facet>
|
||||
<composite:insertChildren />
|
||||
<f:facet name="footer" rendered="#{not empty cc.facets.footer}">
|
||||
<composite:renderFacet name="footer" />
|
||||
</f:facet>
|
||||
</p:overlayPanel>
|
||||
</composite:implementation>
|
||||
</html>
|
||||
@@ -7,12 +7,82 @@
|
||||
xmlns:f="http://xmlns.jcp.org/jsf/core">
|
||||
|
||||
<!--
|
||||
Freya Panel - Panneau PrimeFaces avec style Freya
|
||||
═══════════════════════════════════════════════════════════
|
||||
Lions.dev Panel - Panneau pliable avec identité Lions.dev
|
||||
═══════════════════════════════════════════════════════════
|
||||
Version: 1.0.0 | Priority: CRITIQUE #10
|
||||
|
||||
Panneau pliable/fermable pour organiser le contenu en sections.
|
||||
Styles Lions.dev avec header coloré selon severity et animations smooth.
|
||||
|
||||
Usage:
|
||||
<fr:panel header="Configuration" toggleable="true">
|
||||
<fr:panel header="Configuration" icon="pi pi-cog" toggleable="true" severity="primary">
|
||||
<p>Contenu du panneau...</p>
|
||||
</fr:panel>
|
||||
|
||||
Exemples:
|
||||
|
||||
1. Panel simple pliable:
|
||||
<fr:panel header="Détails" toggleable="true" collapsed="false">
|
||||
<p>Contenu visible par défaut...</p>
|
||||
</fr:panel>
|
||||
|
||||
2. Panel avec severity et icône:
|
||||
<fr:panel header="Alerte" icon="pi pi-exclamation-triangle" severity="warning" toggleable="true">
|
||||
<p>Vérifiez vos paramètres...</p>
|
||||
</fr:panel>
|
||||
|
||||
3. Panel avec actions dans le header:
|
||||
<fr:panel header="Projet" icon="pi pi-folder">
|
||||
<f:facet name="actions">
|
||||
<fr:commandButton icon="pi pi-refresh" text="true" size="sm" />
|
||||
<fr:commandButton icon="pi pi-download" text="true" size="sm" />
|
||||
</f:facet>
|
||||
<p>Contenu du projet...</p>
|
||||
</fr:panel>
|
||||
|
||||
4. Panel fermable:
|
||||
<fr:panel header="Notification" closable="true" severity="info">
|
||||
<p>Cette section peut être fermée définitivement.</p>
|
||||
</fr:panel>
|
||||
|
||||
5. Panel avec footer:
|
||||
<fr:panel header="Formulaire" severity="success">
|
||||
<fr:fieldInput label="Nom" value="#{bean.nom}" />
|
||||
<fr:fieldInput label="Email" value="#{bean.email}" />
|
||||
<f:facet name="footer">
|
||||
<fr:commandButton value="Enregistrer" severity="success" />
|
||||
<fr:commandButton value="Annuler" severity="secondary" outlined="true" />
|
||||
</f:facet>
|
||||
</fr:panel>
|
||||
|
||||
Attributs disponibles:
|
||||
- header: Titre du panneau
|
||||
- icon: Icône PrimeIcons dans le header
|
||||
- severity: primary|success|info|warning|danger|help - Couleur du header
|
||||
- toggleable: true/false - Permet de plier/déplier
|
||||
- collapsed: true/false - État initial (plié si true)
|
||||
- closable: true/false - Bouton fermer (X)
|
||||
- widgetVar: Variable JavaScript pour contrôle programmatique
|
||||
- styleClass: Classes CSS additionnelles
|
||||
|
||||
Facets disponibles:
|
||||
- header: Contenu personnalisé du header (remplace header text)
|
||||
- actions: Boutons/actions dans le header
|
||||
- footer: Contenu du footer
|
||||
|
||||
Severity variants:
|
||||
- primary: Header bleu Lions (#2D9BEF)
|
||||
- success: Header vert (#22BB69)
|
||||
- info: Header cyan (#00BCD4)
|
||||
- warning: Header orange (#FF9800)
|
||||
- danger: Header rouge (#F44336)
|
||||
- help: Header or Lions (#FFC700 - premium)
|
||||
|
||||
Contrôle JavaScript (avec widgetVar):
|
||||
PF('monPanel').toggle(); // Plier/déplier
|
||||
PF('monPanel').collapse(); // Plier
|
||||
PF('monPanel').expand(); // Déplier
|
||||
-->
|
||||
|
||||
<composite:interface>
|
||||
@@ -24,6 +94,8 @@
|
||||
<composite:attribute name="styleClass" required="false" type="java.lang.String" />
|
||||
<composite:attribute name="style" required="false" type="java.lang.String" />
|
||||
<composite:attribute name="widgetVar" required="false" type="java.lang.String" />
|
||||
<composite:attribute name="icon" required="false" type="java.lang.String" />
|
||||
<composite:attribute name="severity" required="false" type="java.lang.String" />
|
||||
|
||||
<!-- Facets -->
|
||||
<composite:facet name="header" />
|
||||
@@ -40,13 +112,16 @@
|
||||
toggleable="#{cc.attrs.toggleable}"
|
||||
collapsed="#{cc.attrs.collapsed}"
|
||||
closable="#{cc.attrs.closable}"
|
||||
styleClass="#{cc.attrs.styleClass}"
|
||||
styleClass="#{cc.attrs.styleClass} #{not empty cc.attrs.severity ? 'ui-panel-'.concat(cc.attrs.severity) : ''}"
|
||||
style="#{cc.attrs.style}"
|
||||
widgetVar="#{cc.attrs.widgetVar}">
|
||||
|
||||
<!-- Custom header facet -->
|
||||
<f:facet name="header" rendered="#{not empty cc.facets.header}">
|
||||
<composite:renderFacet name="header" />
|
||||
<!-- Custom header facet with icon support -->
|
||||
<f:facet name="header" rendered="#{not empty cc.facets.header or not empty cc.attrs.icon}">
|
||||
<div class="flex align-items-center gap-2">
|
||||
<i class="#{cc.attrs.icon}" rendered="#{not empty cc.attrs.icon}" />
|
||||
<composite:renderFacet name="header" />
|
||||
</div>
|
||||
</f:facet>
|
||||
|
||||
<!-- Actions in header -->
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE html>
|
||||
<html xmlns="http://www.w3.org/1999/xhtml"
|
||||
xmlns:composite="http://xmlns.jcp.org/jsf/composite"
|
||||
xmlns:h="http://xmlns.jcp.org/jsf/html"
|
||||
xmlns:p="http://primefaces.org/ui"
|
||||
xmlns:f="http://xmlns.jcp.org/jsf/core">
|
||||
<composite:interface>
|
||||
<composite:attribute name="id" required="false" type="java.lang.String" />
|
||||
<composite:attribute name="widgetVar" required="true" type="java.lang.String" />
|
||||
<composite:attribute name="position" required="false" type="java.lang.String" default="left" />
|
||||
<composite:attribute name="visible" required="false" type="java.lang.Boolean" default="false" />
|
||||
<composite:attribute name="fullScreen" required="false" type="java.lang.Boolean" default="false" />
|
||||
<composite:attribute name="modal" required="false" type="java.lang.Boolean" default="true" />
|
||||
<composite:attribute name="showCloseIcon" required="false" type="java.lang.Boolean" default="true" />
|
||||
<composite:attribute name="baseZIndex" required="false" type="java.lang.Integer" default="1000" />
|
||||
<composite:attribute name="styleClass" required="false" type="java.lang.String" />
|
||||
<composite:attribute name="style" required="false" type="java.lang.String" />
|
||||
<composite:facet name="header" />
|
||||
<composite:facet name="footer" />
|
||||
<composite:clientBehavior name="show" event="show" targets="sidebar" />
|
||||
<composite:clientBehavior name="hide" event="hide" targets="sidebar" />
|
||||
<composite:insertChildren />
|
||||
</composite:interface>
|
||||
<composite:implementation>
|
||||
<p:sidebar id="sidebar"
|
||||
widgetVar="#{cc.attrs.widgetVar}"
|
||||
position="#{cc.attrs.position}"
|
||||
visible="#{cc.attrs.visible}"
|
||||
fullScreen="#{cc.attrs.fullScreen}"
|
||||
modal="#{cc.attrs.modal}"
|
||||
showCloseIcon="#{cc.attrs.showCloseIcon}"
|
||||
baseZIndex="#{cc.attrs.baseZIndex}"
|
||||
styleClass="#{cc.attrs.styleClass}"
|
||||
style="#{cc.attrs.style}">
|
||||
<f:facet name="header" rendered="#{not empty cc.facets.header}">
|
||||
<composite:renderFacet name="header" />
|
||||
</f:facet>
|
||||
<composite:insertChildren />
|
||||
<f:facet name="footer" rendered="#{not empty cc.facets.footer}">
|
||||
<composite:renderFacet name="footer" />
|
||||
</f:facet>
|
||||
</p:sidebar>
|
||||
</composite:implementation>
|
||||
</html>
|
||||
@@ -6,14 +6,77 @@
|
||||
xmlns:p="http://primefaces.org/ui">
|
||||
|
||||
<!--
|
||||
Freya SplitButton - Bouton avec menu déroulant
|
||||
═══════════════════════════════════════════════════════════
|
||||
Lions.dev SplitButton - Bouton avec menu déroulant Lions.dev
|
||||
═══════════════════════════════════════════════════════════
|
||||
Version: 1.0.0
|
||||
Priority: HAUTE #4
|
||||
|
||||
Description:
|
||||
Bouton divisé combinant une action principale et un menu déroulant d'actions secondaires.
|
||||
Parfait pour "Sauvegarder + Options", "Exporter + Formats", etc.
|
||||
Utilise MenuModel pour les options du menu.
|
||||
|
||||
Fonctionnalités:
|
||||
- Gradients Lions.dev élégants sur bouton principal
|
||||
- Animations hover (translateY + shadow elevation)
|
||||
- Focus ring accessible (WCAG 2.1 AA)
|
||||
- Support des variantes (solid, outlined, rounded, raised)
|
||||
- Support des tailles (sm, base, lg)
|
||||
- Menu déroulant avec items personnalisables
|
||||
|
||||
Usage:
|
||||
<fr:splitButton value="Sauvegarder"
|
||||
icon="pi pi-save"
|
||||
<fr:splitButton value="Sauvegarder"
|
||||
icon="pi pi-save"
|
||||
action="#{bean.save}"
|
||||
model="#{bean.saveMenuModel}"
|
||||
severity="success" />
|
||||
model="#{bean.saveOptionsMenu}"
|
||||
severity="success"
|
||||
size="lg" />
|
||||
|
||||
Exemples:
|
||||
|
||||
1. Actions de sauvegarde multiples:
|
||||
<fr:splitButton value="Sauvegarder" icon="pi pi-save" severity="success"
|
||||
action="#{bean.save}" model="#{bean.saveMenu}" update="@form" />
|
||||
|
||||
Bean:
|
||||
private MenuModel saveMenu;
|
||||
@PostConstruct
|
||||
public void init() {
|
||||
saveMenu = new DefaultMenuModel();
|
||||
saveMenu.getElements().add(new DefaultMenuItem("Sauvegarder et continuer"));
|
||||
saveMenu.getElements().add(new DefaultMenuItem("Sauvegarder et fermer"));
|
||||
saveMenu.getElements().add(new DefaultMenuItem("Sauvegarder comme brouillon"));
|
||||
}
|
||||
|
||||
2. Export avec formats multiples:
|
||||
<fr:splitButton value="Exporter PDF" icon="pi pi-download" severity="primary"
|
||||
action="#{bean.exportPDF}" model="#{bean.exportFormatsMenu}" />
|
||||
|
||||
3. Actions dangereuses avec confirmation:
|
||||
<fr:splitButton value="Supprimer" icon="pi pi-trash" severity="danger"
|
||||
action="#{bean.delete}" model="#{bean.deleteOptionsMenu}"
|
||||
outlined="true" raised="true" />
|
||||
|
||||
4. Bouton premium avec options:
|
||||
<fr:splitButton value="Publier" severity="help" rounded="true"
|
||||
action="#{bean.publish}" model="#{bean.publishMenu}" />
|
||||
|
||||
Severity disponibles:
|
||||
- primary (Lions Blue #2D9BEF - défaut)
|
||||
- secondary (Gray)
|
||||
- success (Green #22BB69)
|
||||
- info (Cyan #00BCD4)
|
||||
- warning (Orange #FF9800)
|
||||
- help (Lions Gold #FFC700 - premium)
|
||||
- danger (Red #F44336)
|
||||
|
||||
Tailles disponibles:
|
||||
- sm (32px height)
|
||||
- base (40px height - défaut)
|
||||
- lg (48px height)
|
||||
|
||||
Note: Le menu déroulant hérite automatiquement du style du bouton principal.
|
||||
-->
|
||||
|
||||
<composite:interface>
|
||||
@@ -38,6 +101,10 @@
|
||||
|
||||
<!-- Severity: primary, secondary, success, info, warning, help, danger -->
|
||||
<composite:attribute name="severity" required="false" type="java.lang.String" />
|
||||
|
||||
<!-- Size: sm, base, lg -->
|
||||
<composite:attribute name="size" required="false" type="java.lang.String" default="base" />
|
||||
|
||||
<!-- Variants -->
|
||||
<composite:attribute name="outlined" required="false" type="java.lang.Boolean" default="false" />
|
||||
<composite:attribute name="rounded" required="false" type="java.lang.Boolean" default="false" />
|
||||
@@ -46,6 +113,10 @@
|
||||
<!-- Action source -->
|
||||
<composite:actionSource name="button" targets="splitButton" />
|
||||
|
||||
<!-- AJAX Behaviors Support -->
|
||||
<composite:clientBehavior name="action" event="action" targets="splitButton" default="true" />
|
||||
<composite:clientBehavior name="click" event="click" targets="splitButton" />
|
||||
|
||||
<!-- Content insertion for p:menuitem -->
|
||||
<composite:insertChildren />
|
||||
</composite:interface>
|
||||
@@ -65,7 +136,7 @@
|
||||
global="#{cc.attrs.global}"
|
||||
model="#{cc.attrs.model}"
|
||||
menuStyleClass="#{cc.attrs.menuStyleClass}"
|
||||
styleClass="#{cc.attrs.styleClass} #{not empty cc.attrs.severity ? 'ui-button-'.concat(cc.attrs.severity) : ''} #{cc.attrs.outlined ? 'ui-button-outlined' : ''} #{cc.attrs.rounded ? 'ui-button-rounded' : ''} #{cc.attrs.raised ? 'ui-button-raised' : ''}"
|
||||
styleClass="#{cc.attrs.styleClass} #{not empty cc.attrs.severity ? 'ui-button-'.concat(cc.attrs.severity) : ''} #{not empty cc.attrs.size and cc.attrs.size ne 'base' ? 'ui-button-'.concat(cc.attrs.size) : ''} #{cc.attrs.outlined ? 'ui-button-outlined' : ''} #{cc.attrs.rounded ? 'ui-button-rounded' : ''} #{cc.attrs.raised ? 'ui-button-raised' : ''}"
|
||||
style="#{cc.attrs.style}"
|
||||
title="#{cc.attrs.title}"
|
||||
widgetVar="#{cc.attrs.widgetVar}">
|
||||
|
||||
@@ -3,53 +3,39 @@
|
||||
<html xmlns="http://www.w3.org/1999/xhtml"
|
||||
xmlns:composite="http://xmlns.jcp.org/jsf/composite"
|
||||
xmlns:h="http://xmlns.jcp.org/jsf/html"
|
||||
xmlns:p="http://primefaces.org/ui"
|
||||
xmlns:f="http://xmlns.jcp.org/jsf/core">
|
||||
|
||||
xmlns:p="http://primefaces.org/ui">
|
||||
<!--
|
||||
Freya TabView - Onglets avec style Freya
|
||||
|
||||
Usage:
|
||||
<fr:tabView id="tabs" activeIndex="0">
|
||||
<p:tab title="Général">
|
||||
<p>Contenu onglet 1</p>
|
||||
</p:tab>
|
||||
<p:tab title="Détails">
|
||||
<p>Contenu onglet 2</p>
|
||||
</p:tab>
|
||||
</fr:tabView>
|
||||
═══════════════════════════════════════════════════════════
|
||||
Lions.dev TabView - Onglets de navigation
|
||||
═══════════════════════════════════════════════════════════
|
||||
Version: 1.0.0 | Priority: CRITIQUE #27
|
||||
Onglets de navigation avec icônes, barre active et styles Lions.dev.
|
||||
-->
|
||||
|
||||
<composite:interface>
|
||||
<composite:attribute name="id" required="false" type="java.lang.String" />
|
||||
<composite:attribute name="activeIndex" required="false" type="java.lang.Integer" default="0" />
|
||||
<composite:attribute name="orientation" required="false" type="java.lang.String" default="top" />
|
||||
<composite:attribute name="dynamic" required="false" type="java.lang.Boolean" default="false" />
|
||||
<composite:attribute name="cache" required="false" type="java.lang.Boolean" default="true" />
|
||||
<composite:attribute name="scrollable" required="false" type="java.lang.Boolean" default="false" />
|
||||
<composite:attribute name="orientation" required="false" type="java.lang.String" default="top" />
|
||||
<composite:attribute name="styleClass" required="false" type="java.lang.String" />
|
||||
<composite:attribute name="style" required="false" type="java.lang.String" />
|
||||
<composite:attribute name="styleClass" required="false" type="java.lang.String" />
|
||||
<composite:attribute name="widgetVar" required="false" type="java.lang.String" />
|
||||
|
||||
<!-- Content insertion for tabs -->
|
||||
<composite:clientBehavior name="tabChange" event="tabChange" targets="tabView" />
|
||||
<composite:clientBehavior name="tabClose" event="tabClose" targets="tabView" />
|
||||
<composite:insertChildren />
|
||||
</composite:interface>
|
||||
|
||||
<composite:implementation>
|
||||
<p:tabView id="tabView"
|
||||
activeIndex="#{cc.attrs.activeIndex}"
|
||||
orientation="#{cc.attrs.orientation}"
|
||||
dynamic="#{cc.attrs.dynamic}"
|
||||
cache="#{cc.attrs.cache}"
|
||||
scrollable="#{cc.attrs.scrollable}"
|
||||
orientation="#{cc.attrs.orientation}"
|
||||
styleClass="#{cc.attrs.styleClass}"
|
||||
style="#{cc.attrs.style}"
|
||||
styleClass="#{cc.attrs.styleClass}"
|
||||
widgetVar="#{cc.attrs.widgetVar}">
|
||||
|
||||
<!-- Tabs -->
|
||||
<composite:insertChildren />
|
||||
</p:tabView>
|
||||
</composite:implementation>
|
||||
|
||||
</html>
|
||||
|
||||
|
||||
164
runtime/src/main/resources/META-INF/resources/freya/toast.xhtml
Normal file
164
runtime/src/main/resources/META-INF/resources/freya/toast.xhtml
Normal file
@@ -0,0 +1,164 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE html>
|
||||
<html xmlns="http://www.w3.org/1999/xhtml"
|
||||
xmlns:composite="http://xmlns.jcp.org/jsf/composite"
|
||||
xmlns:h="http://xmlns.jcp.org/jsf/html"
|
||||
xmlns:p="http://primefaces.org/ui">
|
||||
|
||||
<!--
|
||||
═══════════════════════════════════════════════════════════
|
||||
Lions.dev Toast - Notifications toast temporaires
|
||||
═══════════════════════════════════════════════════════════
|
||||
Version: 1.0.0 | Priority: CRITIQUE #19
|
||||
|
||||
Notifications toast modernes en coin d'écran avec fermeture automatique.
|
||||
Styles Lions.dev avec gradients, ombres XL et animations fluides.
|
||||
|
||||
Usage:
|
||||
<fr:toast id="toast" />
|
||||
|
||||
Exemples:
|
||||
|
||||
1. Toast basique (coin supérieur droit):
|
||||
<fr:toast id="toast" />
|
||||
|
||||
2. Toast coin inférieur gauche avec durée personnalisée:
|
||||
<fr:toast id="toast" position="bottom-left" life="5000" />
|
||||
|
||||
3. Toast sticky (ne se ferme pas automatiquement):
|
||||
<fr:toast id="toast" sticky="true" />
|
||||
|
||||
4. Toast avec widgetVar pour contrôle JS:
|
||||
<fr:toast id="toast" widgetVar="toastWidget" position="top-center" />
|
||||
|
||||
5. Toast pour un composant spécifique:
|
||||
<fr:toast id="formToast" forId="userForm" position="bottom-right" />
|
||||
|
||||
Attributs disponibles:
|
||||
- id: Identifiant du composant
|
||||
- life: Durée d'affichage en ms (défaut: 3000ms = 3s)
|
||||
- sticky: true/false - Ne pas fermer automatiquement (défaut: false)
|
||||
- position: Position à l'écran (défaut: "top-right")
|
||||
- "top-right" (défaut)
|
||||
- "top-left"
|
||||
- "bottom-right"
|
||||
- "bottom-left"
|
||||
- "top-center"
|
||||
- "bottom-center"
|
||||
- "center"
|
||||
- showDetail: true/false - Afficher les détails (défaut: true)
|
||||
- showSummary: true/false - Afficher le résumé (défaut: true)
|
||||
- escape: true/false - Échapper le HTML (défaut: true)
|
||||
- forId: ID du composant ciblé
|
||||
- globalOnly: true/false - Messages globaux seulement (défaut: false)
|
||||
- widgetVar: Variable JS pour contrôle programmatique
|
||||
- styleClass: Classes CSS additionnelles
|
||||
- style: Styles CSS inline
|
||||
|
||||
Ajout de toasts depuis le backing bean:
|
||||
```java
|
||||
// Toast info
|
||||
FacesContext.getCurrentInstance().addMessage(null,
|
||||
new FacesMessage(FacesMessage.SEVERITY_INFO,
|
||||
"Information",
|
||||
"Le traitement est en cours..."));
|
||||
|
||||
// Toast success
|
||||
FacesContext.getCurrentInstance().addMessage(null,
|
||||
new FacesMessage(FacesMessage.SEVERITY_INFO,
|
||||
"Succès",
|
||||
"Votre commande a été enregistrée avec succès."));
|
||||
|
||||
// Toast warning
|
||||
FacesContext.getCurrentInstance().addMessage(null,
|
||||
new FacesMessage(FacesMessage.SEVERITY_WARN,
|
||||
"Attention",
|
||||
"Votre session expirera dans 5 minutes."));
|
||||
|
||||
// Toast error
|
||||
FacesContext.getCurrentInstance().addMessage(null,
|
||||
new FacesMessage(FacesMessage.SEVERITY_ERROR,
|
||||
"Erreur",
|
||||
"Impossible de se connecter au serveur."));
|
||||
```
|
||||
|
||||
Contrôle JavaScript (avec widgetVar):
|
||||
```javascript
|
||||
// Afficher un toast programmatiquement
|
||||
PF('toastWidget').show({
|
||||
severity: 'info',
|
||||
summary: 'Notification',
|
||||
detail: 'Message personnalisé',
|
||||
life: 3000
|
||||
});
|
||||
|
||||
// Effacer tous les toasts
|
||||
PF('toastWidget').clear();
|
||||
```
|
||||
|
||||
Types de severity:
|
||||
- info: Notification d'information (bleu cyan)
|
||||
- success: Opération réussie (vert)
|
||||
- warn: Avertissement (orange)
|
||||
- error: Erreur (rouge)
|
||||
|
||||
Positions disponibles:
|
||||
- top-right: Coin supérieur droit (défaut) - Recommandé pour notifications
|
||||
- top-left: Coin supérieur gauche
|
||||
- bottom-right: Coin inférieur droit - Recommandé pour confirmations
|
||||
- bottom-left: Coin inférieur gauche
|
||||
- top-center: Centre en haut
|
||||
- bottom-center: Centre en bas
|
||||
- center: Centre de l'écran
|
||||
|
||||
Durées recommandées:
|
||||
- Info courte: 2000-3000ms (2-3 secondes)
|
||||
- Succès: 3000-4000ms (3-4 secondes)
|
||||
- Warning: 5000-7000ms (5-7 secondes)
|
||||
- Error: 7000-10000ms (7-10 secondes) ou sticky
|
||||
- Sticky: Pour les messages critiques nécessitant une action
|
||||
|
||||
Notes:
|
||||
- Empilage automatique de plusieurs toasts
|
||||
- Animation fade-in/fade-out fluide
|
||||
- Effet hover avec translateY
|
||||
- Bordure gauche colorée selon severity
|
||||
- Gradients subtils selon le type
|
||||
- Ombres XL pour effet de profondeur
|
||||
- Icônes automatiques selon severity
|
||||
- Bouton fermer avec rotation au hover
|
||||
- Responsive: pleine largeur sur mobile
|
||||
- Support ARIA pour accessibilité
|
||||
-->
|
||||
|
||||
<composite:interface>
|
||||
<composite:attribute name="id" required="false" type="java.lang.String" />
|
||||
<composite:attribute name="life" required="false" type="java.lang.Integer" default="3000" />
|
||||
<composite:attribute name="sticky" required="false" type="java.lang.Boolean" default="false" />
|
||||
<composite:attribute name="position" required="false" type="java.lang.String" default="top-right" />
|
||||
<composite:attribute name="showDetail" required="false" type="java.lang.Boolean" default="true" />
|
||||
<composite:attribute name="showSummary" required="false" type="java.lang.Boolean" default="true" />
|
||||
<composite:attribute name="escape" required="false" type="java.lang.Boolean" default="true" />
|
||||
<composite:attribute name="forId" required="false" type="java.lang.String" />
|
||||
<composite:attribute name="globalOnly" required="false" type="java.lang.Boolean" default="false" />
|
||||
<composite:attribute name="widgetVar" required="false" type="java.lang.String" />
|
||||
<composite:attribute name="styleClass" required="false" type="java.lang.String" />
|
||||
<composite:attribute name="style" required="false" type="java.lang.String" />
|
||||
</composite:interface>
|
||||
|
||||
<composite:implementation>
|
||||
<p:toast id="toast"
|
||||
life="#{cc.attrs.life}"
|
||||
sticky="#{cc.attrs.sticky}"
|
||||
position="#{cc.attrs.position}"
|
||||
showDetail="#{cc.attrs.showDetail}"
|
||||
showSummary="#{cc.attrs.showSummary}"
|
||||
escape="#{cc.attrs.escape}"
|
||||
for="#{cc.attrs.forId}"
|
||||
globalOnly="#{cc.attrs.globalOnly}"
|
||||
widgetVar="#{cc.attrs.widgetVar}"
|
||||
styleClass="#{cc.attrs.styleClass}"
|
||||
style="#{cc.attrs.style}" />
|
||||
</composite:implementation>
|
||||
|
||||
</html>
|
||||
@@ -0,0 +1,570 @@
|
||||
/*
|
||||
* ═══════════════════════════════════════════════════════════
|
||||
* Lions.dev - Mixins SCSS
|
||||
* ═══════════════════════════════════════════════════════════
|
||||
* Mixins réutilisables pour composants Lions.dev
|
||||
*
|
||||
* Version: 1.0.0
|
||||
* Date: 1er Janvier 2026
|
||||
* Author: Lions Development Team
|
||||
* ═══════════════════════════════════════════════════════════
|
||||
*/
|
||||
|
||||
@import 'variables';
|
||||
|
||||
// ============================================
|
||||
// 📱 RESPONSIVE BREAKPOINTS
|
||||
// ============================================
|
||||
|
||||
@mixin lions-responsive-sm {
|
||||
@media (min-width: $lions-breakpoint-sm) {
|
||||
@content;
|
||||
}
|
||||
}
|
||||
|
||||
@mixin lions-responsive-md {
|
||||
@media (min-width: $lions-breakpoint-md) {
|
||||
@content;
|
||||
}
|
||||
}
|
||||
|
||||
@mixin lions-responsive-lg {
|
||||
@media (min-width: $lions-breakpoint-lg) {
|
||||
@content;
|
||||
}
|
||||
}
|
||||
|
||||
@mixin lions-responsive-xl {
|
||||
@media (min-width: $lions-breakpoint-xl) {
|
||||
@content;
|
||||
}
|
||||
}
|
||||
|
||||
@mixin lions-responsive-2xl {
|
||||
@media (min-width: $lions-breakpoint-2xl) {
|
||||
@content;
|
||||
}
|
||||
}
|
||||
|
||||
// Responsive custom breakpoint
|
||||
@mixin lions-responsive-custom($breakpoint) {
|
||||
@media (min-width: $breakpoint) {
|
||||
@content;
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// 🎨 GRADIENTS
|
||||
// ============================================
|
||||
|
||||
@mixin lions-gradient-blue {
|
||||
background: linear-gradient(135deg, $lions-blue-500 0%, $lions-blue-700 100%);
|
||||
}
|
||||
|
||||
@mixin lions-gradient-gold {
|
||||
background: linear-gradient(135deg, $lions-gold-500 0%, $lions-gold-700 100%);
|
||||
}
|
||||
|
||||
@mixin lions-gradient-success {
|
||||
background: linear-gradient(135deg, $lions-success-500 0%, $lions-success-700 100%);
|
||||
}
|
||||
|
||||
@mixin lions-gradient-warning {
|
||||
background: linear-gradient(135deg, $lions-warning-500 0%, $lions-warning-700 100%);
|
||||
}
|
||||
|
||||
@mixin lions-gradient-danger {
|
||||
background: linear-gradient(135deg, $lions-danger-500 0%, $lions-danger-700 100%);
|
||||
}
|
||||
|
||||
@mixin lions-gradient-info {
|
||||
background: linear-gradient(135deg, $lions-info-500 0%, $lions-info-700 100%);
|
||||
}
|
||||
|
||||
// Gradient custom
|
||||
@mixin lions-gradient-custom($color-start, $color-end, $angle: 135deg) {
|
||||
background: linear-gradient($angle, $color-start 0%, $color-end 100%);
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// 🎯 OMBRES
|
||||
// ============================================
|
||||
|
||||
@mixin lions-shadow($level: 'base') {
|
||||
@if $level == 'xs' {
|
||||
box-shadow: $lions-shadow-xs;
|
||||
} @else if $level == 'sm' {
|
||||
box-shadow: $lions-shadow-sm;
|
||||
} @else if $level == 'base' {
|
||||
box-shadow: $lions-shadow-base;
|
||||
} @else if $level == 'md' {
|
||||
box-shadow: $lions-shadow-md;
|
||||
} @else if $level == 'lg' {
|
||||
box-shadow: $lions-shadow-lg;
|
||||
} @else if $level == 'xl' {
|
||||
box-shadow: $lions-shadow-xl;
|
||||
} @else if $level == '2xl' {
|
||||
box-shadow: $lions-shadow-2xl;
|
||||
}
|
||||
}
|
||||
|
||||
// Shadow colorée
|
||||
@mixin lions-shadow-color($color, $opacity: 0.25) {
|
||||
box-shadow: 0 4px 16px rgba($color, $opacity);
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// 🔲 BORDER RADIUS
|
||||
// ============================================
|
||||
|
||||
@mixin lions-rounded($size: 'base') {
|
||||
@if $size == 'none' {
|
||||
border-radius: $lions-radius-none;
|
||||
} @else if $size == 'sm' {
|
||||
border-radius: $lions-radius-sm;
|
||||
} @else if $size == 'base' {
|
||||
border-radius: $lions-radius-base;
|
||||
} @else if $size == 'md' {
|
||||
border-radius: $lions-radius-md;
|
||||
} @else if $size == 'lg' {
|
||||
border-radius: $lions-radius-lg;
|
||||
} @else if $size == 'xl' {
|
||||
border-radius: $lions-radius-xl;
|
||||
} @else if $size == '2xl' {
|
||||
border-radius: $lions-radius-2xl;
|
||||
} @else if $size == 'full' {
|
||||
border-radius: $lions-radius-full;
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// 🌀 TRANSITIONS
|
||||
// ============================================
|
||||
|
||||
@mixin lions-transition($properties: all, $duration: $lions-transition-base, $easing: $lions-ease-in-out) {
|
||||
transition: $properties $duration $easing;
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// ♿ FOCUS RING
|
||||
// ============================================
|
||||
|
||||
@mixin lions-focus-ring($color: $lions-blue-500, $opacity: 0.4) {
|
||||
outline: 3px solid rgba($color, $opacity);
|
||||
outline-offset: 2px;
|
||||
}
|
||||
|
||||
@mixin lions-focus-visible {
|
||||
&:focus-visible {
|
||||
@include lions-focus-ring();
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// 📐 SIZING
|
||||
// ============================================
|
||||
|
||||
@mixin lions-size($width, $height: $width) {
|
||||
width: $width;
|
||||
height: $height;
|
||||
}
|
||||
|
||||
// Square
|
||||
@mixin lions-square($size) {
|
||||
@include lions-size($size, $size);
|
||||
}
|
||||
|
||||
// Circle
|
||||
@mixin lions-circle($size) {
|
||||
@include lions-square($size);
|
||||
border-radius: $lions-radius-full;
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// 📏 SPACING
|
||||
// ============================================
|
||||
|
||||
@mixin lions-padding($value) {
|
||||
padding: $value;
|
||||
}
|
||||
|
||||
@mixin lions-padding-x($value) {
|
||||
padding-left: $value;
|
||||
padding-right: $value;
|
||||
}
|
||||
|
||||
@mixin lions-padding-y($value) {
|
||||
padding-top: $value;
|
||||
padding-bottom: $value;
|
||||
}
|
||||
|
||||
@mixin lions-margin($value) {
|
||||
margin: $value;
|
||||
}
|
||||
|
||||
@mixin lions-margin-x($value) {
|
||||
margin-left: $value;
|
||||
margin-right: $value;
|
||||
}
|
||||
|
||||
@mixin lions-margin-y($value) {
|
||||
margin-top: $value;
|
||||
margin-bottom: $value;
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// 🔤 TYPOGRAPHY
|
||||
// ============================================
|
||||
|
||||
@mixin lions-font-size($size: 'base') {
|
||||
@if $size == 'xs' {
|
||||
font-size: $lions-font-xs;
|
||||
} @else if $size == 'sm' {
|
||||
font-size: $lions-font-sm;
|
||||
} @else if $size == 'base' {
|
||||
font-size: $lions-font-base;
|
||||
} @else if $size == 'lg' {
|
||||
font-size: $lions-font-lg;
|
||||
} @else if $size == 'xl' {
|
||||
font-size: $lions-font-xl;
|
||||
} @else if $size == '2xl' {
|
||||
font-size: $lions-font-2xl;
|
||||
} @else if $size == '3xl' {
|
||||
font-size: $lions-font-3xl;
|
||||
} @else if $size == '4xl' {
|
||||
font-size: $lions-font-4xl;
|
||||
} @else if $size == '5xl' {
|
||||
font-size: $lions-font-5xl;
|
||||
}
|
||||
}
|
||||
|
||||
@mixin lions-font-weight($weight: 'regular') {
|
||||
@if $weight == 'light' {
|
||||
font-weight: $lions-font-light;
|
||||
} @else if $weight == 'regular' {
|
||||
font-weight: $lions-font-regular;
|
||||
} @else if $weight == 'medium' {
|
||||
font-weight: $lions-font-medium;
|
||||
} @else if $weight == 'semibold' {
|
||||
font-weight: $lions-font-semibold;
|
||||
} @else if $weight == 'bold' {
|
||||
font-weight: $lions-font-bold;
|
||||
} @else if $weight == 'extrabold' {
|
||||
font-weight: $lions-font-extrabold;
|
||||
} @else if $weight == 'black' {
|
||||
font-weight: $lions-font-black;
|
||||
}
|
||||
}
|
||||
|
||||
// Truncate text with ellipsis
|
||||
@mixin lions-truncate {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
// Line clamp (multiline truncate)
|
||||
@mixin lions-line-clamp($lines: 2) {
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: $lines;
|
||||
-webkit-box-orient: vertical;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// 🎨 BUTTON VARIANTS
|
||||
// ============================================
|
||||
|
||||
@mixin lions-button-base {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: $lions-spacing-2;
|
||||
font-family: $lions-font-family-primary;
|
||||
@include lions-font-weight('semibold');
|
||||
text-align: center;
|
||||
text-decoration: none;
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
border: $lions-button-border-width solid transparent;
|
||||
@include lions-rounded('base');
|
||||
@include lions-transition();
|
||||
|
||||
&:disabled {
|
||||
opacity: 0.5;
|
||||
cursor: not-allowed;
|
||||
pointer-events: none;
|
||||
}
|
||||
}
|
||||
|
||||
@mixin lions-button-size($size: 'base') {
|
||||
@if $size == 'sm' {
|
||||
height: $lions-button-height-sm;
|
||||
padding: 0 $lions-button-padding-x-sm;
|
||||
@include lions-font-size('sm');
|
||||
} @else if $size == 'base' {
|
||||
height: $lions-button-height-base;
|
||||
padding: 0 $lions-button-padding-x-base;
|
||||
@include lions-font-size('base');
|
||||
} @else if $size == 'lg' {
|
||||
height: $lions-button-height-lg;
|
||||
padding: 0 $lions-button-padding-x-lg;
|
||||
@include lions-font-size('lg');
|
||||
}
|
||||
}
|
||||
|
||||
@mixin lions-button-solid($color: $lions-blue-500, $color-dark: $lions-blue-700) {
|
||||
@include lions-gradient-custom($color, $color-dark);
|
||||
color: $lions-white;
|
||||
@include lions-shadow-color($color, 0.25);
|
||||
|
||||
&:hover {
|
||||
transform: translateY(-2px);
|
||||
@include lions-shadow-color($color, 0.35);
|
||||
@include lions-gradient-custom(darken($color, 5%), darken($color-dark, 5%));
|
||||
}
|
||||
|
||||
&:active {
|
||||
transform: translateY(0);
|
||||
@include lions-shadow-color($color, 0.2);
|
||||
}
|
||||
|
||||
&:focus-visible {
|
||||
@include lions-focus-ring($color);
|
||||
}
|
||||
}
|
||||
|
||||
@mixin lions-button-outlined($color: $lions-blue-500) {
|
||||
background: transparent;
|
||||
border-color: $color;
|
||||
color: darken($color, 10%);
|
||||
box-shadow: none;
|
||||
|
||||
&:hover {
|
||||
background: rgba($color, 0.1);
|
||||
border-color: darken($color, 10%);
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
|
||||
&:active {
|
||||
transform: translateY(0);
|
||||
background: rgba($color, 0.15);
|
||||
}
|
||||
|
||||
&:focus-visible {
|
||||
@include lions-focus-ring($color);
|
||||
}
|
||||
}
|
||||
|
||||
@mixin lions-button-text($color: $lions-blue-500) {
|
||||
background: transparent;
|
||||
border: none;
|
||||
color: darken($color, 10%);
|
||||
box-shadow: none;
|
||||
|
||||
&:hover {
|
||||
background: rgba($color, 0.1);
|
||||
}
|
||||
|
||||
&:active {
|
||||
background: rgba($color, 0.15);
|
||||
}
|
||||
|
||||
&:focus-visible {
|
||||
@include lions-focus-ring($color);
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// 📝 FORM FIELD VARIANTS
|
||||
// ============================================
|
||||
|
||||
@mixin lions-input-base {
|
||||
width: 100%;
|
||||
font-family: $lions-font-family-primary;
|
||||
@include lions-font-size('base');
|
||||
color: $lions-text-primary;
|
||||
background: $lions-white;
|
||||
border: $lions-input-border-width solid $lions-gray-300;
|
||||
@include lions-rounded('base');
|
||||
@include lions-transition();
|
||||
|
||||
&::placeholder {
|
||||
color: $lions-gray-400;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
border-color: $lions-gray-400;
|
||||
}
|
||||
|
||||
&:focus {
|
||||
outline: none;
|
||||
border-color: $lions-blue-500;
|
||||
box-shadow: 0 0 0 3px rgba($lions-blue-500, 0.15);
|
||||
}
|
||||
|
||||
&:disabled {
|
||||
background: $lions-gray-100;
|
||||
cursor: not-allowed;
|
||||
opacity: 0.6;
|
||||
}
|
||||
}
|
||||
|
||||
@mixin lions-input-size($size: 'base') {
|
||||
@if $size == 'sm' {
|
||||
height: $lions-input-height-sm;
|
||||
padding: 0 $lions-spacing-3;
|
||||
@include lions-font-size('sm');
|
||||
} @else if $size == 'base' {
|
||||
height: $lions-input-height-base;
|
||||
padding: $lions-input-padding-y $lions-input-padding-x;
|
||||
} @else if $size == 'lg' {
|
||||
height: $lions-input-height-lg;
|
||||
padding: $lions-spacing-4 $lions-spacing-5;
|
||||
@include lions-font-size('lg');
|
||||
}
|
||||
}
|
||||
|
||||
@mixin lions-input-state-error {
|
||||
border-color: $lions-danger-500;
|
||||
box-shadow: 0 0 0 3px rgba($lions-danger-500, 0.15);
|
||||
|
||||
&:focus {
|
||||
border-color: $lions-danger-500;
|
||||
box-shadow: 0 0 0 3px rgba($lions-danger-500, 0.15);
|
||||
}
|
||||
}
|
||||
|
||||
@mixin lions-input-state-success {
|
||||
border-color: $lions-success-500;
|
||||
box-shadow: 0 0 0 3px rgba($lions-success-500, 0.15);
|
||||
|
||||
&:focus {
|
||||
border-color: $lions-success-500;
|
||||
box-shadow: 0 0 0 3px rgba($lions-success-500, 0.15);
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// 🎴 CARD & PANEL
|
||||
// ============================================
|
||||
|
||||
@mixin lions-card {
|
||||
background: $lions-white;
|
||||
border: 1px solid $lions-gray-200;
|
||||
@include lions-rounded('md');
|
||||
@include lions-shadow('base');
|
||||
@include lions-padding($lions-card-padding);
|
||||
@include lions-transition();
|
||||
|
||||
&:hover {
|
||||
@include lions-shadow('md');
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// 🔄 LOADING SPINNER
|
||||
// ============================================
|
||||
|
||||
@mixin lions-spinner($size: 1rem, $color: $lions-blue-500) {
|
||||
display: inline-block;
|
||||
@include lions-size($size);
|
||||
border: 2px solid rgba($color, 0.25);
|
||||
border-top-color: $color;
|
||||
border-radius: $lions-radius-full;
|
||||
animation: lions-spin 0.6s linear infinite;
|
||||
}
|
||||
|
||||
@keyframes lions-spin {
|
||||
to {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// 📊 TABLE
|
||||
// ============================================
|
||||
|
||||
@mixin lions-table-base {
|
||||
width: 100%;
|
||||
border-collapse: separate;
|
||||
border-spacing: 0;
|
||||
border: 1px solid $lions-gray-200;
|
||||
@include lions-rounded('md');
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
@mixin lions-table-header {
|
||||
background: $lions-gray-100;
|
||||
@include lions-font-weight('semibold');
|
||||
color: $lions-text-primary;
|
||||
padding: $lions-spacing-4;
|
||||
border-bottom: 2px solid $lions-gray-300;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
@mixin lions-table-cell {
|
||||
padding: $lions-spacing-4;
|
||||
border-bottom: 1px solid $lions-gray-200;
|
||||
color: $lions-text-primary;
|
||||
}
|
||||
|
||||
@mixin lions-table-row-hover {
|
||||
&:hover {
|
||||
background: rgba($lions-blue-50, 0.5);
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// 🎭 UTILITIES
|
||||
// ============================================
|
||||
|
||||
// Visually hidden (screen reader only)
|
||||
@mixin lions-sr-only {
|
||||
position: absolute;
|
||||
width: 1px;
|
||||
height: 1px;
|
||||
padding: 0;
|
||||
margin: -1px;
|
||||
overflow: hidden;
|
||||
clip: rect(0, 0, 0, 0);
|
||||
white-space: nowrap;
|
||||
border-width: 0;
|
||||
}
|
||||
|
||||
// Reset list styles
|
||||
@mixin lions-list-reset {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
// Clearfix
|
||||
@mixin lions-clearfix {
|
||||
&::after {
|
||||
content: '';
|
||||
display: table;
|
||||
clear: both;
|
||||
}
|
||||
}
|
||||
|
||||
// Center element absolutely
|
||||
@mixin lions-absolute-center {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
}
|
||||
|
||||
// Flexbox center
|
||||
@mixin lions-flex-center {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
// Grid center
|
||||
@mixin lions-grid-center {
|
||||
display: grid;
|
||||
place-items: center;
|
||||
}
|
||||
@@ -0,0 +1,273 @@
|
||||
/*
|
||||
* ═══════════════════════════════════════════════════════════
|
||||
* Lions.dev - Variables SCSS
|
||||
* ═══════════════════════════════════════════════════════════
|
||||
* Toutes les variables de design système Lions.dev
|
||||
* Basé sur la Charte Graphique Lions.dev v1.0.0
|
||||
*
|
||||
* Version: 1.0.0
|
||||
* Date: 1er Janvier 2026
|
||||
* Author: Lions Development Team
|
||||
* ═══════════════════════════════════════════════════════════
|
||||
*/
|
||||
|
||||
// ============================================
|
||||
// 🎨 PALETTE DE COULEURS LIONS.DEV
|
||||
// ============================================
|
||||
|
||||
// 🔵 Lions Blue (Couleur signature)
|
||||
$lions-blue-50: #E8F4FD !default;
|
||||
$lions-blue-100: #C3E2FA !default;
|
||||
$lions-blue-200: #9BCEF7 !default;
|
||||
$lions-blue-300: #72BAF4 !default;
|
||||
$lions-blue-400: #50AAF1 !default;
|
||||
$lions-blue-500: #2D9BEF !default; // Base - Lions Blue
|
||||
$lions-blue-600: #2889D6 !default;
|
||||
$lions-blue-700: #2275BC !default;
|
||||
$lions-blue-800: #1C61A3 !default;
|
||||
$lions-blue-900: #164D89 !default;
|
||||
|
||||
// 🟠 Lions Gold (Couleur d'accentuation)
|
||||
$lions-gold-50: #FFF9E6 !default;
|
||||
$lions-gold-100: #FFEFB3 !default;
|
||||
$lions-gold-200: #FFE580 !default;
|
||||
$lions-gold-300: #FFDB4D !default;
|
||||
$lions-gold-400: #FFD11A !default;
|
||||
$lions-gold-500: #FFC700 !default; // Base - Lions Gold
|
||||
$lions-gold-600: #E6B300 !default;
|
||||
$lions-gold-700: #CC9F00 !default;
|
||||
$lions-gold-800: #B38B00 !default;
|
||||
$lions-gold-900: #997700 !default;
|
||||
|
||||
// ✅ Success (Vert Lions)
|
||||
$lions-success-50: #E8F8F0 !default;
|
||||
$lions-success-100: #C3ECD5 !default;
|
||||
$lions-success-200: #9CE0BA !default;
|
||||
$lions-success-300: #74D39F !default;
|
||||
$lions-success-400: #4DC784 !default;
|
||||
$lions-success-500: #22BB69 !default; // Base
|
||||
$lions-success-600: #1FA85F !default;
|
||||
$lions-success-700: #1B9555 !default;
|
||||
$lions-success-800: #18824B !default;
|
||||
$lions-success-900: #146F41 !default;
|
||||
|
||||
// ⚠️ Warning (Orange Lions)
|
||||
$lions-warning-50: #FFF3E0 !default;
|
||||
$lions-warning-100: #FFE0B3 !default;
|
||||
$lions-warning-200: #FFCC80 !default;
|
||||
$lions-warning-300: #FFB84D !default;
|
||||
$lions-warning-400: #FFA826 !default;
|
||||
$lions-warning-500: #FF9800 !default; // Base
|
||||
$lions-warning-600: #F08A00 !default;
|
||||
$lions-warning-700: #E07B00 !default;
|
||||
$lions-warning-800: #D16D00 !default;
|
||||
$lions-warning-900: #C15E00 !default;
|
||||
|
||||
// ❌ Danger (Rouge Lions)
|
||||
$lions-danger-50: #FEEBEE !default;
|
||||
$lions-danger-100: #FCCDD2 !default;
|
||||
$lions-danger-200: #FAA6AF !default;
|
||||
$lions-danger-300: #F87F8C !default;
|
||||
$lions-danger-400: #F65870 !default;
|
||||
$lions-danger-500: #F44336 !default; // Base
|
||||
$lions-danger-600: #E93D31 !default;
|
||||
$lions-danger-700: #DD342C !default;
|
||||
$lions-danger-800: #D22C27 !default;
|
||||
$lions-danger-900: #C41F1E !default;
|
||||
|
||||
// ℹ️ Info (Cyan Lions)
|
||||
$lions-info-50: #E0F7FA !default;
|
||||
$lions-info-100: #B3EBF3 !default;
|
||||
$lions-info-200: #80DEEB !default;
|
||||
$lions-info-300: #4DD1E3 !default;
|
||||
$lions-info-400: #26C6DC !default;
|
||||
$lions-info-500: #00BCD4 !default; // Base
|
||||
$lions-info-600: #00AAC1 !default;
|
||||
$lions-info-700: #0098AE !default;
|
||||
$lions-info-800: #00869B !default;
|
||||
$lions-info-900: #007488 !default;
|
||||
|
||||
// 🎨 Surface (Neutral)
|
||||
$lions-white: #FFFFFF !default;
|
||||
$lions-gray-50: #FAFBFC !default;
|
||||
$lions-gray-100: #F4F6F8 !default;
|
||||
$lions-gray-200: #E8ECEF !default;
|
||||
$lions-gray-300: #D2D9DF !default;
|
||||
$lions-gray-400: #B0B9C2 !default;
|
||||
$lions-gray-500: #8995A1 !default; // Base
|
||||
$lions-gray-600: #68737F !default;
|
||||
$lions-gray-700: #4F5861 !default;
|
||||
$lions-gray-800: #363E46 !default;
|
||||
$lions-gray-900: #1F252B !default;
|
||||
$lions-black: #0A0D0F !default;
|
||||
|
||||
// Text colors
|
||||
$lions-text-primary: $lions-gray-900 !default;
|
||||
$lions-text-secondary: $lions-gray-600 !default;
|
||||
$lions-text-disabled: $lions-gray-400 !default;
|
||||
$lions-text-inverse: $lions-white !default;
|
||||
|
||||
// ============================================
|
||||
// 🔤 TYPOGRAPHIE
|
||||
// ============================================
|
||||
|
||||
// Font families
|
||||
$lions-font-family-primary: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif !default;
|
||||
$lions-font-family-headings: 'Inter', system-ui, -apple-system, sans-serif !default;
|
||||
$lions-font-family-mono: 'JetBrains Mono', 'Fira Code', 'Consolas', monospace !default;
|
||||
|
||||
// Font sizes (Ratio 1.250 - Major Third)
|
||||
$lions-font-xs: 0.750rem !default; // 12px
|
||||
$lions-font-sm: 0.875rem !default; // 14px
|
||||
$lions-font-base: 1.000rem !default; // 16px (base)
|
||||
$lions-font-lg: 1.125rem !default; // 18px
|
||||
$lions-font-xl: 1.250rem !default; // 20px
|
||||
$lions-font-2xl: 1.563rem !default; // 25px
|
||||
$lions-font-3xl: 1.953rem !default; // 31px
|
||||
$lions-font-4xl: 2.441rem !default; // 39px
|
||||
$lions-font-5xl: 3.052rem !default; // 49px
|
||||
|
||||
// Font weights
|
||||
$lions-font-light: 300 !default;
|
||||
$lions-font-regular: 400 !default;
|
||||
$lions-font-medium: 500 !default;
|
||||
$lions-font-semibold: 600 !default;
|
||||
$lions-font-bold: 700 !default;
|
||||
$lions-font-extrabold: 800 !default;
|
||||
$lions-font-black: 900 !default;
|
||||
|
||||
// Line heights
|
||||
$lions-line-height-tight: 1.25 !default;
|
||||
$lions-line-height-normal: 1.5 !default;
|
||||
$lions-line-height-relaxed: 1.75 !default;
|
||||
$lions-line-height-loose: 2.0 !default;
|
||||
|
||||
// ============================================
|
||||
// 📏 ESPACEMENTS (Système 4px)
|
||||
// ============================================
|
||||
|
||||
$lions-spacing-0: 0 !default;
|
||||
$lions-spacing-1: 0.25rem !default; // 4px
|
||||
$lions-spacing-2: 0.5rem !default; // 8px
|
||||
$lions-spacing-3: 0.75rem !default; // 12px
|
||||
$lions-spacing-4: 1rem !default; // 16px (base)
|
||||
$lions-spacing-5: 1.25rem !default; // 20px
|
||||
$lions-spacing-6: 1.5rem !default; // 24px
|
||||
$lions-spacing-8: 2rem !default; // 32px
|
||||
$lions-spacing-10: 2.5rem !default; // 40px
|
||||
$lions-spacing-12: 3rem !default; // 48px
|
||||
$lions-spacing-16: 4rem !default; // 64px
|
||||
$lions-spacing-20: 5rem !default; // 80px
|
||||
$lions-spacing-24: 6rem !default; // 96px
|
||||
|
||||
// ============================================
|
||||
// 🔲 COMPOSANTS - DIMENSIONS
|
||||
// ============================================
|
||||
|
||||
// Boutons
|
||||
$lions-button-height-sm: 2rem !default; // 32px
|
||||
$lions-button-height-base: 2.5rem !default; // 40px
|
||||
$lions-button-height-lg: 3rem !default; // 48px
|
||||
$lions-button-padding-x-sm: 0.75rem !default; // 12px
|
||||
$lions-button-padding-x-base: 1rem !default; // 16px
|
||||
$lions-button-padding-x-lg: 1.5rem !default; // 24px
|
||||
$lions-button-border-radius: 0.5rem !default; // 8px
|
||||
$lions-button-border-width: 2px !default;
|
||||
|
||||
// Champs de formulaire
|
||||
$lions-input-height-sm: 2rem !default; // 32px
|
||||
$lions-input-height-base: 2.75rem !default; // 44px
|
||||
$lions-input-height-lg: 3.25rem !default; // 52px
|
||||
$lions-input-padding-x: 1rem !default; // 16px
|
||||
$lions-input-padding-y: 0.75rem !default; // 12px
|
||||
$lions-input-border-radius: 0.5rem !default; // 8px
|
||||
$lions-input-border-width: 1px !default;
|
||||
|
||||
// Cards & Panels
|
||||
$lions-card-padding: $lions-spacing-6 !default; // 24px
|
||||
$lions-card-border-radius: 0.75rem !default; // 12px
|
||||
$lions-card-border-width: 1px !default;
|
||||
|
||||
// Tables
|
||||
$lions-table-row-padding: $lions-spacing-4 !default; // 16px
|
||||
$lions-table-header-height: 3rem !default; // 48px
|
||||
$lions-table-row-height: 2.75rem !default; // 44px
|
||||
|
||||
// ============================================
|
||||
// 🌈 ANIMATIONS & TRANSITIONS
|
||||
// ============================================
|
||||
|
||||
// Durées
|
||||
$lions-transition-fast: 150ms !default;
|
||||
$lions-transition-base: 250ms !default;
|
||||
$lions-transition-slow: 350ms !default;
|
||||
|
||||
// Easing functions
|
||||
$lions-ease-in-out: cubic-bezier(0.4, 0, 0.2, 1) !default;
|
||||
$lions-ease-out: cubic-bezier(0.0, 0, 0.2, 1) !default;
|
||||
$lions-ease-in: cubic-bezier(0.4, 0, 1, 1) !default;
|
||||
$lions-ease-smooth: cubic-bezier(0.25, 0.1, 0.25, 1) !default;
|
||||
|
||||
// ============================================
|
||||
// 🎯 OMBRES (Elevation System)
|
||||
// ============================================
|
||||
|
||||
$lions-shadow-xs: 0 1px 2px 0 rgba($lions-black, 0.05) !default;
|
||||
$lions-shadow-sm: 0 1px 3px 0 rgba($lions-black, 0.1), 0 1px 2px 0 rgba($lions-black, 0.06) !default;
|
||||
$lions-shadow-base: 0 4px 6px -1px rgba($lions-black, 0.1), 0 2px 4px -1px rgba($lions-black, 0.06) !default;
|
||||
$lions-shadow-md: 0 10px 15px -3px rgba($lions-black, 0.1), 0 4px 6px -2px rgba($lions-black, 0.05) !default;
|
||||
$lions-shadow-lg: 0 20px 25px -5px rgba($lions-black, 0.1), 0 10px 10px -5px rgba($lions-black, 0.04) !default;
|
||||
$lions-shadow-xl: 0 25px 50px -12px rgba($lions-black, 0.25) !default;
|
||||
$lions-shadow-2xl: 0 30px 60px -15px rgba($lions-black, 0.3) !default;
|
||||
|
||||
// Shadows colorées
|
||||
$lions-shadow-blue: 0 4px 16px rgba($lions-blue-500, 0.25) !default;
|
||||
$lions-shadow-gold: 0 4px 16px rgba($lions-gold-500, 0.25) !default;
|
||||
|
||||
// ============================================
|
||||
// 🔄 BREAKPOINTS (Mobile-First)
|
||||
// ============================================
|
||||
|
||||
$lions-breakpoint-xs: 0 !default;
|
||||
$lions-breakpoint-sm: 576px !default;
|
||||
$lions-breakpoint-md: 768px !default;
|
||||
$lions-breakpoint-lg: 1024px !default;
|
||||
$lions-breakpoint-xl: 1280px !default;
|
||||
$lions-breakpoint-2xl: 1536px !default;
|
||||
|
||||
// ============================================
|
||||
// 🎨 Z-INDEX LAYERS
|
||||
// ============================================
|
||||
|
||||
$lions-z-dropdown: 1000 !default;
|
||||
$lions-z-sticky: 1020 !default;
|
||||
$lions-z-fixed: 1030 !default;
|
||||
$lions-z-modal-backdrop: 1040 !default;
|
||||
$lions-z-modal: 1050 !default;
|
||||
$lions-z-popover: 1060 !default;
|
||||
$lions-z-tooltip: 1070 !default;
|
||||
|
||||
// ============================================
|
||||
// 🔲 BORDER RADIUS
|
||||
// ============================================
|
||||
|
||||
$lions-radius-none: 0 !default;
|
||||
$lions-radius-sm: 0.25rem !default; // 4px
|
||||
$lions-radius-base: 0.5rem !default; // 8px
|
||||
$lions-radius-md: 0.75rem !default; // 12px
|
||||
$lions-radius-lg: 1rem !default; // 16px
|
||||
$lions-radius-xl: 1.5rem !default; // 24px
|
||||
$lions-radius-2xl: 2rem !default; // 32px
|
||||
$lions-radius-full: 9999px !default; // Pill shape
|
||||
|
||||
// ============================================
|
||||
// 📐 GRID SYSTEM
|
||||
// ============================================
|
||||
|
||||
$lions-grid-columns: 12 !default;
|
||||
$lions-grid-gutter-width: $lions-spacing-6 !default; // 24px
|
||||
$lions-container-max-width-sm: 540px !default;
|
||||
$lions-container-max-width-md: 720px !default;
|
||||
$lions-container-max-width-lg: 960px !default;
|
||||
$lions-container-max-width-xl: 1140px !default;
|
||||
$lions-container-max-width-2xl: 1320px !default;
|
||||
@@ -0,0 +1,451 @@
|
||||
/*
|
||||
* ═══════════════════════════════════════════════════════════
|
||||
* Lions.dev - Breadcrumb & Steps Component Styles
|
||||
* ═══════════════════════════════════════════════════════════
|
||||
* Styles pour Breadcrumb et Steps (fil d'Ariane et étapes)
|
||||
*
|
||||
* Version: 1.0.0
|
||||
* Date: 2 Janvier 2026
|
||||
* Author: Lions Development Team
|
||||
* Priority: MOYENNE #37-38
|
||||
* ═══════════════════════════════════════════════════════════
|
||||
*/
|
||||
|
||||
@import '../variables';
|
||||
@import '../mixins';
|
||||
|
||||
// ============================================
|
||||
// 🍞 BREADCRUMB STYLES
|
||||
// ============================================
|
||||
|
||||
.ui-breadcrumb {
|
||||
background: rgba($lions-gray-100, 0.5);
|
||||
border: 1px solid $lions-gray-300;
|
||||
@include lions-rounded('md');
|
||||
padding: $lions-spacing-3 $lions-spacing-5;
|
||||
@include lions-shadow('sm');
|
||||
|
||||
.ui-breadcrumb-list {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
gap: $lions-spacing-2;
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.ui-breadcrumb-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: $lions-spacing-2;
|
||||
|
||||
.ui-menuitem-link {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: $lions-spacing-2;
|
||||
padding: $lions-spacing-1 $lions-spacing-2;
|
||||
@include lions-rounded('sm');
|
||||
color: $lions-gray-600;
|
||||
text-decoration: none;
|
||||
@include lions-font-size('sm');
|
||||
@include lions-font-weight('medium');
|
||||
@include lions-transition();
|
||||
|
||||
.ui-menuitem-icon {
|
||||
font-size: 1rem;
|
||||
color: $lions-gray-500;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background: rgba($lions-blue-50, 0.6);
|
||||
color: $lions-blue-700;
|
||||
|
||||
.ui-menuitem-icon {
|
||||
color: $lions-blue-600;
|
||||
}
|
||||
}
|
||||
|
||||
&:focus-visible {
|
||||
@include lions-focus-ring();
|
||||
}
|
||||
}
|
||||
|
||||
// Separator
|
||||
.ui-breadcrumb-chevron {
|
||||
color: $lions-gray-400;
|
||||
font-size: 0.875rem;
|
||||
@include lions-transition();
|
||||
}
|
||||
|
||||
// Last item (current page)
|
||||
&:last-child {
|
||||
.ui-menuitem-link {
|
||||
color: $lions-text-primary;
|
||||
@include lions-font-weight('semibold');
|
||||
pointer-events: none;
|
||||
|
||||
.ui-menuitem-icon {
|
||||
color: $lions-blue-600;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Home icon
|
||||
&:first-child {
|
||||
.ui-menuitem-link {
|
||||
.ui-menuitem-icon {
|
||||
font-size: 1.125rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Compact breadcrumb
|
||||
.ui-breadcrumb-compact {
|
||||
padding: $lions-spacing-2 $lions-spacing-4;
|
||||
|
||||
.ui-menuitem-link {
|
||||
padding: $lions-spacing-1;
|
||||
@include lions-font-size('xs');
|
||||
}
|
||||
|
||||
.ui-breadcrumb-chevron {
|
||||
font-size: 0.75rem;
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// 📊 STEPS STYLES
|
||||
// ============================================
|
||||
|
||||
.ui-steps {
|
||||
position: relative;
|
||||
padding: $lions-spacing-4 0;
|
||||
|
||||
.ui-steps-list {
|
||||
display: flex;
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.ui-steps-item {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
position: relative;
|
||||
text-align: center;
|
||||
|
||||
// Connector line
|
||||
&::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 1.25rem;
|
||||
left: 50%;
|
||||
right: -50%;
|
||||
height: 2px;
|
||||
background: $lions-gray-300;
|
||||
z-index: 0;
|
||||
@include lions-transition();
|
||||
}
|
||||
|
||||
&:last-child::before {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.ui-menuitem-link {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: $lions-spacing-3;
|
||||
text-decoration: none;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
@include lions-transition();
|
||||
|
||||
.ui-steps-number {
|
||||
width: 2.5rem;
|
||||
height: 2.5rem;
|
||||
@include lions-rounded('full');
|
||||
@include lions-flex-center;
|
||||
background: $lions-white;
|
||||
border: 2px solid $lions-gray-300;
|
||||
color: $lions-gray-600;
|
||||
@include lions-font-weight('semibold');
|
||||
@include lions-font-size('base');
|
||||
@include lions-shadow('sm');
|
||||
@include lions-transition();
|
||||
}
|
||||
|
||||
.ui-steps-title {
|
||||
@include lions-font-size('sm');
|
||||
@include lions-font-weight('medium');
|
||||
color: $lions-gray-600;
|
||||
@include lions-transition();
|
||||
max-width: 150px;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
.ui-steps-number {
|
||||
border-color: $lions-blue-400;
|
||||
transform: scale(1.05);
|
||||
}
|
||||
|
||||
.ui-steps-title {
|
||||
color: $lions-blue-700;
|
||||
}
|
||||
}
|
||||
|
||||
&:focus-visible {
|
||||
.ui-steps-number {
|
||||
@include lions-focus-ring();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Completed state
|
||||
&.ui-steps-completed {
|
||||
&::before {
|
||||
background: $lions-success-500;
|
||||
}
|
||||
|
||||
.ui-menuitem-link {
|
||||
.ui-steps-number {
|
||||
@include lions-gradient-success;
|
||||
border-color: $lions-success-600;
|
||||
color: $lions-white;
|
||||
|
||||
.pi {
|
||||
font-size: 1rem;
|
||||
}
|
||||
}
|
||||
|
||||
.ui-steps-title {
|
||||
color: $lions-success-700;
|
||||
@include lions-font-weight('semibold');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Active/Current state
|
||||
&.ui-steps-current {
|
||||
.ui-menuitem-link {
|
||||
.ui-steps-number {
|
||||
@include lions-gradient-blue;
|
||||
border-color: $lions-blue-600;
|
||||
color: $lions-white;
|
||||
@include lions-shadow('md');
|
||||
box-shadow: 0 0 0 4px rgba($lions-blue-500, 0.2), $lions-shadow-md;
|
||||
}
|
||||
|
||||
.ui-steps-title {
|
||||
color: $lions-blue-700;
|
||||
@include lions-font-weight('bold');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Disabled/Future state
|
||||
&.ui-steps-disabled {
|
||||
.ui-menuitem-link {
|
||||
pointer-events: none;
|
||||
|
||||
.ui-steps-number {
|
||||
background: $lions-gray-100;
|
||||
border-color: $lions-gray-300;
|
||||
color: $lions-gray-400;
|
||||
}
|
||||
|
||||
.ui-steps-title {
|
||||
color: $lions-gray-400;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Vertical steps
|
||||
&.ui-steps-vertical {
|
||||
.ui-steps-list {
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.ui-steps-item {
|
||||
flex-direction: row;
|
||||
align-items: flex-start;
|
||||
text-align: left;
|
||||
width: 100%;
|
||||
padding: $lions-spacing-4 0;
|
||||
|
||||
&::before {
|
||||
top: 3.5rem;
|
||||
bottom: -$lions-spacing-4;
|
||||
left: 1.25rem;
|
||||
right: auto;
|
||||
width: 2px;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
padding-bottom: 0;
|
||||
|
||||
&::before {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.ui-menuitem-link {
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
gap: $lions-spacing-4;
|
||||
}
|
||||
|
||||
.ui-steps-title {
|
||||
text-align: left;
|
||||
max-width: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Readonly steps
|
||||
&.ui-steps-readonly {
|
||||
.ui-steps-item .ui-menuitem-link {
|
||||
pointer-events: none;
|
||||
cursor: default;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// 📱 RESPONSIVE ADJUSTMENTS
|
||||
// ============================================
|
||||
|
||||
@media (max-width: $lions-breakpoint-md) {
|
||||
.ui-breadcrumb {
|
||||
.ui-breadcrumb-list {
|
||||
gap: $lions-spacing-1;
|
||||
}
|
||||
|
||||
.ui-menuitem-link {
|
||||
padding: $lions-spacing-1;
|
||||
@include lions-font-size('xs');
|
||||
}
|
||||
|
||||
.ui-breadcrumb-chevron {
|
||||
font-size: 0.75rem;
|
||||
}
|
||||
}
|
||||
|
||||
.ui-steps {
|
||||
// Switch to vertical on mobile
|
||||
.ui-steps-list {
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.ui-steps-item {
|
||||
flex-direction: row;
|
||||
align-items: flex-start;
|
||||
text-align: left;
|
||||
width: 100%;
|
||||
padding: $lions-spacing-3 0;
|
||||
|
||||
&::before {
|
||||
top: 3rem;
|
||||
bottom: 0;
|
||||
left: 1.25rem;
|
||||
right: auto;
|
||||
width: 2px;
|
||||
height: calc(100% - 2.5rem);
|
||||
}
|
||||
|
||||
&:last-child::before {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.ui-menuitem-link {
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
gap: $lions-spacing-3;
|
||||
}
|
||||
|
||||
.ui-steps-number {
|
||||
width: 2rem;
|
||||
height: 2rem;
|
||||
@include lions-font-size('sm');
|
||||
}
|
||||
|
||||
.ui-steps-title {
|
||||
text-align: left;
|
||||
max-width: none;
|
||||
@include lions-font-size('xs');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// ♿ ACCESSIBILITY ENHANCEMENTS
|
||||
// ============================================
|
||||
|
||||
.ui-breadcrumb,
|
||||
.ui-steps {
|
||||
// Focus visible
|
||||
.ui-menuitem-link:focus-visible {
|
||||
@include lions-focus-ring();
|
||||
}
|
||||
|
||||
// High contrast mode
|
||||
@media (prefers-contrast: high) {
|
||||
border-width: 2px !important;
|
||||
|
||||
.ui-menuitem-link,
|
||||
.ui-steps-number {
|
||||
border-width: 2px !important;
|
||||
}
|
||||
}
|
||||
|
||||
// Reduced motion
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
* {
|
||||
transition: none !important;
|
||||
animation: none !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// 🎨 UTILITY CLASSES
|
||||
// ============================================
|
||||
|
||||
// Large steps
|
||||
.ui-steps-lg {
|
||||
.ui-steps-number {
|
||||
width: 3rem !important;
|
||||
height: 3rem !important;
|
||||
@include lions-font-size('lg');
|
||||
}
|
||||
|
||||
.ui-steps-title {
|
||||
@include lions-font-size('base');
|
||||
}
|
||||
}
|
||||
|
||||
// Compact steps
|
||||
.ui-steps-sm {
|
||||
padding: $lions-spacing-2 0;
|
||||
|
||||
.ui-steps-number {
|
||||
width: 2rem !important;
|
||||
height: 2rem !important;
|
||||
@include lions-font-size('sm');
|
||||
}
|
||||
|
||||
.ui-steps-title {
|
||||
@include lions-font-size('xs');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,442 @@
|
||||
/*
|
||||
* ═══════════════════════════════════════════════════════════
|
||||
* Lions.dev - Button Component Styles
|
||||
* ═══════════════════════════════════════════════════════════
|
||||
* Amélioration visuelle complète des boutons avec identité Lions.dev
|
||||
*
|
||||
* Version: 1.0.0
|
||||
* Date: 1er Janvier 2026
|
||||
* Author: Lions Development Team
|
||||
* Priority: CRITIQUE #1
|
||||
* ═══════════════════════════════════════════════════════════
|
||||
*/
|
||||
|
||||
@import '../variables';
|
||||
@import '../mixins';
|
||||
|
||||
// ============================================
|
||||
// 🎯 BASE BUTTON STYLES
|
||||
// ============================================
|
||||
|
||||
.ui-button {
|
||||
@include lions-button-base;
|
||||
@include lions-button-size('base');
|
||||
@include lions-button-solid($lions-blue-500, $lions-blue-700);
|
||||
|
||||
// Override PrimeFaces defaults
|
||||
font-family: $lions-font-family-primary !important;
|
||||
|
||||
// Icon spacing
|
||||
.ui-button-icon-left {
|
||||
margin-right: $lions-spacing-2;
|
||||
}
|
||||
|
||||
.ui-button-icon-right {
|
||||
margin-left: $lions-spacing-2;
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// 🎨 SEVERITY VARIANTS (Solid buttons)
|
||||
// ============================================
|
||||
|
||||
// Primary (Lions Blue) - Default
|
||||
.ui-button-primary,
|
||||
.ui-button:not([class*="ui-button-"]) {
|
||||
@include lions-button-solid($lions-blue-500, $lions-blue-700);
|
||||
}
|
||||
|
||||
// Secondary (Gray)
|
||||
.ui-button-secondary {
|
||||
@include lions-button-solid($lions-gray-500, $lions-gray-700);
|
||||
}
|
||||
|
||||
// Success (Green)
|
||||
.ui-button-success {
|
||||
@include lions-button-solid($lions-success-500, $lions-success-700);
|
||||
}
|
||||
|
||||
// Info (Cyan)
|
||||
.ui-button-info {
|
||||
@include lions-button-solid($lions-info-500, $lions-info-700);
|
||||
}
|
||||
|
||||
// Warning (Orange)
|
||||
.ui-button-warning {
|
||||
@include lions-button-solid($lions-warning-500, $lions-warning-700);
|
||||
}
|
||||
|
||||
// Help (Lions Gold - Premium)
|
||||
.ui-button-help {
|
||||
@include lions-button-solid($lions-gold-500, $lions-gold-700);
|
||||
|
||||
// Extra visual impact for premium actions
|
||||
font-weight: $lions-font-bold;
|
||||
letter-spacing: 0.025em;
|
||||
}
|
||||
|
||||
// Danger (Red)
|
||||
.ui-button-danger {
|
||||
@include lions-button-solid($lions-danger-500, $lions-danger-700);
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// 📝 OUTLINED VARIANT
|
||||
// ============================================
|
||||
|
||||
.ui-button.ui-button-outlined {
|
||||
&.ui-button-primary,
|
||||
&:not([class*="ui-button-"]) {
|
||||
@include lions-button-outlined($lions-blue-500);
|
||||
}
|
||||
|
||||
&.ui-button-secondary {
|
||||
@include lions-button-outlined($lions-gray-600);
|
||||
}
|
||||
|
||||
&.ui-button-success {
|
||||
@include lions-button-outlined($lions-success-500);
|
||||
}
|
||||
|
||||
&.ui-button-info {
|
||||
@include lions-button-outlined($lions-info-500);
|
||||
}
|
||||
|
||||
&.ui-button-warning {
|
||||
@include lions-button-outlined($lions-warning-500);
|
||||
}
|
||||
|
||||
&.ui-button-help {
|
||||
@include lions-button-outlined($lions-gold-600);
|
||||
}
|
||||
|
||||
&.ui-button-danger {
|
||||
@include lions-button-outlined($lions-danger-500);
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// 🔗 TEXT VARIANT
|
||||
// ============================================
|
||||
|
||||
.ui-button.ui-button-text {
|
||||
&.ui-button-primary,
|
||||
&:not([class*="ui-button-"]) {
|
||||
@include lions-button-text($lions-blue-500);
|
||||
}
|
||||
|
||||
&.ui-button-secondary {
|
||||
@include lions-button-text($lions-gray-600);
|
||||
}
|
||||
|
||||
&.ui-button-success {
|
||||
@include lions-button-text($lions-success-500);
|
||||
}
|
||||
|
||||
&.ui-button-info {
|
||||
@include lions-button-text($lions-info-500);
|
||||
}
|
||||
|
||||
&.ui-button-warning {
|
||||
@include lions-button-text($lions-warning-500);
|
||||
}
|
||||
|
||||
&.ui-button-help {
|
||||
@include lions-button-text($lions-gold-600);
|
||||
}
|
||||
|
||||
&.ui-button-danger {
|
||||
@include lions-button-text($lions-danger-500);
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// 🔗 LINK VARIANT (Text sans background hover)
|
||||
// ============================================
|
||||
|
||||
.ui-button.ui-button-link {
|
||||
background: transparent !important;
|
||||
border: none !important;
|
||||
box-shadow: none !important;
|
||||
padding: 0 !important;
|
||||
height: auto !important;
|
||||
color: $lions-blue-500;
|
||||
text-decoration: underline;
|
||||
text-underline-offset: 2px;
|
||||
|
||||
&:hover {
|
||||
color: $lions-blue-700;
|
||||
transform: none;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
&:active {
|
||||
color: $lions-blue-800;
|
||||
transform: none;
|
||||
}
|
||||
|
||||
&:focus-visible {
|
||||
@include lions-focus-ring($lions-blue-500);
|
||||
}
|
||||
|
||||
// Severity variants for link
|
||||
&.ui-button-secondary {
|
||||
color: $lions-gray-700;
|
||||
&:hover { color: $lions-gray-900; }
|
||||
}
|
||||
|
||||
&.ui-button-success {
|
||||
color: $lions-success-500;
|
||||
&:hover { color: $lions-success-700; }
|
||||
}
|
||||
|
||||
&.ui-button-danger {
|
||||
color: $lions-danger-500;
|
||||
&:hover { color: $lions-danger-700; }
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// 🔵 ROUNDED VARIANT (Pill shape)
|
||||
// ============================================
|
||||
|
||||
.ui-button.ui-button-rounded {
|
||||
border-radius: $lions-radius-full !important;
|
||||
padding-left: $lions-spacing-6 !important;
|
||||
padding-right: $lions-spacing-6 !important;
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// 📐 RAISED VARIANT (Elevated)
|
||||
// ============================================
|
||||
|
||||
.ui-button.ui-button-raised {
|
||||
@include lions-shadow('lg');
|
||||
|
||||
&:hover {
|
||||
@include lions-shadow('xl');
|
||||
transform: translateY(-3px);
|
||||
}
|
||||
|
||||
&:active {
|
||||
@include lions-shadow('md');
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// 📏 SIZE VARIANTS
|
||||
// ============================================
|
||||
|
||||
.ui-button {
|
||||
// Small buttons
|
||||
&.ui-button-sm,
|
||||
&[data-size="sm"] {
|
||||
@include lions-button-size('sm');
|
||||
|
||||
.ui-button-icon-left {
|
||||
margin-right: $lions-spacing-1;
|
||||
}
|
||||
|
||||
.ui-button-icon-right {
|
||||
margin-left: $lions-spacing-1;
|
||||
}
|
||||
}
|
||||
|
||||
// Large buttons
|
||||
&.ui-button-lg,
|
||||
&[data-size="lg"] {
|
||||
@include lions-button-size('lg');
|
||||
|
||||
.ui-button-icon-left {
|
||||
margin-right: $lions-spacing-3;
|
||||
}
|
||||
|
||||
.ui-button-icon-right {
|
||||
margin-left: $lions-spacing-3;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// 🔄 LOADING STATE
|
||||
// ============================================
|
||||
|
||||
.ui-button {
|
||||
&.ui-state-loading,
|
||||
&[aria-busy="true"] {
|
||||
position: relative;
|
||||
pointer-events: none;
|
||||
color: transparent !important;
|
||||
|
||||
&::after {
|
||||
content: '';
|
||||
@include lions-spinner(1rem, $lions-white);
|
||||
@include lions-absolute-center;
|
||||
}
|
||||
}
|
||||
|
||||
// Loading state for outlined/text buttons
|
||||
&.ui-button-outlined,
|
||||
&.ui-button-text {
|
||||
&.ui-state-loading,
|
||||
&[aria-busy="true"] {
|
||||
&::after {
|
||||
border-top-color: currentColor;
|
||||
border-color: rgba(currentColor, 0.25);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// ❌ DISABLED STATE
|
||||
// ============================================
|
||||
|
||||
.ui-button {
|
||||
&:disabled,
|
||||
&.ui-state-disabled {
|
||||
opacity: 0.5 !important;
|
||||
cursor: not-allowed !important;
|
||||
pointer-events: none !important;
|
||||
transform: none !important;
|
||||
box-shadow: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// 🎖️ BADGE SUPPORT
|
||||
// ============================================
|
||||
|
||||
.ui-button {
|
||||
.ui-badge {
|
||||
position: absolute;
|
||||
top: -$lions-spacing-2;
|
||||
right: -$lions-spacing-2;
|
||||
min-width: $lions-spacing-5;
|
||||
height: $lions-spacing-5;
|
||||
line-height: $lions-spacing-5;
|
||||
border-radius: $lions-radius-full;
|
||||
background: $lions-danger-500;
|
||||
color: $lions-white;
|
||||
font-size: $lions-font-xs;
|
||||
font-weight: $lions-font-bold;
|
||||
padding: 0 $lions-spacing-1;
|
||||
|
||||
&.ui-badge-success {
|
||||
background: $lions-success-500;
|
||||
}
|
||||
|
||||
&.ui-badge-warning {
|
||||
background: $lions-warning-500;
|
||||
}
|
||||
|
||||
&.ui-badge-info {
|
||||
background: $lions-info-500;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// 🎨 ICON-ONLY BUTTONS (Square)
|
||||
// ============================================
|
||||
|
||||
.ui-button {
|
||||
&.ui-button-icon-only {
|
||||
width: $lions-button-height-base;
|
||||
padding: 0 !important;
|
||||
@include lions-flex-center;
|
||||
|
||||
&.ui-button-sm,
|
||||
&[data-size="sm"] {
|
||||
width: $lions-button-height-sm;
|
||||
}
|
||||
|
||||
&.ui-button-lg,
|
||||
&[data-size="lg"] {
|
||||
width: $lions-button-height-lg;
|
||||
}
|
||||
|
||||
// Rounded icon-only buttons become circles
|
||||
&.ui-button-rounded {
|
||||
border-radius: $lions-radius-full !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// 🎯 BUTTON GROUP SUPPORT
|
||||
// ============================================
|
||||
|
||||
.ui-buttonset {
|
||||
display: inline-flex;
|
||||
gap: 0;
|
||||
|
||||
.ui-button {
|
||||
border-radius: 0;
|
||||
margin-left: -1px;
|
||||
|
||||
&:first-child {
|
||||
margin-left: 0;
|
||||
border-top-left-radius: $lions-radius-base;
|
||||
border-bottom-left-radius: $lions-radius-base;
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
border-top-right-radius: $lions-radius-base;
|
||||
border-bottom-right-radius: $lions-radius-base;
|
||||
}
|
||||
|
||||
&:hover,
|
||||
&:focus {
|
||||
z-index: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// ♿ ACCESSIBILITY ENHANCEMENTS
|
||||
// ============================================
|
||||
|
||||
.ui-button {
|
||||
// Ensure focus ring is always visible
|
||||
&:focus-visible {
|
||||
@include lions-focus-ring();
|
||||
}
|
||||
|
||||
// High contrast mode support
|
||||
@media (prefers-contrast: high) {
|
||||
border-width: 2px !important;
|
||||
}
|
||||
|
||||
// Reduced motion support
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
transition: none !important;
|
||||
|
||||
&:hover {
|
||||
transform: none !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// 📱 RESPONSIVE ADJUSTMENTS
|
||||
// ============================================
|
||||
|
||||
// Touch-friendly on mobile
|
||||
@media (hover: none) and (pointer: coarse) {
|
||||
.ui-button {
|
||||
min-height: 44px; // iOS touch target minimum
|
||||
|
||||
&.ui-button-sm {
|
||||
min-height: 40px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Smaller spacing on mobile
|
||||
@include lions-responsive-custom($lions-breakpoint-md) {
|
||||
.ui-buttonset {
|
||||
gap: $lions-spacing-1;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,509 @@
|
||||
/*
|
||||
* ═══════════════════════════════════════════════════════════
|
||||
* Lions.dev - Card & Panel Component Styles
|
||||
* ═══════════════════════════════════════════════════════════
|
||||
* Amélioration visuelle complète des cartes et panneaux avec identité Lions.dev
|
||||
*
|
||||
* Version: 1.0.0
|
||||
* Date: 1er Janvier 2026
|
||||
* Author: Lions Development Team
|
||||
* Priority: CRITIQUE #9-10
|
||||
* ═══════════════════════════════════════════════════════════
|
||||
*/
|
||||
|
||||
@import '../variables';
|
||||
@import '../mixins';
|
||||
|
||||
// ============================================
|
||||
// 🎴 CARD BASE STYLES
|
||||
// ============================================
|
||||
|
||||
.card {
|
||||
@include lions-card;
|
||||
@include lions-transition();
|
||||
|
||||
// Card with border accent
|
||||
&.card-border-top {
|
||||
border-top: 4px solid $lions-blue-500;
|
||||
}
|
||||
|
||||
&.card-border-left {
|
||||
border-left: 4px solid $lions-blue-500;
|
||||
}
|
||||
|
||||
// Severity colors for border
|
||||
&.card-primary {
|
||||
border-top-color: $lions-blue-500;
|
||||
border-left-color: $lions-blue-500;
|
||||
}
|
||||
|
||||
&.card-success {
|
||||
border-top-color: $lions-success-500;
|
||||
border-left-color: $lions-success-500;
|
||||
}
|
||||
|
||||
&.card-info {
|
||||
border-top-color: $lions-info-500;
|
||||
border-left-color: $lions-info-500;
|
||||
}
|
||||
|
||||
&.card-warning {
|
||||
border-top-color: $lions-warning-500;
|
||||
border-left-color: $lions-warning-500;
|
||||
}
|
||||
|
||||
&.card-danger {
|
||||
border-top-color: $lions-danger-500;
|
||||
border-left-color: $lions-danger-500;
|
||||
}
|
||||
|
||||
&.card-help {
|
||||
border-top-color: $lions-gold-500;
|
||||
border-left-color: $lions-gold-500;
|
||||
}
|
||||
|
||||
// Hover effect
|
||||
&.card-hover:hover {
|
||||
@include lions-shadow('lg');
|
||||
transform: translateY(-2px);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
// Clickable card
|
||||
&.card-clickable {
|
||||
cursor: pointer;
|
||||
@include lions-transition();
|
||||
|
||||
&:hover {
|
||||
@include lions-shadow('lg');
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
|
||||
&:active {
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// 📋 CARD HEADER
|
||||
// ============================================
|
||||
|
||||
.card-header {
|
||||
padding: $lions-spacing-4 $lions-spacing-6;
|
||||
border-bottom: 1px solid $lions-gray-200;
|
||||
background: $lions-gray-50;
|
||||
@include lions-rounded('md');
|
||||
border-bottom-left-radius: 0;
|
||||
border-bottom-right-radius: 0;
|
||||
|
||||
h5, h6 {
|
||||
margin: 0;
|
||||
@include lions-font-weight('bold');
|
||||
color: $lions-text-primary;
|
||||
}
|
||||
|
||||
.card-title {
|
||||
@include lions-font-size('lg');
|
||||
@include lions-font-weight('bold');
|
||||
color: $lions-text-primary;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.card-subtitle {
|
||||
@include lions-font-size('sm');
|
||||
color: $lions-text-secondary;
|
||||
margin: $lions-spacing-1 0 0 0;
|
||||
}
|
||||
|
||||
// Icon in header
|
||||
.card-icon {
|
||||
font-size: 1.5rem;
|
||||
color: $lions-blue-500;
|
||||
margin-right: $lions-spacing-3;
|
||||
}
|
||||
|
||||
// Actions in header (buttons, etc.)
|
||||
.card-actions {
|
||||
display: flex;
|
||||
gap: $lions-spacing-2;
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// 📄 CARD CONTENT
|
||||
// ============================================
|
||||
|
||||
.card-content {
|
||||
padding: $lions-spacing-6;
|
||||
color: $lions-text-primary;
|
||||
line-height: $lions-line-height-normal;
|
||||
|
||||
p:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Compact card content
|
||||
.card-compact .card-content {
|
||||
padding: $lions-spacing-4;
|
||||
}
|
||||
|
||||
// Large card content
|
||||
.card-large .card-content {
|
||||
padding: $lions-spacing-8;
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// 🦶 CARD FOOTER
|
||||
// ============================================
|
||||
|
||||
.card-footer {
|
||||
padding: $lions-spacing-4 $lions-spacing-6;
|
||||
border-top: 1px solid $lions-gray-200;
|
||||
background: $lions-gray-50;
|
||||
@include lions-rounded('md');
|
||||
border-top-left-radius: 0;
|
||||
border-top-right-radius: 0;
|
||||
|
||||
// Actions in footer
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
gap: $lions-spacing-2;
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// 📦 PANEL STYLES (Collapsible Card)
|
||||
// ============================================
|
||||
|
||||
.ui-panel {
|
||||
@include lions-card;
|
||||
@include lions-transition();
|
||||
|
||||
.ui-panel-titlebar {
|
||||
background: $lions-gray-50;
|
||||
border-bottom: 1px solid $lions-gray-200;
|
||||
padding: $lions-spacing-4 $lions-spacing-6;
|
||||
@include lions-rounded('md');
|
||||
border-bottom-left-radius: 0;
|
||||
border-bottom-right-radius: 0;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
|
||||
.ui-panel-title {
|
||||
@include lions-font-size('lg');
|
||||
@include lions-font-weight('bold');
|
||||
color: $lions-text-primary;
|
||||
margin: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: $lions-spacing-2;
|
||||
|
||||
// Icon support
|
||||
i {
|
||||
font-size: 1.25rem;
|
||||
color: $lions-blue-500;
|
||||
}
|
||||
}
|
||||
|
||||
.ui-panel-titlebar-icon {
|
||||
@include lions-button-base;
|
||||
width: 2rem;
|
||||
height: 2rem;
|
||||
padding: 0;
|
||||
@include lions-flex-center;
|
||||
background: transparent;
|
||||
border: none;
|
||||
color: $lions-gray-600;
|
||||
@include lions-transition();
|
||||
|
||||
&:hover {
|
||||
background: $lions-gray-200;
|
||||
color: $lions-blue-500;
|
||||
}
|
||||
|
||||
&:focus-visible {
|
||||
@include lions-focus-ring();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.ui-panel-content {
|
||||
padding: $lions-spacing-6;
|
||||
background: $lions-white;
|
||||
@include lions-rounded('md');
|
||||
border-top-left-radius: 0;
|
||||
border-top-right-radius: 0;
|
||||
}
|
||||
|
||||
// Panel states
|
||||
&.ui-panel-collapsed {
|
||||
.ui-panel-content {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.ui-panel-titlebar {
|
||||
@include lions-rounded('md');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// 🎨 PANEL SEVERITY VARIANTS
|
||||
// ============================================
|
||||
|
||||
.ui-panel-primary {
|
||||
.ui-panel-titlebar {
|
||||
background: linear-gradient(135deg, rgba($lions-blue-500, 0.1) 0%, rgba($lions-blue-700, 0.05) 100%);
|
||||
border-bottom-color: $lions-blue-200;
|
||||
|
||||
.ui-panel-title i {
|
||||
color: $lions-blue-500;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.ui-panel-success {
|
||||
.ui-panel-titlebar {
|
||||
background: linear-gradient(135deg, rgba($lions-success-500, 0.1) 0%, rgba($lions-success-700, 0.05) 100%);
|
||||
border-bottom-color: $lions-success-200;
|
||||
|
||||
.ui-panel-title i {
|
||||
color: $lions-success-500;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.ui-panel-info {
|
||||
.ui-panel-titlebar {
|
||||
background: linear-gradient(135deg, rgba($lions-info-500, 0.1) 0%, rgba($lions-info-700, 0.05) 100%);
|
||||
border-bottom-color: $lions-info-200;
|
||||
|
||||
.ui-panel-title i {
|
||||
color: $lions-info-500;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.ui-panel-warning {
|
||||
.ui-panel-titlebar {
|
||||
background: linear-gradient(135deg, rgba($lions-warning-500, 0.1) 0%, rgba($lions-warning-700, 0.05) 100%);
|
||||
border-bottom-color: $lions-warning-200;
|
||||
|
||||
.ui-panel-title i {
|
||||
color: $lions-warning-500;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.ui-panel-danger {
|
||||
.ui-panel-titlebar {
|
||||
background: linear-gradient(135deg, rgba($lions-danger-500, 0.1) 0%, rgba($lions-danger-700, 0.05) 100%);
|
||||
border-bottom-color: $lions-danger-200;
|
||||
|
||||
.ui-panel-title i {
|
||||
color: $lions-danger-500;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.ui-panel-help {
|
||||
.ui-panel-titlebar {
|
||||
background: linear-gradient(135deg, rgba($lions-gold-500, 0.15) 0%, rgba($lions-gold-700, 0.08) 100%);
|
||||
border-bottom-color: $lions-gold-200;
|
||||
|
||||
.ui-panel-title i {
|
||||
color: $lions-gold-600;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// 📐 CARD SIZES
|
||||
// ============================================
|
||||
|
||||
.card-compact {
|
||||
padding: $lions-spacing-4;
|
||||
|
||||
.card-header {
|
||||
padding: $lions-spacing-3 $lions-spacing-4;
|
||||
}
|
||||
|
||||
.card-content {
|
||||
padding: $lions-spacing-4;
|
||||
}
|
||||
|
||||
.card-footer {
|
||||
padding: $lions-spacing-3 $lions-spacing-4;
|
||||
}
|
||||
}
|
||||
|
||||
.card-large {
|
||||
padding: $lions-spacing-8;
|
||||
|
||||
.card-header {
|
||||
padding: $lions-spacing-6 $lions-spacing-8;
|
||||
}
|
||||
|
||||
.card-content {
|
||||
padding: $lions-spacing-8;
|
||||
}
|
||||
|
||||
.card-footer {
|
||||
padding: $lions-spacing-6 $lions-spacing-8;
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// 🎯 SPECIAL CARD VARIANTS
|
||||
// ============================================
|
||||
|
||||
// Outlined card (no fill)
|
||||
.card-outlined {
|
||||
background: transparent;
|
||||
border: 2px solid $lions-gray-300;
|
||||
box-shadow: none;
|
||||
|
||||
&:hover {
|
||||
border-color: $lions-blue-500;
|
||||
@include lions-shadow('sm');
|
||||
}
|
||||
}
|
||||
|
||||
// Elevated card (more shadow)
|
||||
.card-elevated {
|
||||
@include lions-shadow('xl');
|
||||
|
||||
&:hover {
|
||||
@include lions-shadow('2xl');
|
||||
}
|
||||
}
|
||||
|
||||
// Flat card (no shadow)
|
||||
.card-flat {
|
||||
box-shadow: none;
|
||||
border: 1px solid $lions-gray-200;
|
||||
}
|
||||
|
||||
// Gradient card backgrounds
|
||||
.card-gradient-blue {
|
||||
background: linear-gradient(135deg, rgba($lions-blue-500, 0.05) 0%, rgba($lions-blue-700, 0.02) 100%);
|
||||
}
|
||||
|
||||
.card-gradient-gold {
|
||||
background: linear-gradient(135deg, rgba($lions-gold-500, 0.08) 0%, rgba($lions-gold-700, 0.03) 100%);
|
||||
}
|
||||
|
||||
.card-gradient-success {
|
||||
background: linear-gradient(135deg, rgba($lions-success-500, 0.05) 0%, rgba($lions-success-700, 0.02) 100%);
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// 📊 CARD GRID / LIST
|
||||
// ============================================
|
||||
|
||||
.card-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
|
||||
gap: $lions-spacing-6;
|
||||
|
||||
// Responsive
|
||||
@media (max-width: $lions-breakpoint-md) {
|
||||
grid-template-columns: 1fr;
|
||||
gap: $lions-spacing-4;
|
||||
}
|
||||
}
|
||||
|
||||
.card-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: $lions-spacing-4;
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// 🖼️ CARD WITH IMAGE
|
||||
// ============================================
|
||||
|
||||
.card-image {
|
||||
width: 100%;
|
||||
height: 200px;
|
||||
object-fit: cover;
|
||||
@include lions-rounded('md');
|
||||
border-bottom-left-radius: 0;
|
||||
border-bottom-right-radius: 0;
|
||||
}
|
||||
|
||||
.card-image-container {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
@include lions-rounded('md');
|
||||
border-bottom-left-radius: 0;
|
||||
border-bottom-right-radius: 0;
|
||||
|
||||
img {
|
||||
width: 100%;
|
||||
height: 200px;
|
||||
object-fit: cover;
|
||||
@include lions-transition();
|
||||
}
|
||||
|
||||
&:hover img {
|
||||
transform: scale(1.05);
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// ♿ ACCESSIBILITY ENHANCEMENTS
|
||||
// ============================================
|
||||
|
||||
.card {
|
||||
// High contrast mode
|
||||
@media (prefers-contrast: high) {
|
||||
border-width: 2px !important;
|
||||
}
|
||||
|
||||
// Reduced motion
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
transition: none !important;
|
||||
|
||||
&:hover {
|
||||
transform: none !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.ui-panel {
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
.ui-panel-content {
|
||||
transition: none !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// 📱 RESPONSIVE ADJUSTMENTS
|
||||
// ============================================
|
||||
|
||||
@media (max-width: $lions-breakpoint-md) {
|
||||
.card,
|
||||
.ui-panel {
|
||||
.card-content,
|
||||
.ui-panel-content {
|
||||
padding: $lions-spacing-4;
|
||||
}
|
||||
|
||||
.card-header,
|
||||
.ui-panel-titlebar {
|
||||
padding: $lions-spacing-3 $lions-spacing-4;
|
||||
}
|
||||
|
||||
.card-footer {
|
||||
padding: $lions-spacing-3 $lions-spacing-4;
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,662 @@
|
||||
/*
|
||||
* ═══════════════════════════════════════════════════════════
|
||||
* Lions.dev - Chart Component Styles
|
||||
* ═══════════════════════════════════════════════════════════
|
||||
* Styles pour Charts (visualisation de données avec Chart.js)
|
||||
*
|
||||
* Version: 1.0.0
|
||||
* Date: 2 Janvier 2026
|
||||
* Author: Lions Development Team
|
||||
* Priority: MOYENNE #44
|
||||
* ═══════════════════════════════════════════════════════════
|
||||
*/
|
||||
|
||||
@import '../variables';
|
||||
@import '../mixins';
|
||||
|
||||
// ============================================
|
||||
// 📊 CHART CONTAINER STYLES
|
||||
// ============================================
|
||||
|
||||
.ui-chart {
|
||||
position: relative;
|
||||
display: block;
|
||||
width: 100%;
|
||||
@include lions-rounded('lg');
|
||||
background: $lions-white;
|
||||
padding: $lions-spacing-5;
|
||||
border: 1px solid $lions-gray-200;
|
||||
@include lions-shadow('sm');
|
||||
|
||||
// Canvas element
|
||||
canvas {
|
||||
max-width: 100%;
|
||||
height: auto !important;
|
||||
}
|
||||
|
||||
// Chart title
|
||||
.ui-chart-title {
|
||||
@include lions-font-size('lg');
|
||||
@include lions-font-weight('bold');
|
||||
color: $lions-text-primary;
|
||||
margin-bottom: $lions-spacing-4;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
// Chart subtitle
|
||||
.ui-chart-subtitle {
|
||||
@include lions-font-size('sm');
|
||||
@include lions-font-weight('medium');
|
||||
color: $lions-gray-600;
|
||||
margin-bottom: $lions-spacing-3;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
// Loading state
|
||||
&.ui-chart-loading {
|
||||
position: relative;
|
||||
min-height: 300px;
|
||||
|
||||
&::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background: rgba($lions-white, 0.9);
|
||||
@include lions-flex-center;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
&::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
width: 3rem;
|
||||
height: 3rem;
|
||||
border: 3px solid $lions-gray-200;
|
||||
border-top-color: $lions-blue-500;
|
||||
border-radius: 50%;
|
||||
animation: lions-chart-spin 1s linear infinite;
|
||||
z-index: 11;
|
||||
}
|
||||
}
|
||||
|
||||
// Size variants
|
||||
&.ui-chart-sm {
|
||||
padding: $lions-spacing-3;
|
||||
|
||||
canvas {
|
||||
max-height: 200px;
|
||||
}
|
||||
}
|
||||
|
||||
&.ui-chart-lg {
|
||||
padding: $lions-spacing-6;
|
||||
|
||||
canvas {
|
||||
min-height: 400px;
|
||||
}
|
||||
}
|
||||
|
||||
// Borderless variant
|
||||
&.ui-chart-borderless {
|
||||
border: none;
|
||||
box-shadow: none;
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// 🎨 LIONS.DEV COLOR PALETTE FOR CHARTS
|
||||
// ============================================
|
||||
|
||||
// Palette de couleurs Lions.dev pour les datasets
|
||||
$lions-chart-colors: (
|
||||
primary: $lions-blue-500,
|
||||
secondary: $lions-gray-500,
|
||||
success: $lions-success-500,
|
||||
info: $lions-info-500,
|
||||
warning: $lions-warning-500,
|
||||
danger: $lions-danger-500,
|
||||
gold: $lions-gold-500,
|
||||
purple: #9333EA,
|
||||
pink: #EC4899,
|
||||
teal: #14B8A6,
|
||||
indigo: #6366F1,
|
||||
orange: #F97316
|
||||
);
|
||||
|
||||
// Couleurs avec transparence pour les zones (area charts)
|
||||
$lions-chart-colors-alpha: (
|
||||
primary: rgba($lions-blue-500, 0.2),
|
||||
secondary: rgba($lions-gray-500, 0.2),
|
||||
success: rgba($lions-success-500, 0.2),
|
||||
info: rgba($lions-info-500, 0.2),
|
||||
warning: rgba($lions-warning-500, 0.2),
|
||||
danger: rgba($lions-danger-500, 0.2),
|
||||
gold: rgba($lions-gold-500, 0.2),
|
||||
purple: rgba(#9333EA, 0.2),
|
||||
pink: rgba(#EC4899, 0.2),
|
||||
teal: rgba(#14B8A6, 0.2),
|
||||
indigo: rgba(#6366F1, 0.2),
|
||||
orange: rgba(#F97316, 0.2)
|
||||
);
|
||||
|
||||
// ============================================
|
||||
// 🏷️ CHART LEGEND STYLES
|
||||
// ============================================
|
||||
|
||||
.chartjs-legend {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: center;
|
||||
gap: $lions-spacing-3;
|
||||
margin-top: $lions-spacing-4;
|
||||
padding: $lions-spacing-3;
|
||||
background: $lions-gray-50;
|
||||
@include lions-rounded('md');
|
||||
|
||||
ul {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: center;
|
||||
gap: $lions-spacing-3;
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
li {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: $lions-spacing-2;
|
||||
cursor: pointer;
|
||||
@include lions-transition();
|
||||
|
||||
&:hover {
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
&.hidden {
|
||||
opacity: 0.4;
|
||||
text-decoration: line-through;
|
||||
}
|
||||
|
||||
span {
|
||||
@include lions-font-size('sm');
|
||||
@include lions-font-weight('medium');
|
||||
color: $lions-text-primary;
|
||||
}
|
||||
|
||||
.legend-color {
|
||||
width: 1rem;
|
||||
height: 1rem;
|
||||
@include lions-rounded('sm');
|
||||
border: 2px solid transparent;
|
||||
@include lions-transition();
|
||||
|
||||
&:hover {
|
||||
border-color: $lions-gray-400;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// 💬 CHART TOOLTIP STYLES
|
||||
// ============================================
|
||||
|
||||
.chartjs-tooltip {
|
||||
position: absolute;
|
||||
background: rgba($lions-gray-900, 0.95);
|
||||
color: $lions-white;
|
||||
@include lions-rounded('md');
|
||||
@include lions-shadow('lg');
|
||||
padding: $lions-spacing-3 $lions-spacing-4;
|
||||
pointer-events: none;
|
||||
z-index: 1000;
|
||||
@include lions-transition();
|
||||
opacity: 0;
|
||||
transform: translateY(10px);
|
||||
|
||||
&.active {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
|
||||
.tooltip-title {
|
||||
@include lions-font-size('sm');
|
||||
@include lions-font-weight('bold');
|
||||
color: $lions-white;
|
||||
margin-bottom: $lions-spacing-2;
|
||||
padding-bottom: $lions-spacing-2;
|
||||
border-bottom: 1px solid rgba($lions-white, 0.2);
|
||||
}
|
||||
|
||||
.tooltip-body {
|
||||
@include lions-font-size('sm');
|
||||
color: $lions-white;
|
||||
|
||||
.tooltip-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: $lions-spacing-2;
|
||||
margin-bottom: $lions-spacing-1;
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.tooltip-color {
|
||||
width: 0.75rem;
|
||||
height: 0.75rem;
|
||||
@include lions-rounded('sm');
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.tooltip-label {
|
||||
@include lions-font-weight('medium');
|
||||
color: rgba($lions-white, 0.9);
|
||||
}
|
||||
|
||||
.tooltip-value {
|
||||
@include lions-font-weight('bold');
|
||||
color: $lions-white;
|
||||
margin-left: auto;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// 📈 CHART TYPE SPECIFIC STYLES
|
||||
// ============================================
|
||||
|
||||
// Line Chart
|
||||
.ui-chart-line {
|
||||
canvas {
|
||||
min-height: 300px;
|
||||
}
|
||||
}
|
||||
|
||||
// Bar Chart
|
||||
.ui-chart-bar {
|
||||
canvas {
|
||||
min-height: 300px;
|
||||
}
|
||||
}
|
||||
|
||||
// Pie/Doughnut Chart
|
||||
.ui-chart-pie,
|
||||
.ui-chart-doughnut {
|
||||
canvas {
|
||||
max-width: 400px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.chartjs-legend {
|
||||
margin-top: $lions-spacing-5;
|
||||
}
|
||||
}
|
||||
|
||||
// Radar Chart
|
||||
.ui-chart-radar {
|
||||
canvas {
|
||||
max-width: 400px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
}
|
||||
|
||||
// Polar Area Chart
|
||||
.ui-chart-polararea {
|
||||
canvas {
|
||||
max-width: 400px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// 📊 CHART WRAPPER & GRID
|
||||
// ============================================
|
||||
|
||||
.ui-chart-wrapper {
|
||||
position: relative;
|
||||
display: block;
|
||||
width: 100%;
|
||||
|
||||
// Chart header avec actions
|
||||
.ui-chart-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-bottom: $lions-spacing-4;
|
||||
padding-bottom: $lions-spacing-3;
|
||||
border-bottom: 1px solid $lions-gray-200;
|
||||
|
||||
.ui-chart-title {
|
||||
@include lions-font-size('lg');
|
||||
@include lions-font-weight('bold');
|
||||
color: $lions-text-primary;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.ui-chart-actions {
|
||||
display: flex;
|
||||
gap: $lions-spacing-2;
|
||||
|
||||
button {
|
||||
@include lions-button-base;
|
||||
padding: $lions-spacing-2 $lions-spacing-3;
|
||||
@include lions-rounded('md');
|
||||
background: $lions-gray-100;
|
||||
border: 1px solid $lions-gray-300;
|
||||
color: $lions-gray-700;
|
||||
cursor: pointer;
|
||||
@include lions-transition();
|
||||
|
||||
&:hover {
|
||||
background: $lions-gray-200;
|
||||
border-color: $lions-gray-400;
|
||||
}
|
||||
|
||||
i {
|
||||
font-size: 1rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Chart footer avec légende ou infos
|
||||
.ui-chart-footer {
|
||||
margin-top: $lions-spacing-4;
|
||||
padding-top: $lions-spacing-3;
|
||||
border-top: 1px solid $lions-gray-200;
|
||||
@include lions-font-size('sm');
|
||||
color: $lions-gray-600;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
|
||||
// Chart Grid (multiple charts)
|
||||
.ui-chart-grid {
|
||||
display: grid;
|
||||
gap: $lions-spacing-5;
|
||||
|
||||
&.ui-chart-grid-2 {
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
}
|
||||
|
||||
&.ui-chart-grid-3 {
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
}
|
||||
|
||||
&.ui-chart-grid-4 {
|
||||
grid-template-columns: repeat(4, 1fr);
|
||||
}
|
||||
|
||||
@media (max-width: $lions-breakpoint-lg) {
|
||||
&.ui-chart-grid-3,
|
||||
&.ui-chart-grid-4 {
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: $lions-breakpoint-md) {
|
||||
&.ui-chart-grid-2,
|
||||
&.ui-chart-grid-3,
|
||||
&.ui-chart-grid-4 {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// 🎬 ANIMATIONS
|
||||
// ============================================
|
||||
|
||||
@keyframes lions-chart-spin {
|
||||
0% {
|
||||
transform: translate(-50%, -50%) rotate(0deg);
|
||||
}
|
||||
100% {
|
||||
transform: translate(-50%, -50%) rotate(360deg);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes lions-chart-fade-in {
|
||||
0% {
|
||||
opacity: 0;
|
||||
transform: translateY(20px);
|
||||
}
|
||||
100% {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
.ui-chart {
|
||||
animation: lions-chart-fade-in 0.4s ease-out;
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// 📱 RESPONSIVE ADJUSTMENTS
|
||||
// ============================================
|
||||
|
||||
@media (max-width: $lions-breakpoint-md) {
|
||||
.ui-chart {
|
||||
padding: $lions-spacing-4;
|
||||
|
||||
canvas {
|
||||
min-height: 250px !important;
|
||||
}
|
||||
|
||||
&.ui-chart-sm canvas {
|
||||
max-height: 180px;
|
||||
}
|
||||
|
||||
&.ui-chart-lg canvas {
|
||||
min-height: 300px !important;
|
||||
}
|
||||
}
|
||||
|
||||
.ui-chart-pie,
|
||||
.ui-chart-doughnut,
|
||||
.ui-chart-radar,
|
||||
.ui-chart-polararea {
|
||||
canvas {
|
||||
max-width: 100% !important;
|
||||
}
|
||||
}
|
||||
|
||||
.chartjs-legend {
|
||||
padding: $lions-spacing-2;
|
||||
|
||||
ul {
|
||||
gap: $lions-spacing-2;
|
||||
}
|
||||
|
||||
li {
|
||||
@include lions-font-size('xs');
|
||||
|
||||
.legend-color {
|
||||
width: 0.875rem;
|
||||
height: 0.875rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.ui-chart-wrapper {
|
||||
.ui-chart-header {
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
gap: $lions-spacing-3;
|
||||
|
||||
.ui-chart-actions {
|
||||
width: 100%;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: $lions-breakpoint-sm) {
|
||||
.ui-chart {
|
||||
padding: $lions-spacing-3;
|
||||
|
||||
canvas {
|
||||
min-height: 200px !important;
|
||||
}
|
||||
}
|
||||
|
||||
.chartjs-tooltip {
|
||||
padding: $lions-spacing-2 $lions-spacing-3;
|
||||
@include lions-font-size('xs');
|
||||
|
||||
.tooltip-title {
|
||||
@include lions-font-size('xs');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// ♿ ACCESSIBILITY ENHANCEMENTS
|
||||
// ============================================
|
||||
|
||||
.ui-chart {
|
||||
// Focus visible for interactive elements
|
||||
button:focus-visible {
|
||||
@include lions-focus-ring();
|
||||
}
|
||||
|
||||
// High contrast mode
|
||||
@media (prefers-contrast: high) {
|
||||
border-width: 2px !important;
|
||||
|
||||
.chartjs-legend li {
|
||||
border: 2px solid currentColor;
|
||||
padding: $lions-spacing-1 $lions-spacing-2;
|
||||
@include lions-rounded('md');
|
||||
}
|
||||
}
|
||||
|
||||
// Reduced motion
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
animation: none !important;
|
||||
|
||||
* {
|
||||
transition: none !important;
|
||||
animation: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
// Screen reader only text for data
|
||||
.sr-only {
|
||||
position: absolute;
|
||||
width: 1px;
|
||||
height: 1px;
|
||||
padding: 0;
|
||||
margin: -1px;
|
||||
overflow: hidden;
|
||||
clip: rect(0, 0, 0, 0);
|
||||
white-space: nowrap;
|
||||
border-width: 0;
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// 🎨 UTILITY CLASSES
|
||||
// ============================================
|
||||
|
||||
// Chart with gradient background
|
||||
.ui-chart-gradient {
|
||||
background: linear-gradient(135deg, rgba($lions-blue-50, 0.5) 0%, rgba($lions-white, 1) 100%);
|
||||
}
|
||||
|
||||
// Chart with dark theme
|
||||
.ui-chart-dark {
|
||||
background: $lions-gray-900;
|
||||
border-color: $lions-gray-700;
|
||||
|
||||
.ui-chart-title,
|
||||
.ui-chart-subtitle {
|
||||
color: $lions-white;
|
||||
}
|
||||
|
||||
.ui-chart-header {
|
||||
border-bottom-color: $lions-gray-700;
|
||||
|
||||
.ui-chart-title {
|
||||
color: $lions-white;
|
||||
}
|
||||
}
|
||||
|
||||
.ui-chart-footer {
|
||||
border-top-color: $lions-gray-700;
|
||||
color: $lions-gray-400;
|
||||
}
|
||||
|
||||
.chartjs-legend {
|
||||
background: rgba($lions-gray-800, 0.5);
|
||||
|
||||
li span {
|
||||
color: $lions-white;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Chart full height
|
||||
.ui-chart-fullheight {
|
||||
height: 100%;
|
||||
|
||||
canvas {
|
||||
height: 100% !important;
|
||||
}
|
||||
}
|
||||
|
||||
// Chart with hover effect
|
||||
.ui-chart-hoverable {
|
||||
@include lions-transition();
|
||||
|
||||
&:hover {
|
||||
@include lions-shadow('md');
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
}
|
||||
|
||||
// Chart export button
|
||||
.ui-chart-export {
|
||||
position: absolute;
|
||||
top: $lions-spacing-3;
|
||||
right: $lions-spacing-3;
|
||||
|
||||
button {
|
||||
@include lions-button-base;
|
||||
width: 2rem;
|
||||
height: 2rem;
|
||||
min-width: 2rem;
|
||||
padding: 0;
|
||||
@include lions-flex-center;
|
||||
background: $lions-white;
|
||||
border: 1px solid $lions-gray-300;
|
||||
@include lions-rounded('md');
|
||||
@include lions-shadow('sm');
|
||||
color: $lions-gray-600;
|
||||
cursor: pointer;
|
||||
@include lions-transition();
|
||||
|
||||
&:hover {
|
||||
background: $lions-blue-50;
|
||||
border-color: $lions-blue-300;
|
||||
color: $lions-blue-600;
|
||||
@include lions-shadow('md');
|
||||
}
|
||||
|
||||
i {
|
||||
font-size: 1rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,538 @@
|
||||
/*
|
||||
* ═══════════════════════════════════════════════════════════
|
||||
* Lions.dev - Data Display Component Styles
|
||||
* ═══════════════════════════════════════════════════════════
|
||||
* Styles complets pour DataTable, DataView et composants de données
|
||||
*
|
||||
* Version: 1.0.0
|
||||
* Date: 1er Janvier 2026
|
||||
* Author: Lions Development Team
|
||||
* Priority: CRITIQUE #16-17
|
||||
* ═══════════════════════════════════════════════════════════
|
||||
*/
|
||||
|
||||
@import '../variables';
|
||||
@import '../mixins';
|
||||
|
||||
// ============================================
|
||||
// 📊 DATATABLE BASE STYLES
|
||||
// ============================================
|
||||
|
||||
.ui-datatable {
|
||||
@include lions-card;
|
||||
overflow: hidden;
|
||||
|
||||
// Table wrapper
|
||||
.ui-datatable-tablewrapper {
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
||||
// Table element
|
||||
table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
background: $lions-white;
|
||||
@include lions-font-size('base');
|
||||
}
|
||||
|
||||
// Header
|
||||
.ui-datatable-thead {
|
||||
background: linear-gradient(135deg, rgba($lions-blue-50, 0.5) 0%, rgba($lions-blue-100, 0.3) 100%);
|
||||
border-bottom: 2px solid $lions-blue-500;
|
||||
|
||||
th {
|
||||
padding: $lions-spacing-4 $lions-spacing-5;
|
||||
text-align: left;
|
||||
@include lions-font-weight('bold');
|
||||
color: $lions-text-primary;
|
||||
border-bottom: 1px solid $lions-gray-300;
|
||||
white-space: nowrap;
|
||||
@include lions-transition();
|
||||
|
||||
// Sortable column
|
||||
&.ui-sortable-column {
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
background: rgba($lions-blue-100, 0.5);
|
||||
color: $lions-blue-700;
|
||||
}
|
||||
|
||||
.ui-sortable-column-icon {
|
||||
margin-left: $lions-spacing-2;
|
||||
color: $lions-gray-500;
|
||||
@include lions-transition();
|
||||
}
|
||||
|
||||
&.ui-state-active {
|
||||
background: rgba($lions-blue-200, 0.6);
|
||||
color: $lions-blue-700;
|
||||
|
||||
.ui-sortable-column-icon {
|
||||
color: $lions-blue-600;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Filter inputs in header
|
||||
.ui-column-filter {
|
||||
width: 100%;
|
||||
margin-top: $lions-spacing-2;
|
||||
@include lions-input-base;
|
||||
@include lions-input-size('sm');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Body
|
||||
.ui-datatable-data {
|
||||
tr {
|
||||
border-bottom: 1px solid $lions-gray-200;
|
||||
@include lions-transition();
|
||||
|
||||
&:hover {
|
||||
background: rgba($lions-blue-50, 0.4);
|
||||
}
|
||||
|
||||
&.ui-state-highlight {
|
||||
background: rgba($lions-blue-100, 0.6);
|
||||
color: $lions-blue-900;
|
||||
}
|
||||
|
||||
// Striped rows
|
||||
&:nth-child(even) {
|
||||
background: rgba($lions-gray-50, 0.3);
|
||||
|
||||
&:hover {
|
||||
background: rgba($lions-blue-50, 0.5);
|
||||
}
|
||||
}
|
||||
|
||||
td {
|
||||
padding: $lions-spacing-4 $lions-spacing-5;
|
||||
color: $lions-text-primary;
|
||||
vertical-align: middle;
|
||||
border-bottom: 1px solid $lions-gray-200;
|
||||
|
||||
// Row editor controls
|
||||
.ui-row-editor {
|
||||
display: flex;
|
||||
gap: $lions-spacing-2;
|
||||
|
||||
.ui-icon {
|
||||
cursor: pointer;
|
||||
color: $lions-blue-500;
|
||||
padding: $lions-spacing-1;
|
||||
@include lions-rounded('sm');
|
||||
@include lions-transition();
|
||||
|
||||
&:hover {
|
||||
background: rgba($lions-blue-100, 0.8);
|
||||
color: $lions-blue-700;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Empty message
|
||||
.ui-datatable-empty-message {
|
||||
td {
|
||||
text-align: center;
|
||||
padding: $lions-spacing-8 !important;
|
||||
color: $lions-text-secondary;
|
||||
@include lions-font-size('lg');
|
||||
}
|
||||
}
|
||||
|
||||
// Small size variant
|
||||
&.ui-datatable-sm {
|
||||
th, td {
|
||||
padding: $lions-spacing-2 $lions-spacing-3;
|
||||
@include lions-font-size('sm');
|
||||
}
|
||||
}
|
||||
|
||||
// Large size variant
|
||||
&.ui-datatable-lg {
|
||||
th, td {
|
||||
padding: $lions-spacing-5 $lions-spacing-6;
|
||||
@include lions-font-size('lg');
|
||||
}
|
||||
}
|
||||
|
||||
// Gridlines variant
|
||||
&.ui-datatable-gridlines {
|
||||
td, th {
|
||||
border: 1px solid $lions-gray-300;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// 📄 DATATABLE PAGINATOR
|
||||
// ============================================
|
||||
|
||||
.ui-paginator {
|
||||
background: linear-gradient(135deg, rgba($lions-gray-50, 0.8) 0%, rgba($lions-gray-100, 0.5) 100%);
|
||||
border-top: 1px solid $lions-gray-300;
|
||||
padding: $lions-spacing-3 $lions-spacing-5;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
flex-wrap: wrap;
|
||||
gap: $lions-spacing-3;
|
||||
|
||||
// Current page report
|
||||
.ui-paginator-current {
|
||||
@include lions-font-size('sm');
|
||||
color: $lions-text-secondary;
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
// Page links container
|
||||
.ui-paginator-pages {
|
||||
display: flex;
|
||||
gap: $lions-spacing-1;
|
||||
}
|
||||
|
||||
// Page link buttons
|
||||
.ui-paginator-page,
|
||||
.ui-paginator-first,
|
||||
.ui-paginator-prev,
|
||||
.ui-paginator-next,
|
||||
.ui-paginator-last {
|
||||
@include lions-button-base;
|
||||
min-width: 2.5rem;
|
||||
height: 2.5rem;
|
||||
padding: 0;
|
||||
@include lions-flex-center;
|
||||
background: $lions-white;
|
||||
border: 1px solid $lions-gray-300;
|
||||
color: $lions-text-primary;
|
||||
@include lions-transition();
|
||||
|
||||
&:hover:not(.ui-state-disabled):not(.ui-state-active) {
|
||||
background: $lions-blue-50;
|
||||
border-color: $lions-blue-500;
|
||||
color: $lions-blue-700;
|
||||
}
|
||||
|
||||
&.ui-state-active {
|
||||
@include lions-gradient-blue;
|
||||
border-color: $lions-blue-700;
|
||||
color: $lions-white;
|
||||
font-weight: $lions-font-bold;
|
||||
}
|
||||
|
||||
&.ui-state-disabled {
|
||||
opacity: 0.5;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
}
|
||||
|
||||
// Rows per page dropdown
|
||||
.ui-paginator-rpp-options {
|
||||
@include lions-input-base;
|
||||
@include lions-input-size('sm');
|
||||
min-width: 80px;
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// 🔍 DATATABLE SELECTION
|
||||
// ============================================
|
||||
|
||||
.ui-datatable {
|
||||
// Checkbox selection column
|
||||
.ui-selection-column {
|
||||
width: 3rem;
|
||||
text-align: center;
|
||||
|
||||
.ui-chkbox {
|
||||
@include lions-flex-center;
|
||||
}
|
||||
}
|
||||
|
||||
// Radio selection column
|
||||
.ui-radiobutton-column {
|
||||
width: 3rem;
|
||||
text-align: center;
|
||||
|
||||
.ui-radiobutton {
|
||||
@include lions-flex-center;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// 📋 DATAVIEW BASE STYLES
|
||||
// ============================================
|
||||
|
||||
.ui-dataview {
|
||||
.ui-dataview-header {
|
||||
background: linear-gradient(135deg, rgba($lions-blue-50, 0.5) 0%, rgba($lions-blue-100, 0.3) 100%);
|
||||
border-bottom: 2px solid $lions-blue-500;
|
||||
padding: $lions-spacing-4 $lions-spacing-5;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
|
||||
.ui-dataview-layout-options {
|
||||
display: flex;
|
||||
gap: $lions-spacing-1;
|
||||
|
||||
.ui-button {
|
||||
@include lions-button-base;
|
||||
@include lions-button-size('sm');
|
||||
background: $lions-white;
|
||||
border: 1px solid $lions-gray-300;
|
||||
color: $lions-text-primary;
|
||||
|
||||
&:hover {
|
||||
background: $lions-blue-50;
|
||||
border-color: $lions-blue-500;
|
||||
color: $lions-blue-700;
|
||||
}
|
||||
|
||||
&.ui-state-active {
|
||||
@include lions-gradient-blue;
|
||||
border-color: $lions-blue-700;
|
||||
color: $lions-white;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.ui-dataview-content {
|
||||
padding: $lions-spacing-5;
|
||||
background: $lions-white;
|
||||
}
|
||||
|
||||
// List layout
|
||||
.ui-dataview-list {
|
||||
.ui-dataview-row {
|
||||
padding: $lions-spacing-4;
|
||||
border-bottom: 1px solid $lions-gray-200;
|
||||
@include lions-transition();
|
||||
|
||||
&:hover {
|
||||
background: rgba($lions-blue-50, 0.4);
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Grid layout
|
||||
.ui-dataview-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
|
||||
gap: $lions-spacing-5;
|
||||
|
||||
@media (max-width: $lions-breakpoint-md) {
|
||||
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
|
||||
gap: $lions-spacing-4;
|
||||
}
|
||||
|
||||
@media (max-width: $lions-breakpoint-sm) {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
|
||||
// Empty message
|
||||
.ui-dataview-emptymessage {
|
||||
text-align: center;
|
||||
padding: $lions-spacing-8;
|
||||
color: $lions-text-secondary;
|
||||
@include lions-font-size('lg');
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// 📊 SCROLLABLE DATATABLE
|
||||
// ============================================
|
||||
|
||||
.ui-datatable-scrollable {
|
||||
.ui-datatable-scrollable-header,
|
||||
.ui-datatable-scrollable-footer {
|
||||
background: linear-gradient(135deg, rgba($lions-blue-50, 0.5) 0%, rgba($lions-blue-100, 0.3) 100%);
|
||||
border-bottom: 2px solid $lions-blue-500;
|
||||
}
|
||||
|
||||
.ui-datatable-scrollable-body {
|
||||
overflow: auto;
|
||||
|
||||
// Custom scrollbar
|
||||
&::-webkit-scrollbar {
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar-track {
|
||||
background: $lions-gray-100;
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar-thumb {
|
||||
background: $lions-gray-400;
|
||||
@include lions-rounded('full');
|
||||
|
||||
&:hover {
|
||||
background: $lions-blue-500;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// 🔄 RESIZABLE & REORDERABLE COLUMNS
|
||||
// ============================================
|
||||
|
||||
.ui-datatable {
|
||||
// Column resizer
|
||||
.ui-column-resizer {
|
||||
width: 2px;
|
||||
background: $lions-blue-500;
|
||||
cursor: col-resize;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 0;
|
||||
height: 100%;
|
||||
opacity: 0;
|
||||
@include lions-transition();
|
||||
|
||||
&:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
th:hover .ui-column-resizer {
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
// Reorderable column indicator
|
||||
.ui-datatable-reorderablerow-handle {
|
||||
cursor: move;
|
||||
color: $lions-gray-500;
|
||||
padding: 0 $lions-spacing-2;
|
||||
|
||||
&:hover {
|
||||
color: $lions-blue-500;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// 🎯 ROW EXPANSION
|
||||
// ============================================
|
||||
|
||||
.ui-datatable {
|
||||
.ui-row-toggler {
|
||||
cursor: pointer;
|
||||
color: $lions-blue-500;
|
||||
@include lions-transition();
|
||||
|
||||
&:hover {
|
||||
color: $lions-blue-700;
|
||||
}
|
||||
}
|
||||
|
||||
.ui-expanded-row-content {
|
||||
td {
|
||||
background: rgba($lions-blue-50, 0.2);
|
||||
border-left: 4px solid $lions-blue-500;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// 📱 RESPONSIVE DATATABLE
|
||||
// ============================================
|
||||
|
||||
@media (max-width: $lions-breakpoint-md) {
|
||||
.ui-datatable {
|
||||
th, td {
|
||||
padding: $lions-spacing-3 $lions-spacing-4;
|
||||
@include lions-font-size('sm');
|
||||
}
|
||||
}
|
||||
|
||||
.ui-paginator {
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
|
||||
.ui-paginator-current {
|
||||
text-align: center;
|
||||
margin: 0 0 $lions-spacing-2 0;
|
||||
}
|
||||
|
||||
.ui-paginator-pages {
|
||||
justify-content: center;
|
||||
}
|
||||
}
|
||||
|
||||
.ui-dataview-grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// ♿ ACCESSIBILITY ENHANCEMENTS
|
||||
// ============================================
|
||||
|
||||
.ui-datatable,
|
||||
.ui-dataview {
|
||||
// Focus visible
|
||||
th:focus-visible,
|
||||
td:focus-visible,
|
||||
.ui-paginator-page:focus-visible {
|
||||
@include lions-focus-ring();
|
||||
}
|
||||
|
||||
// High contrast mode
|
||||
@media (prefers-contrast: high) {
|
||||
border-width: 2px !important;
|
||||
|
||||
th, td {
|
||||
border-width: 2px !important;
|
||||
}
|
||||
}
|
||||
|
||||
// Reduced motion
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
tr, th, td, .ui-paginator-page {
|
||||
transition: none !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// 🎨 DATATABLE SEVERITY ROW STYLES
|
||||
// ============================================
|
||||
|
||||
.ui-datatable-data {
|
||||
tr {
|
||||
&.row-success {
|
||||
background: rgba($lions-success-50, 0.3);
|
||||
border-left: 4px solid $lions-success-500;
|
||||
}
|
||||
|
||||
&.row-warning {
|
||||
background: rgba($lions-warning-50, 0.3);
|
||||
border-left: 4px solid $lions-warning-500;
|
||||
}
|
||||
|
||||
&.row-danger {
|
||||
background: rgba($lions-danger-50, 0.3);
|
||||
border-left: 4px solid $lions-danger-500;
|
||||
}
|
||||
|
||||
&.row-info {
|
||||
background: rgba($lions-info-50, 0.3);
|
||||
border-left: 4px solid $lions-info-500;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,577 @@
|
||||
/*
|
||||
* ═══════════════════════════════════════════════════════════
|
||||
* Lions.dev - Display Component Styles
|
||||
* ═══════════════════════════════════════════════════════════
|
||||
* Styles pour Avatar, Badge, Tag et composants d'affichage
|
||||
*
|
||||
* Version: 1.0.0
|
||||
* Date: 2 Janvier 2026
|
||||
* Author: Lions Development Team
|
||||
* Priority: HAUTE #34-36
|
||||
* ═══════════════════════════════════════════════════════════
|
||||
*/
|
||||
|
||||
@import '../variables';
|
||||
@import '../mixins';
|
||||
|
||||
// ============================================
|
||||
// 👤 AVATAR STYLES
|
||||
// ============================================
|
||||
|
||||
.ui-avatar {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
vertical-align: middle;
|
||||
@include lions-transition();
|
||||
|
||||
// Base avatar (40px)
|
||||
width: 2.5rem;
|
||||
height: 2.5rem;
|
||||
@include lions-rounded('full');
|
||||
@include lions-gradient-blue;
|
||||
color: $lions-white;
|
||||
@include lions-font-weight('semibold');
|
||||
@include lions-font-size('base');
|
||||
|
||||
// Avatar image
|
||||
img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
// Avatar label/initials
|
||||
.ui-avatar-text {
|
||||
@include lions-font-weight('semibold');
|
||||
text-transform: uppercase;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
// Avatar icon
|
||||
.ui-avatar-icon {
|
||||
font-size: 1.25rem;
|
||||
}
|
||||
|
||||
// Size variants
|
||||
&.ui-avatar-xs {
|
||||
width: 1.5rem;
|
||||
height: 1.5rem;
|
||||
@include lions-font-size('xs');
|
||||
|
||||
.ui-avatar-icon {
|
||||
font-size: 0.75rem;
|
||||
}
|
||||
}
|
||||
|
||||
&.ui-avatar-sm {
|
||||
width: 2rem;
|
||||
height: 2rem;
|
||||
@include lions-font-size('sm');
|
||||
|
||||
.ui-avatar-icon {
|
||||
font-size: 1rem;
|
||||
}
|
||||
}
|
||||
|
||||
&.ui-avatar-lg {
|
||||
width: 3rem;
|
||||
height: 3rem;
|
||||
@include lions-font-size('lg');
|
||||
|
||||
.ui-avatar-icon {
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
&.ui-avatar-xl {
|
||||
width: 4rem;
|
||||
height: 4rem;
|
||||
@include lions-font-size('xl');
|
||||
|
||||
.ui-avatar-icon {
|
||||
font-size: 2rem;
|
||||
}
|
||||
}
|
||||
|
||||
// Shape variants
|
||||
&.ui-avatar-circle {
|
||||
@include lions-rounded('full');
|
||||
}
|
||||
|
||||
&.ui-avatar-square {
|
||||
@include lions-rounded(0);
|
||||
}
|
||||
|
||||
&.ui-avatar-rounded {
|
||||
@include lions-rounded('lg');
|
||||
}
|
||||
|
||||
// Severity variants
|
||||
&.ui-avatar-secondary {
|
||||
background: $lions-gray-500;
|
||||
}
|
||||
|
||||
&.ui-avatar-success {
|
||||
@include lions-gradient-success;
|
||||
}
|
||||
|
||||
&.ui-avatar-info {
|
||||
@include lions-gradient-info;
|
||||
}
|
||||
|
||||
&.ui-avatar-warning {
|
||||
@include lions-gradient-warning;
|
||||
}
|
||||
|
||||
&.ui-avatar-danger {
|
||||
@include lions-gradient-danger;
|
||||
}
|
||||
|
||||
// Avatar group
|
||||
&-group {
|
||||
display: inline-flex;
|
||||
|
||||
.ui-avatar {
|
||||
margin-left: -0.5rem;
|
||||
border: 2px solid $lions-white;
|
||||
@include lions-shadow('sm');
|
||||
|
||||
&:first-child {
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
z-index: 10;
|
||||
transform: scale(1.1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Status indicator
|
||||
&-status {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
width: 0.75rem;
|
||||
height: 0.75rem;
|
||||
@include lions-rounded('full');
|
||||
border: 2px solid $lions-white;
|
||||
|
||||
&-online {
|
||||
background: $lions-success-500;
|
||||
}
|
||||
|
||||
&-offline {
|
||||
background: $lions-gray-400;
|
||||
}
|
||||
|
||||
&-busy {
|
||||
background: $lions-danger-500;
|
||||
}
|
||||
|
||||
&-away {
|
||||
background: $lions-warning-500;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// 🏷️ BADGE STYLES
|
||||
// ============================================
|
||||
|
||||
.ui-badge {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 0.25rem 0.5rem;
|
||||
@include lions-rounded('md');
|
||||
@include lions-font-size('xs');
|
||||
@include lions-font-weight('semibold');
|
||||
line-height: 1;
|
||||
white-space: nowrap;
|
||||
vertical-align: middle;
|
||||
@include lions-transition();
|
||||
|
||||
// Default (primary)
|
||||
background: $lions-blue-100;
|
||||
color: $lions-blue-700;
|
||||
|
||||
// Severity variants
|
||||
&.ui-badge-secondary {
|
||||
background: $lions-gray-100;
|
||||
color: $lions-gray-700;
|
||||
}
|
||||
|
||||
&.ui-badge-success {
|
||||
background: $lions-success-100;
|
||||
color: $lions-success-700;
|
||||
}
|
||||
|
||||
&.ui-badge-info {
|
||||
background: $lions-info-100;
|
||||
color: $lions-info-700;
|
||||
}
|
||||
|
||||
&.ui-badge-warning {
|
||||
background: $lions-warning-100;
|
||||
color: $lions-warning-700;
|
||||
}
|
||||
|
||||
&.ui-badge-danger {
|
||||
background: $lions-danger-100;
|
||||
color: $lions-danger-700;
|
||||
}
|
||||
|
||||
// Size variants
|
||||
&.ui-badge-sm {
|
||||
padding: 0.125rem 0.375rem;
|
||||
@include lions-font-size('2xs');
|
||||
}
|
||||
|
||||
&.ui-badge-lg {
|
||||
padding: 0.375rem 0.75rem;
|
||||
@include lions-font-size('sm');
|
||||
}
|
||||
|
||||
// Pill variant
|
||||
&.ui-badge-pill {
|
||||
@include lions-rounded('full');
|
||||
}
|
||||
|
||||
// Dot variant
|
||||
&.ui-badge-dot {
|
||||
padding: 0;
|
||||
width: 0.5rem;
|
||||
height: 0.5rem;
|
||||
@include lions-rounded('full');
|
||||
|
||||
&.ui-badge-sm {
|
||||
width: 0.375rem;
|
||||
height: 0.375rem;
|
||||
}
|
||||
|
||||
&.ui-badge-lg {
|
||||
width: 0.625rem;
|
||||
height: 0.625rem;
|
||||
}
|
||||
}
|
||||
|
||||
// Outlined variant
|
||||
&.ui-badge-outlined {
|
||||
background: transparent;
|
||||
border: 1px solid currentColor;
|
||||
|
||||
&.ui-badge-primary {
|
||||
color: $lions-blue-600;
|
||||
border-color: $lions-blue-600;
|
||||
}
|
||||
|
||||
&.ui-badge-secondary {
|
||||
color: $lions-gray-600;
|
||||
border-color: $lions-gray-600;
|
||||
}
|
||||
|
||||
&.ui-badge-success {
|
||||
color: $lions-success-600;
|
||||
border-color: $lions-success-600;
|
||||
}
|
||||
|
||||
&.ui-badge-info {
|
||||
color: $lions-info-600;
|
||||
border-color: $lions-info-600;
|
||||
}
|
||||
|
||||
&.ui-badge-warning {
|
||||
color: $lions-warning-600;
|
||||
border-color: $lions-warning-600;
|
||||
}
|
||||
|
||||
&.ui-badge-danger {
|
||||
color: $lions-danger-600;
|
||||
border-color: $lions-danger-600;
|
||||
}
|
||||
}
|
||||
|
||||
// Overlay badge (for icons, avatars)
|
||||
&-overlay {
|
||||
position: absolute;
|
||||
top: -0.25rem;
|
||||
right: -0.25rem;
|
||||
min-width: 1.25rem;
|
||||
height: 1.25rem;
|
||||
padding: 0 0.375rem;
|
||||
@include lions-rounded('full');
|
||||
@include lions-shadow('sm');
|
||||
}
|
||||
}
|
||||
|
||||
// Badge on button
|
||||
.ui-button {
|
||||
position: relative;
|
||||
|
||||
.ui-badge-overlay {
|
||||
top: -0.5rem;
|
||||
right: -0.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// 🔖 TAG STYLES
|
||||
// ============================================
|
||||
|
||||
.ui-tag {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: $lions-spacing-2;
|
||||
padding: 0.375rem 0.75rem;
|
||||
@include lions-rounded('md');
|
||||
@include lions-font-size('sm');
|
||||
@include lions-font-weight('medium');
|
||||
line-height: 1.5;
|
||||
vertical-align: middle;
|
||||
@include lions-transition();
|
||||
|
||||
// Default (primary)
|
||||
background: $lions-blue-100;
|
||||
color: $lions-blue-700;
|
||||
|
||||
// Tag icon
|
||||
.ui-tag-icon {
|
||||
font-size: 1rem;
|
||||
line-height: 1;
|
||||
|
||||
&.ui-tag-icon-left {
|
||||
margin-right: -$lions-spacing-1;
|
||||
}
|
||||
|
||||
&.ui-tag-icon-right {
|
||||
margin-left: -$lions-spacing-1;
|
||||
}
|
||||
}
|
||||
|
||||
// Tag value
|
||||
.ui-tag-value {
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
// Close button
|
||||
.ui-tag-close {
|
||||
@include lions-button-base;
|
||||
width: 1rem;
|
||||
height: 1rem;
|
||||
min-width: 1rem;
|
||||
padding: 0;
|
||||
@include lions-flex-center;
|
||||
background: transparent;
|
||||
border: none;
|
||||
color: currentColor;
|
||||
opacity: 0.6;
|
||||
cursor: pointer;
|
||||
@include lions-rounded('full');
|
||||
@include lions-transition();
|
||||
|
||||
&:hover {
|
||||
opacity: 1;
|
||||
background: rgba($lions-black, 0.1);
|
||||
}
|
||||
|
||||
&:active {
|
||||
transform: scale(0.9);
|
||||
}
|
||||
|
||||
.ui-icon {
|
||||
font-size: 0.75rem;
|
||||
}
|
||||
}
|
||||
|
||||
// Severity variants
|
||||
&.ui-tag-secondary {
|
||||
background: $lions-gray-100;
|
||||
color: $lions-gray-700;
|
||||
}
|
||||
|
||||
&.ui-tag-success {
|
||||
background: $lions-success-100;
|
||||
color: $lions-success-700;
|
||||
}
|
||||
|
||||
&.ui-tag-info {
|
||||
background: $lions-info-100;
|
||||
color: $lions-info-700;
|
||||
}
|
||||
|
||||
&.ui-tag-warning {
|
||||
background: $lions-warning-100;
|
||||
color: $lions-warning-700;
|
||||
}
|
||||
|
||||
&.ui-tag-danger {
|
||||
background: $lions-danger-100;
|
||||
color: $lions-danger-700;
|
||||
}
|
||||
|
||||
// Size variants
|
||||
&.ui-tag-sm {
|
||||
padding: 0.25rem 0.5rem;
|
||||
@include lions-font-size('xs');
|
||||
|
||||
.ui-tag-icon {
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
|
||||
.ui-tag-close {
|
||||
width: 0.875rem;
|
||||
height: 0.875rem;
|
||||
|
||||
.ui-icon {
|
||||
font-size: 0.625rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.ui-tag-lg {
|
||||
padding: 0.5rem 1rem;
|
||||
@include lions-font-size('base');
|
||||
|
||||
.ui-tag-icon {
|
||||
font-size: 1.125rem;
|
||||
}
|
||||
|
||||
.ui-tag-close {
|
||||
width: 1.125rem;
|
||||
height: 1.125rem;
|
||||
|
||||
.ui-icon {
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Pill variant
|
||||
&.ui-tag-pill {
|
||||
@include lions-rounded('full');
|
||||
}
|
||||
|
||||
// Outlined variant
|
||||
&.ui-tag-outlined {
|
||||
background: transparent;
|
||||
border: 1px solid currentColor;
|
||||
|
||||
&.ui-tag-primary {
|
||||
color: $lions-blue-600;
|
||||
border-color: $lions-blue-600;
|
||||
}
|
||||
|
||||
&.ui-tag-secondary {
|
||||
color: $lions-gray-600;
|
||||
border-color: $lions-gray-600;
|
||||
}
|
||||
|
||||
&.ui-tag-success {
|
||||
color: $lions-success-600;
|
||||
border-color: $lions-success-600;
|
||||
}
|
||||
|
||||
&.ui-tag-info {
|
||||
color: $lions-info-600;
|
||||
border-color: $lions-info-600;
|
||||
}
|
||||
|
||||
&.ui-tag-warning {
|
||||
color: $lions-warning-600;
|
||||
border-color: $lions-warning-600;
|
||||
}
|
||||
|
||||
&.ui-tag-danger {
|
||||
color: $lions-danger-600;
|
||||
border-color: $lions-danger-600;
|
||||
}
|
||||
|
||||
.ui-tag-close:hover {
|
||||
background: rgba(currentColor, 0.1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Tag group
|
||||
.ui-tag-group {
|
||||
display: inline-flex;
|
||||
flex-wrap: wrap;
|
||||
gap: $lions-spacing-2;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// 📱 RESPONSIVE ADJUSTMENTS
|
||||
// ============================================
|
||||
|
||||
@media (max-width: $lions-breakpoint-md) {
|
||||
.ui-avatar {
|
||||
&.ui-avatar-xl {
|
||||
width: 3rem;
|
||||
height: 3rem;
|
||||
@include lions-font-size('lg');
|
||||
}
|
||||
}
|
||||
|
||||
.ui-avatar-group {
|
||||
.ui-avatar {
|
||||
margin-left: -0.375rem;
|
||||
}
|
||||
}
|
||||
|
||||
.ui-tag-group {
|
||||
gap: $lions-spacing-1;
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// ♿ ACCESSIBILITY ENHANCEMENTS
|
||||
// ============================================
|
||||
|
||||
.ui-avatar,
|
||||
.ui-badge,
|
||||
.ui-tag {
|
||||
// High contrast mode
|
||||
@media (prefers-contrast: high) {
|
||||
border: 2px solid currentColor !important;
|
||||
}
|
||||
|
||||
// Reduced motion
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
transition: none !important;
|
||||
animation: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// 🎨 UTILITY CLASSES
|
||||
// ============================================
|
||||
|
||||
// Avatar with border
|
||||
.ui-avatar-bordered {
|
||||
border: 2px solid $lions-white;
|
||||
@include lions-shadow('md');
|
||||
}
|
||||
|
||||
// Badge pulse animation
|
||||
.ui-badge-pulse {
|
||||
animation: lions-badge-pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;
|
||||
}
|
||||
|
||||
@keyframes lions-badge-pulse {
|
||||
0%, 100% {
|
||||
opacity: 1;
|
||||
}
|
||||
50% {
|
||||
opacity: 0.5;
|
||||
}
|
||||
}
|
||||
|
||||
// Tag removable
|
||||
.ui-tag-removable {
|
||||
padding-right: $lions-spacing-2;
|
||||
}
|
||||
@@ -0,0 +1,789 @@
|
||||
/*
|
||||
* ═══════════════════════════════════════════════════════════
|
||||
* Lions.dev - Form Field Component Styles
|
||||
* ═══════════════════════════════════════════════════════════
|
||||
* Amélioration visuelle complète des champs de formulaire avec identité Lions.dev
|
||||
*
|
||||
* Version: 1.0.0
|
||||
* Date: 1er Janvier 2026
|
||||
* Author: Lions Development Team
|
||||
* Priority: CRITIQUE #5-8
|
||||
* ═══════════════════════════════════════════════════════════
|
||||
*/
|
||||
|
||||
@import '../variables';
|
||||
@import '../mixins';
|
||||
|
||||
// ============================================
|
||||
// 🎯 BASE FIELD STYLES
|
||||
// ============================================
|
||||
|
||||
.field {
|
||||
margin-bottom: $lions-spacing-6;
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// 🏷️ LABEL STYLES
|
||||
// ============================================
|
||||
|
||||
.ui-outputlabel,
|
||||
label {
|
||||
display: block;
|
||||
margin-bottom: $lions-spacing-2;
|
||||
@include lions-font-size('sm');
|
||||
@include lions-font-weight('semibold');
|
||||
color: $lions-text-primary;
|
||||
|
||||
// Required indicator (red asterisk)
|
||||
.p-error {
|
||||
color: $lions-danger-500;
|
||||
margin-left: 2px;
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// 📝 INPUT TEXT BASE STYLES
|
||||
// ============================================
|
||||
|
||||
.ui-inputtext,
|
||||
.ui-inputfield {
|
||||
@include lions-input-base;
|
||||
@include lions-input-size('base');
|
||||
|
||||
// Override PrimeFaces defaults
|
||||
font-family: $lions-font-family-primary !important;
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// 📏 INPUT SIZES
|
||||
// ============================================
|
||||
|
||||
// Small inputs
|
||||
.ui-inputtext.ui-inputtext-sm,
|
||||
.ui-inputfield.ui-inputfield-sm,
|
||||
.field-sm .ui-inputtext,
|
||||
.field-sm .ui-inputfield {
|
||||
@include lions-input-size('sm');
|
||||
}
|
||||
|
||||
// Large inputs
|
||||
.ui-inputtext.ui-inputtext-lg,
|
||||
.ui-inputfield.ui-inputfield-lg,
|
||||
.field-lg .ui-inputtext,
|
||||
.field-lg .ui-inputfield {
|
||||
@include lions-input-size('lg');
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// ✅ INPUT STATES
|
||||
// ============================================
|
||||
|
||||
// Success state (valid input)
|
||||
.ui-inputtext.ui-state-valid,
|
||||
.ui-inputfield.ui-state-valid {
|
||||
@include lions-input-state-success;
|
||||
}
|
||||
|
||||
// Error state (invalid input)
|
||||
.ui-inputtext.ui-state-error,
|
||||
.ui-inputfield.ui-state-error,
|
||||
.ui-inputtext.ui-state-invalid,
|
||||
.ui-inputfield.ui-state-invalid {
|
||||
@include lions-input-state-error;
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// 📝 TEXTAREA STYLES
|
||||
// ============================================
|
||||
|
||||
.ui-inputtextarea {
|
||||
@include lions-input-base;
|
||||
min-height: 80px;
|
||||
padding: $lions-input-padding-y $lions-input-padding-x;
|
||||
resize: vertical;
|
||||
line-height: $lions-line-height-normal;
|
||||
|
||||
// Size variants
|
||||
&.ui-inputtextarea-sm {
|
||||
@include lions-font-size('sm');
|
||||
padding: $lions-spacing-2 $lions-spacing-3;
|
||||
}
|
||||
|
||||
&.ui-inputtextarea-lg {
|
||||
@include lions-font-size('lg');
|
||||
padding: $lions-spacing-4 $lions-spacing-5;
|
||||
}
|
||||
|
||||
// States
|
||||
&.ui-state-error {
|
||||
@include lions-input-state-error;
|
||||
}
|
||||
|
||||
&.ui-state-valid {
|
||||
@include lions-input-state-success;
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// 🔽 SELECT / DROPDOWN STYLES
|
||||
// ============================================
|
||||
|
||||
.ui-selectonemenu {
|
||||
width: 100%;
|
||||
|
||||
.ui-selectonemenu-trigger {
|
||||
@include lions-flex-center;
|
||||
background: $lions-gray-50;
|
||||
border-left: 1px solid $lions-gray-300;
|
||||
width: $lions-button-height-base;
|
||||
color: $lions-gray-600;
|
||||
@include lions-transition();
|
||||
|
||||
&:hover {
|
||||
background: $lions-gray-100;
|
||||
color: $lions-blue-500;
|
||||
}
|
||||
}
|
||||
|
||||
.ui-selectonemenu-label {
|
||||
@include lions-input-base;
|
||||
@include lions-input-size('base');
|
||||
border-radius: $lions-radius-base 0 0 $lions-radius-base !important;
|
||||
}
|
||||
|
||||
&.ui-state-focus {
|
||||
.ui-selectonemenu-label {
|
||||
outline: none;
|
||||
border-color: $lions-blue-500;
|
||||
box-shadow: 0 0 0 3px rgba($lions-blue-500, 0.15);
|
||||
}
|
||||
|
||||
.ui-selectonemenu-trigger {
|
||||
border-color: $lions-blue-500;
|
||||
}
|
||||
}
|
||||
|
||||
&.ui-state-error {
|
||||
.ui-selectonemenu-label {
|
||||
@include lions-input-state-error;
|
||||
border-radius: $lions-radius-base 0 0 $lions-radius-base !important;
|
||||
}
|
||||
|
||||
.ui-selectonemenu-trigger {
|
||||
border-color: $lions-danger-500;
|
||||
}
|
||||
}
|
||||
|
||||
// Size variants
|
||||
&.ui-selectonemenu-sm {
|
||||
.ui-selectonemenu-label {
|
||||
@include lions-input-size('sm');
|
||||
}
|
||||
|
||||
.ui-selectonemenu-trigger {
|
||||
width: $lions-button-height-sm;
|
||||
}
|
||||
}
|
||||
|
||||
&.ui-selectonemenu-lg {
|
||||
.ui-selectonemenu-label {
|
||||
@include lions-input-size('lg');
|
||||
}
|
||||
|
||||
.ui-selectonemenu-trigger {
|
||||
width: $lions-button-height-lg;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Dropdown panel
|
||||
.ui-selectonemenu-panel {
|
||||
border: 1px solid $lions-gray-300;
|
||||
@include lions-rounded('md');
|
||||
@include lions-shadow('lg');
|
||||
background: $lions-white;
|
||||
margin-top: $lions-spacing-1;
|
||||
|
||||
.ui-selectonemenu-items {
|
||||
padding: $lions-spacing-1 0;
|
||||
|
||||
.ui-selectonemenu-item {
|
||||
padding: $lions-spacing-3 $lions-spacing-4;
|
||||
color: $lions-text-primary;
|
||||
@include lions-transition();
|
||||
cursor: pointer;
|
||||
|
||||
&:hover,
|
||||
&.ui-state-highlight {
|
||||
background: rgba($lions-blue-50, 0.8);
|
||||
color: $lions-blue-700;
|
||||
}
|
||||
|
||||
&.ui-state-highlight {
|
||||
@include lions-font-weight('semibold');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.ui-selectonemenu-filter-container {
|
||||
padding: $lions-spacing-2;
|
||||
border-bottom: 1px solid $lions-gray-200;
|
||||
|
||||
.ui-selectonemenu-filter {
|
||||
@include lions-input-base;
|
||||
@include lions-input-size('sm');
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// 🔒 PASSWORD INPUT STYLES
|
||||
// ============================================
|
||||
|
||||
.ui-password {
|
||||
width: 100%;
|
||||
|
||||
.ui-inputtext {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
// Password strength meter
|
||||
.ui-password-panel {
|
||||
background: $lions-white;
|
||||
border: 1px solid $lions-gray-300;
|
||||
@include lions-rounded('md');
|
||||
@include lions-shadow('md');
|
||||
padding: $lions-spacing-4;
|
||||
margin-top: $lions-spacing-2;
|
||||
|
||||
.ui-password-meter {
|
||||
height: 8px;
|
||||
background: $lions-gray-200;
|
||||
@include lions-rounded('full');
|
||||
overflow: hidden;
|
||||
margin-bottom: $lions-spacing-3;
|
||||
|
||||
.ui-password-strength {
|
||||
height: 100%;
|
||||
@include lions-transition(width);
|
||||
|
||||
&.weak {
|
||||
width: 33%;
|
||||
background: $lions-danger-500;
|
||||
}
|
||||
|
||||
&.medium {
|
||||
width: 66%;
|
||||
background: $lions-warning-500;
|
||||
}
|
||||
|
||||
&.strong {
|
||||
width: 100%;
|
||||
background: $lions-success-500;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.ui-password-info {
|
||||
@include lions-font-size('xs');
|
||||
color: $lions-text-secondary;
|
||||
margin-top: $lions-spacing-2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// 📅 CALENDAR / DATE PICKER STYLES
|
||||
// ============================================
|
||||
|
||||
.ui-calendar {
|
||||
.ui-inputtext {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.ui-datepicker-trigger {
|
||||
@include lions-button-base;
|
||||
@include lions-button-size('base');
|
||||
@include lions-button-solid($lions-blue-500, $lions-blue-700);
|
||||
margin-left: $lions-spacing-2;
|
||||
width: auto;
|
||||
min-width: $lions-button-height-base;
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// 🔢 NUMBER INPUT (InputNumber)
|
||||
// ============================================
|
||||
|
||||
.ui-inputnumber {
|
||||
.ui-inputtext {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.ui-inputnumber-button-group {
|
||||
.ui-inputnumber-button {
|
||||
@include lions-button-base;
|
||||
background: $lions-gray-100;
|
||||
border-color: $lions-gray-300;
|
||||
color: $lions-text-primary;
|
||||
width: 2rem;
|
||||
height: 50%;
|
||||
|
||||
&:hover {
|
||||
background: $lions-gray-200;
|
||||
color: $lions-blue-500;
|
||||
}
|
||||
|
||||
&:active {
|
||||
background: $lions-gray-300;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// 🔍 AUTOCOMPLETE STYLES
|
||||
// ============================================
|
||||
|
||||
.ui-autocomplete {
|
||||
width: 100%;
|
||||
|
||||
.ui-inputtext {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.ui-autocomplete-dropdown {
|
||||
@include lions-button-base;
|
||||
@include lions-button-size('base');
|
||||
@include lions-button-solid($lions-blue-500, $lions-blue-700);
|
||||
margin-left: $lions-spacing-2;
|
||||
width: auto;
|
||||
min-width: $lions-button-height-base;
|
||||
}
|
||||
|
||||
.ui-autocomplete-multiple-container {
|
||||
@include lions-input-base;
|
||||
@include lions-input-size('base');
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: $lions-spacing-2;
|
||||
min-height: $lions-input-height-base;
|
||||
padding: $lions-spacing-2;
|
||||
|
||||
.ui-autocomplete-token {
|
||||
@include lions-rounded('md');
|
||||
background: $lions-blue-100;
|
||||
color: $lions-blue-700;
|
||||
padding: $lions-spacing-1 $lions-spacing-3;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: $lions-spacing-2;
|
||||
@include lions-font-size('sm');
|
||||
@include lions-transition();
|
||||
|
||||
&:hover {
|
||||
background: $lions-blue-200;
|
||||
}
|
||||
|
||||
.ui-autocomplete-token-icon {
|
||||
cursor: pointer;
|
||||
color: $lions-blue-600;
|
||||
|
||||
&:hover {
|
||||
color: $lions-danger-500;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.ui-autocomplete-input-token {
|
||||
flex: 1;
|
||||
min-width: 120px;
|
||||
|
||||
input {
|
||||
border: none;
|
||||
outline: none;
|
||||
background: transparent;
|
||||
width: 100%;
|
||||
@include lions-font-size('base');
|
||||
color: $lions-text-primary;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// AutoComplete panel
|
||||
.ui-autocomplete-panel {
|
||||
background: $lions-white;
|
||||
border: 1px solid $lions-gray-300;
|
||||
@include lions-rounded('md');
|
||||
@include lions-shadow('lg');
|
||||
margin-top: $lions-spacing-1;
|
||||
max-height: 300px;
|
||||
overflow-y: auto;
|
||||
|
||||
.ui-autocomplete-items {
|
||||
padding: $lions-spacing-1 0;
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
|
||||
.ui-autocomplete-item {
|
||||
padding: $lions-spacing-3 $lions-spacing-4;
|
||||
cursor: pointer;
|
||||
@include lions-transition();
|
||||
@include lions-font-size('base');
|
||||
color: $lions-text-primary;
|
||||
|
||||
&:hover,
|
||||
&.ui-state-highlight {
|
||||
background: rgba($lions-blue-50, 0.8);
|
||||
color: $lions-blue-700;
|
||||
}
|
||||
|
||||
&:active {
|
||||
background: rgba($lions-blue-100, 0.8);
|
||||
}
|
||||
}
|
||||
|
||||
.ui-autocomplete-group {
|
||||
padding: $lions-spacing-2 $lions-spacing-4;
|
||||
@include lions-font-size('sm');
|
||||
@include lions-font-weight('bold');
|
||||
color: $lions-text-secondary;
|
||||
background: $lions-gray-50;
|
||||
border-bottom: 1px solid $lions-gray-200;
|
||||
}
|
||||
}
|
||||
|
||||
.ui-autocomplete-empty-message {
|
||||
padding: $lions-spacing-4;
|
||||
@include lions-font-size('sm');
|
||||
color: $lions-text-secondary;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// 📋 MULTISELECT STYLES
|
||||
// ============================================
|
||||
|
||||
.ui-multiselect {
|
||||
width: 100%;
|
||||
|
||||
.ui-multiselect-label-container {
|
||||
.ui-multiselect-label {
|
||||
@include lions-input-base;
|
||||
@include lions-input-size('base');
|
||||
border-radius: $lions-radius-base 0 0 $lions-radius-base !important;
|
||||
}
|
||||
}
|
||||
|
||||
.ui-multiselect-trigger {
|
||||
@include lions-flex-center;
|
||||
background: $lions-gray-50;
|
||||
border-left: 1px solid $lions-gray-300;
|
||||
width: $lions-button-height-base;
|
||||
color: $lions-gray-600;
|
||||
|
||||
&:hover {
|
||||
background: $lions-gray-100;
|
||||
color: $lions-blue-500;
|
||||
}
|
||||
}
|
||||
|
||||
&.ui-state-focus {
|
||||
.ui-multiselect-label {
|
||||
border-color: $lions-blue-500;
|
||||
box-shadow: 0 0 0 3px rgba($lions-blue-500, 0.15);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.ui-multiselect-panel {
|
||||
border: 1px solid $lions-gray-300;
|
||||
@include lions-rounded('md');
|
||||
@include lions-shadow('lg');
|
||||
|
||||
.ui-multiselect-header {
|
||||
background: $lions-gray-50;
|
||||
padding: $lions-spacing-3 $lions-spacing-4;
|
||||
border-bottom: 1px solid $lions-gray-200;
|
||||
|
||||
.ui-chkbox {
|
||||
margin-right: $lions-spacing-2;
|
||||
}
|
||||
}
|
||||
|
||||
.ui-multiselect-items {
|
||||
padding: $lions-spacing-1 0;
|
||||
|
||||
.ui-multiselect-item {
|
||||
padding: $lions-spacing-3 $lions-spacing-4;
|
||||
@include lions-transition();
|
||||
|
||||
&:hover {
|
||||
background: rgba($lions-blue-50, 0.8);
|
||||
}
|
||||
|
||||
.ui-chkbox {
|
||||
margin-right: $lions-spacing-2;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// ☑️ CHECKBOX & RADIO STYLES
|
||||
// ============================================
|
||||
|
||||
.ui-chkbox,
|
||||
.ui-radiobutton {
|
||||
.ui-chkbox-box,
|
||||
.ui-radiobutton-box {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
border: 2px solid $lions-gray-400;
|
||||
background: $lions-white;
|
||||
@include lions-transition();
|
||||
@include lions-flex-center;
|
||||
|
||||
.ui-chkbox-icon,
|
||||
.ui-radiobutton-icon {
|
||||
color: $lions-white;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
border-color: $lions-blue-500;
|
||||
background: rgba($lions-blue-50, 0.5);
|
||||
}
|
||||
|
||||
&.ui-state-active {
|
||||
@include lions-gradient-blue;
|
||||
border-color: $lions-blue-700;
|
||||
|
||||
.ui-chkbox-icon,
|
||||
.ui-radiobutton-icon {
|
||||
color: $lions-white;
|
||||
}
|
||||
}
|
||||
|
||||
&.ui-state-focus {
|
||||
@include lions-focus-ring($lions-blue-500);
|
||||
}
|
||||
}
|
||||
|
||||
// Checkbox specific
|
||||
.ui-chkbox-box {
|
||||
@include lions-rounded('sm');
|
||||
}
|
||||
|
||||
// Radio specific
|
||||
.ui-radiobutton-box {
|
||||
border-radius: $lions-radius-full;
|
||||
|
||||
&.ui-state-active {
|
||||
.ui-radiobutton-icon {
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
background: $lions-white;
|
||||
border-radius: $lions-radius-full;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// 🔘 SWITCH / TOGGLE STYLES
|
||||
// ============================================
|
||||
|
||||
.ui-inputswitch {
|
||||
width: 48px;
|
||||
height: 24px;
|
||||
background: $lions-gray-300;
|
||||
@include lions-rounded('full');
|
||||
@include lions-transition();
|
||||
cursor: pointer;
|
||||
|
||||
.ui-inputswitch-slider {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
background: $lions-white;
|
||||
@include lions-rounded('full');
|
||||
@include lions-shadow('sm');
|
||||
@include lions-transition();
|
||||
position: absolute;
|
||||
top: 2px;
|
||||
left: 2px;
|
||||
}
|
||||
|
||||
&.ui-inputswitch-checked {
|
||||
@include lions-gradient-blue;
|
||||
|
||||
.ui-inputswitch-slider {
|
||||
left: 26px;
|
||||
}
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background: $lions-gray-400;
|
||||
}
|
||||
|
||||
&.ui-inputswitch-checked:hover {
|
||||
background: linear-gradient(135deg, $lions-blue-600 0%, $lions-blue-800 100%);
|
||||
}
|
||||
|
||||
&.ui-inputswitch-focus {
|
||||
@include lions-focus-ring($lions-blue-500);
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// 📄 FILE UPLOAD STYLES
|
||||
// ============================================
|
||||
|
||||
.ui-fileupload {
|
||||
border: 2px dashed $lions-gray-300;
|
||||
@include lions-rounded('md');
|
||||
background: $lions-gray-50;
|
||||
padding: $lions-spacing-6;
|
||||
@include lions-transition();
|
||||
|
||||
&:hover {
|
||||
border-color: $lions-blue-500;
|
||||
background: rgba($lions-blue-50, 0.3);
|
||||
}
|
||||
|
||||
.ui-fileupload-buttonbar {
|
||||
background: transparent;
|
||||
border: none;
|
||||
padding: $lions-spacing-4;
|
||||
|
||||
.ui-button {
|
||||
@include lions-button-solid($lions-blue-500, $lions-blue-700);
|
||||
}
|
||||
}
|
||||
|
||||
.ui-fileupload-content {
|
||||
background: $lions-white;
|
||||
border: 1px solid $lions-gray-200;
|
||||
@include lions-rounded('md');
|
||||
padding: $lions-spacing-4;
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// 💬 VALIDATION MESSAGES
|
||||
// ============================================
|
||||
|
||||
.ui-message,
|
||||
.ui-messages {
|
||||
@include lions-rounded('md');
|
||||
padding: $lions-spacing-3 $lions-spacing-4;
|
||||
@include lions-font-size('sm');
|
||||
margin-top: $lions-spacing-2;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: $lions-spacing-2;
|
||||
|
||||
&.ui-message-error,
|
||||
&.ui-messages-error {
|
||||
background: rgba($lions-danger-50, 0.8);
|
||||
border-left: 4px solid $lions-danger-500;
|
||||
color: $lions-danger-700;
|
||||
|
||||
.ui-message-icon,
|
||||
.ui-messages-icon {
|
||||
color: $lions-danger-500;
|
||||
}
|
||||
}
|
||||
|
||||
&.ui-message-warn,
|
||||
&.ui-messages-warn {
|
||||
background: rgba($lions-warning-50, 0.8);
|
||||
border-left: 4px solid $lions-warning-500;
|
||||
color: $lions-warning-700;
|
||||
|
||||
.ui-message-icon,
|
||||
.ui-messages-icon {
|
||||
color: $lions-warning-500;
|
||||
}
|
||||
}
|
||||
|
||||
&.ui-message-info,
|
||||
&.ui-messages-info {
|
||||
background: rgba($lions-info-50, 0.8);
|
||||
border-left: 4px solid $lions-info-500;
|
||||
color: $lions-info-700;
|
||||
|
||||
.ui-message-icon,
|
||||
.ui-messages-icon {
|
||||
color: $lions-info-500;
|
||||
}
|
||||
}
|
||||
|
||||
&.ui-message-success,
|
||||
&.ui-messages-success {
|
||||
background: rgba($lions-success-50, 0.8);
|
||||
border-left: 4px solid $lions-success-500;
|
||||
color: $lions-success-700;
|
||||
|
||||
.ui-message-icon,
|
||||
.ui-messages-icon {
|
||||
color: $lions-success-500;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// ♿ ACCESSIBILITY ENHANCEMENTS
|
||||
// ============================================
|
||||
|
||||
// Focus visible for all inputs
|
||||
.ui-inputtext,
|
||||
.ui-inputfield,
|
||||
.ui-inputtextarea,
|
||||
.ui-selectonemenu,
|
||||
.ui-multiselect,
|
||||
.ui-chkbox-box,
|
||||
.ui-radiobutton-box {
|
||||
&:focus-visible {
|
||||
@include lions-focus-ring();
|
||||
}
|
||||
}
|
||||
|
||||
// High contrast mode
|
||||
@media (prefers-contrast: high) {
|
||||
.ui-inputtext,
|
||||
.ui-inputfield,
|
||||
.ui-inputtextarea {
|
||||
border-width: 2px !important;
|
||||
}
|
||||
}
|
||||
|
||||
// Reduced motion
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
.ui-inputtext,
|
||||
.ui-inputfield,
|
||||
.ui-inputtextarea,
|
||||
.ui-selectonemenu,
|
||||
.ui-multiselect {
|
||||
transition: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// 📱 RESPONSIVE ADJUSTMENTS
|
||||
// ============================================
|
||||
|
||||
// Touch-friendly on mobile
|
||||
@media (hover: none) and (pointer: coarse) {
|
||||
.ui-inputtext,
|
||||
.ui-inputfield {
|
||||
min-height: 44px; // iOS touch target minimum
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,890 @@
|
||||
/*
|
||||
* ═══════════════════════════════════════════════════════════
|
||||
* Lions.dev - Advanced Form Component Styles
|
||||
* ═══════════════════════════════════════════════════════════
|
||||
* Styles pour composants de formulaire avancés
|
||||
* Spinner, Mask, Chips, ColorPicker, Editor, FileUpload
|
||||
*
|
||||
* Version: 1.0.0
|
||||
* Date: 2 Janvier 2026
|
||||
* Author: Lions Development Team
|
||||
* Priority: BASSE #40-44
|
||||
* ═══════════════════════════════════════════════════════════
|
||||
*/
|
||||
|
||||
@import '../variables';
|
||||
@import '../mixins';
|
||||
|
||||
// ============================================
|
||||
// 🔢 SPINNER STYLES
|
||||
// ============================================
|
||||
|
||||
.ui-spinner {
|
||||
display: inline-flex;
|
||||
position: relative;
|
||||
width: 100%;
|
||||
|
||||
.ui-spinner-input {
|
||||
width: 100%;
|
||||
height: $lions-input-height-base;
|
||||
padding: $lions-spacing-3 $lions-spacing-4;
|
||||
padding-right: 2.5rem;
|
||||
@include lions-rounded('md');
|
||||
border: 1px solid $lions-gray-300;
|
||||
@include lions-font-size('base');
|
||||
color: $lions-text-primary;
|
||||
background: $lions-white;
|
||||
@include lions-transition();
|
||||
|
||||
&:hover {
|
||||
border-color: $lions-gray-400;
|
||||
}
|
||||
|
||||
&:focus {
|
||||
outline: none;
|
||||
border-color: $lions-blue-500;
|
||||
box-shadow: 0 0 0 3px rgba($lions-blue-500, 0.15);
|
||||
}
|
||||
|
||||
&:disabled {
|
||||
background: $lions-gray-100;
|
||||
cursor: not-allowed;
|
||||
opacity: 0.6;
|
||||
}
|
||||
}
|
||||
|
||||
.ui-spinner-button-group {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 0;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.ui-spinner-button {
|
||||
@include lions-button-base;
|
||||
width: 2rem;
|
||||
height: 50%;
|
||||
min-width: 2rem;
|
||||
padding: 0;
|
||||
@include lions-flex-center;
|
||||
background: $lions-gray-100;
|
||||
border: 1px solid $lions-gray-300;
|
||||
border-left: none;
|
||||
color: $lions-gray-600;
|
||||
cursor: pointer;
|
||||
@include lions-transition();
|
||||
|
||||
&:hover {
|
||||
background: $lions-gray-200;
|
||||
color: $lions-blue-600;
|
||||
}
|
||||
|
||||
&:active {
|
||||
background: $lions-gray-300;
|
||||
}
|
||||
|
||||
&.ui-spinner-up {
|
||||
border-top-right-radius: $lions-rounded-md;
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
&.ui-spinner-down {
|
||||
border-bottom-right-radius: $lions-rounded-md;
|
||||
}
|
||||
|
||||
.ui-icon {
|
||||
font-size: 0.75rem;
|
||||
}
|
||||
}
|
||||
|
||||
// Size variants
|
||||
&.ui-spinner-sm {
|
||||
.ui-spinner-input {
|
||||
height: $lions-input-height-sm;
|
||||
padding: $lions-spacing-2 $lions-spacing-3;
|
||||
padding-right: 2rem;
|
||||
@include lions-font-size('sm');
|
||||
}
|
||||
|
||||
.ui-spinner-button {
|
||||
width: 1.75rem;
|
||||
min-width: 1.75rem;
|
||||
}
|
||||
}
|
||||
|
||||
&.ui-spinner-lg {
|
||||
.ui-spinner-input {
|
||||
height: $lions-input-height-lg;
|
||||
padding: $lions-spacing-4 $lions-spacing-5;
|
||||
padding-right: 3rem;
|
||||
@include lions-font-size('lg');
|
||||
}
|
||||
|
||||
.ui-spinner-button {
|
||||
width: 2.5rem;
|
||||
min-width: 2.5rem;
|
||||
|
||||
.ui-icon {
|
||||
font-size: 1rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// 🎭 INPUT MASK STYLES
|
||||
// ============================================
|
||||
|
||||
.ui-inputmask {
|
||||
width: 100%;
|
||||
height: $lions-input-height-base;
|
||||
padding: $lions-spacing-3 $lions-spacing-4;
|
||||
@include lions-rounded('md');
|
||||
border: 1px solid $lions-gray-300;
|
||||
@include lions-font-size('base');
|
||||
color: $lions-text-primary;
|
||||
background: $lions-white;
|
||||
@include lions-transition();
|
||||
font-family: $lions-font-family-mono;
|
||||
|
||||
&::placeholder {
|
||||
color: $lions-gray-400;
|
||||
font-family: $lions-font-family-primary;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
border-color: $lions-gray-400;
|
||||
}
|
||||
|
||||
&:focus {
|
||||
outline: none;
|
||||
border-color: $lions-blue-500;
|
||||
box-shadow: 0 0 0 3px rgba($lions-blue-500, 0.15);
|
||||
}
|
||||
|
||||
&:disabled {
|
||||
background: $lions-gray-100;
|
||||
cursor: not-allowed;
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
// Size variants
|
||||
&.ui-inputmask-sm {
|
||||
height: $lions-input-height-sm;
|
||||
padding: $lions-spacing-2 $lions-spacing-3;
|
||||
@include lions-font-size('sm');
|
||||
}
|
||||
|
||||
&.ui-inputmask-lg {
|
||||
height: $lions-input-height-lg;
|
||||
padding: $lions-spacing-4 $lions-spacing-5;
|
||||
@include lions-font-size('lg');
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// 🏷️ CHIPS STYLES
|
||||
// ============================================
|
||||
|
||||
.ui-chips {
|
||||
display: block;
|
||||
width: 100%;
|
||||
|
||||
.ui-chips-container {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: $lions-spacing-2;
|
||||
padding: $lions-spacing-2;
|
||||
@include lions-rounded('md');
|
||||
border: 1px solid $lions-gray-300;
|
||||
background: $lions-white;
|
||||
min-height: $lions-input-height-base;
|
||||
@include lions-transition();
|
||||
|
||||
&:hover {
|
||||
border-color: $lions-gray-400;
|
||||
}
|
||||
|
||||
&.ui-state-focus {
|
||||
border-color: $lions-blue-500;
|
||||
box-shadow: 0 0 0 3px rgba($lions-blue-500, 0.15);
|
||||
}
|
||||
|
||||
&.ui-state-disabled {
|
||||
background: $lions-gray-100;
|
||||
cursor: not-allowed;
|
||||
opacity: 0.6;
|
||||
}
|
||||
}
|
||||
|
||||
.ui-chips-token {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: $lions-spacing-2;
|
||||
padding: $lions-spacing-2 $lions-spacing-3;
|
||||
@include lions-rounded('md');
|
||||
background: $lions-blue-100;
|
||||
color: $lions-blue-700;
|
||||
@include lions-font-size('sm');
|
||||
@include lions-font-weight('medium');
|
||||
@include lions-transition();
|
||||
|
||||
&:hover {
|
||||
background: $lions-blue-200;
|
||||
}
|
||||
}
|
||||
|
||||
.ui-chips-token-icon {
|
||||
@include lions-button-base;
|
||||
width: 1rem;
|
||||
height: 1rem;
|
||||
min-width: 1rem;
|
||||
padding: 0;
|
||||
@include lions-flex-center;
|
||||
background: transparent;
|
||||
border: none;
|
||||
color: $lions-blue-600;
|
||||
cursor: pointer;
|
||||
@include lions-rounded('full');
|
||||
@include lions-transition();
|
||||
|
||||
&:hover {
|
||||
background: rgba($lions-blue-700, 0.2);
|
||||
color: $lions-blue-800;
|
||||
}
|
||||
|
||||
.ui-icon {
|
||||
font-size: 0.75rem;
|
||||
}
|
||||
}
|
||||
|
||||
.ui-chips-input-token {
|
||||
flex: 1;
|
||||
min-width: 120px;
|
||||
|
||||
input {
|
||||
width: 100%;
|
||||
border: none;
|
||||
outline: none;
|
||||
background: transparent;
|
||||
padding: $lions-spacing-2;
|
||||
@include lions-font-size('base');
|
||||
color: $lions-text-primary;
|
||||
|
||||
&::placeholder {
|
||||
color: $lions-gray-400;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Severity variants
|
||||
&.ui-chips-success .ui-chips-token {
|
||||
background: $lions-success-100;
|
||||
color: $lions-success-700;
|
||||
|
||||
.ui-chips-token-icon {
|
||||
color: $lions-success-600;
|
||||
}
|
||||
}
|
||||
|
||||
&.ui-chips-warning .ui-chips-token {
|
||||
background: $lions-warning-100;
|
||||
color: $lions-warning-700;
|
||||
|
||||
.ui-chips-token-icon {
|
||||
color: $lions-warning-600;
|
||||
}
|
||||
}
|
||||
|
||||
&.ui-chips-danger .ui-chips-token {
|
||||
background: $lions-danger-100;
|
||||
color: $lions-danger-700;
|
||||
|
||||
.ui-chips-token-icon {
|
||||
color: $lions-danger-600;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// 🎨 COLOR PICKER STYLES
|
||||
// ============================================
|
||||
|
||||
.ui-colorpicker {
|
||||
display: inline-flex;
|
||||
position: relative;
|
||||
|
||||
.ui-colorpicker-preview {
|
||||
width: 2.5rem;
|
||||
height: 2.5rem;
|
||||
@include lions-rounded('md');
|
||||
border: 2px solid $lions-gray-300;
|
||||
cursor: pointer;
|
||||
@include lions-shadow('sm');
|
||||
@include lions-transition();
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
|
||||
&:hover {
|
||||
border-color: $lions-blue-400;
|
||||
transform: scale(1.05);
|
||||
}
|
||||
|
||||
&:active {
|
||||
transform: scale(0.98);
|
||||
}
|
||||
|
||||
.ui-colorpicker-color {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.ui-colorpicker-panel {
|
||||
position: absolute;
|
||||
top: 100%;
|
||||
left: 0;
|
||||
margin-top: $lions-spacing-2;
|
||||
@include lions-rounded('lg');
|
||||
@include lions-shadow('xl');
|
||||
background: $lions-white;
|
||||
border: 1px solid $lions-gray-200;
|
||||
padding: $lions-spacing-4;
|
||||
z-index: 1000;
|
||||
|
||||
.ui-colorpicker-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: $lions-spacing-3;
|
||||
}
|
||||
|
||||
.ui-colorpicker-hue {
|
||||
width: 100%;
|
||||
height: 1rem;
|
||||
@include lions-rounded('md');
|
||||
cursor: crosshair;
|
||||
}
|
||||
|
||||
.ui-colorpicker-color-selector {
|
||||
width: 200px;
|
||||
height: 150px;
|
||||
@include lions-rounded('md');
|
||||
cursor: crosshair;
|
||||
position: relative;
|
||||
background: linear-gradient(to right, #fff, transparent),
|
||||
linear-gradient(to top, #000, transparent);
|
||||
}
|
||||
|
||||
.ui-colorpicker-color-handle {
|
||||
position: absolute;
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
@include lions-rounded('full');
|
||||
border: 2px solid $lions-white;
|
||||
@include lions-shadow('md');
|
||||
transform: translate(-50%, -50%);
|
||||
pointer-events: none;
|
||||
}
|
||||
}
|
||||
|
||||
// Inline variant
|
||||
&.ui-colorpicker-inline {
|
||||
.ui-colorpicker-panel {
|
||||
position: static;
|
||||
margin-top: 0;
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
// Size variants
|
||||
&.ui-colorpicker-sm .ui-colorpicker-preview {
|
||||
width: 2rem;
|
||||
height: 2rem;
|
||||
}
|
||||
|
||||
&.ui-colorpicker-lg .ui-colorpicker-preview {
|
||||
width: 3rem;
|
||||
height: 3rem;
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// 📝 RICH TEXT EDITOR STYLES
|
||||
// ============================================
|
||||
|
||||
.ui-editor {
|
||||
display: block;
|
||||
width: 100%;
|
||||
@include lions-rounded('lg');
|
||||
border: 1px solid $lions-gray-300;
|
||||
background: $lions-white;
|
||||
@include lions-shadow('sm');
|
||||
overflow: hidden;
|
||||
|
||||
.ui-editor-toolbar {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: $lions-spacing-1;
|
||||
padding: $lions-spacing-3;
|
||||
background: $lions-gray-50;
|
||||
border-bottom: 1px solid $lions-gray-200;
|
||||
}
|
||||
|
||||
.ui-editor-toolbar-group {
|
||||
display: flex;
|
||||
gap: $lions-spacing-1;
|
||||
padding-right: $lions-spacing-2;
|
||||
border-right: 1px solid $lions-gray-300;
|
||||
|
||||
&:last-child {
|
||||
border-right: none;
|
||||
padding-right: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.ui-editor-toolbar-button {
|
||||
@include lions-button-base;
|
||||
width: 2rem;
|
||||
height: 2rem;
|
||||
min-width: 2rem;
|
||||
padding: 0;
|
||||
@include lions-flex-center;
|
||||
background: transparent;
|
||||
border: 1px solid transparent;
|
||||
@include lions-rounded('md');
|
||||
color: $lions-gray-600;
|
||||
cursor: pointer;
|
||||
@include lions-transition();
|
||||
|
||||
&:hover {
|
||||
background: $lions-white;
|
||||
border-color: $lions-gray-300;
|
||||
color: $lions-blue-600;
|
||||
}
|
||||
|
||||
&:active,
|
||||
&.ui-state-active {
|
||||
background: $lions-blue-100;
|
||||
border-color: $lions-blue-300;
|
||||
color: $lions-blue-700;
|
||||
}
|
||||
|
||||
.ui-icon {
|
||||
font-size: 1rem;
|
||||
}
|
||||
}
|
||||
|
||||
.ui-editor-content {
|
||||
min-height: 200px;
|
||||
max-height: 500px;
|
||||
overflow-y: auto;
|
||||
padding: $lions-spacing-4;
|
||||
@include lions-font-size('base');
|
||||
line-height: 1.6;
|
||||
color: $lions-text-primary;
|
||||
|
||||
&:focus {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
// Typography inside editor
|
||||
h1, h2, h3, h4, h5, h6 {
|
||||
margin-top: $lions-spacing-4;
|
||||
margin-bottom: $lions-spacing-3;
|
||||
@include lions-font-weight('bold');
|
||||
color: $lions-text-primary;
|
||||
}
|
||||
|
||||
h1 { @include lions-font-size('3xl'); }
|
||||
h2 { @include lions-font-size('2xl'); }
|
||||
h3 { @include lions-font-size('xl'); }
|
||||
h4 { @include lions-font-size('lg'); }
|
||||
h5 { @include lions-font-size('base'); }
|
||||
h6 { @include lions-font-size('sm'); }
|
||||
|
||||
p {
|
||||
margin-bottom: $lions-spacing-3;
|
||||
}
|
||||
|
||||
a {
|
||||
color: $lions-blue-600;
|
||||
text-decoration: underline;
|
||||
|
||||
&:hover {
|
||||
color: $lions-blue-700;
|
||||
}
|
||||
}
|
||||
|
||||
ul, ol {
|
||||
margin-left: $lions-spacing-6;
|
||||
margin-bottom: $lions-spacing-3;
|
||||
}
|
||||
|
||||
li {
|
||||
margin-bottom: $lions-spacing-1;
|
||||
}
|
||||
|
||||
blockquote {
|
||||
border-left: 4px solid $lions-blue-500;
|
||||
padding-left: $lions-spacing-4;
|
||||
margin-left: 0;
|
||||
margin-bottom: $lions-spacing-3;
|
||||
color: $lions-gray-600;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
pre {
|
||||
background: $lions-gray-100;
|
||||
@include lions-rounded('md');
|
||||
padding: $lions-spacing-4;
|
||||
margin-bottom: $lions-spacing-3;
|
||||
overflow-x: auto;
|
||||
font-family: $lions-font-family-mono;
|
||||
@include lions-font-size('sm');
|
||||
}
|
||||
|
||||
code {
|
||||
background: $lions-gray-100;
|
||||
padding: $lions-spacing-1 $lions-spacing-2;
|
||||
@include lions-rounded('sm');
|
||||
font-family: $lions-font-family-mono;
|
||||
@include lions-font-size('sm');
|
||||
color: $lions-danger-600;
|
||||
}
|
||||
|
||||
img {
|
||||
max-width: 100%;
|
||||
height: auto;
|
||||
@include lions-rounded('md');
|
||||
margin: $lions-spacing-3 0;
|
||||
}
|
||||
|
||||
table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
margin-bottom: $lions-spacing-3;
|
||||
|
||||
th, td {
|
||||
border: 1px solid $lions-gray-300;
|
||||
padding: $lions-spacing-3;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
th {
|
||||
background: $lions-gray-100;
|
||||
@include lions-font-weight('semibold');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Focused state
|
||||
&.ui-state-focus {
|
||||
border-color: $lions-blue-500;
|
||||
box-shadow: 0 0 0 3px rgba($lions-blue-500, 0.15);
|
||||
}
|
||||
|
||||
// Disabled state
|
||||
&.ui-state-disabled {
|
||||
opacity: 0.6;
|
||||
cursor: not-allowed;
|
||||
|
||||
.ui-editor-toolbar,
|
||||
.ui-editor-content {
|
||||
pointer-events: none;
|
||||
}
|
||||
}
|
||||
|
||||
// Size variants
|
||||
&.ui-editor-sm {
|
||||
.ui-editor-toolbar {
|
||||
padding: $lions-spacing-2;
|
||||
}
|
||||
|
||||
.ui-editor-toolbar-button {
|
||||
width: 1.75rem;
|
||||
height: 1.75rem;
|
||||
min-width: 1.75rem;
|
||||
|
||||
.ui-icon {
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
}
|
||||
|
||||
.ui-editor-content {
|
||||
min-height: 150px;
|
||||
padding: $lions-spacing-3;
|
||||
@include lions-font-size('sm');
|
||||
}
|
||||
}
|
||||
|
||||
&.ui-editor-lg {
|
||||
.ui-editor-toolbar {
|
||||
padding: $lions-spacing-4;
|
||||
}
|
||||
|
||||
.ui-editor-toolbar-button {
|
||||
width: 2.5rem;
|
||||
height: 2.5rem;
|
||||
min-width: 2.5rem;
|
||||
|
||||
.ui-icon {
|
||||
font-size: 1.125rem;
|
||||
}
|
||||
}
|
||||
|
||||
.ui-editor-content {
|
||||
min-height: 300px;
|
||||
padding: $lions-spacing-5;
|
||||
@include lions-font-size('lg');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// 📂 FILE UPLOAD STYLES
|
||||
// ============================================
|
||||
|
||||
.ui-fileupload {
|
||||
display: block;
|
||||
width: 100%;
|
||||
@include lions-rounded('lg');
|
||||
border: 1px solid $lions-gray-300;
|
||||
background: $lions-white;
|
||||
overflow: hidden;
|
||||
|
||||
.ui-fileupload-buttonbar {
|
||||
display: flex;
|
||||
gap: $lions-spacing-3;
|
||||
padding: $lions-spacing-4;
|
||||
background: $lions-gray-50;
|
||||
border-bottom: 1px solid $lions-gray-200;
|
||||
}
|
||||
|
||||
.ui-fileupload-choose {
|
||||
@include lions-button-primary;
|
||||
}
|
||||
|
||||
.ui-fileupload-upload,
|
||||
.ui-fileupload-cancel {
|
||||
@include lions-button-base;
|
||||
padding: $lions-spacing-3 $lions-spacing-5;
|
||||
@include lions-rounded('md');
|
||||
@include lions-font-weight('semibold');
|
||||
@include lions-transition();
|
||||
}
|
||||
|
||||
.ui-fileupload-upload {
|
||||
background: $lions-success-500;
|
||||
color: $lions-white;
|
||||
|
||||
&:hover {
|
||||
background: $lions-success-600;
|
||||
}
|
||||
}
|
||||
|
||||
.ui-fileupload-cancel {
|
||||
background: $lions-gray-200;
|
||||
color: $lions-gray-700;
|
||||
|
||||
&:hover {
|
||||
background: $lions-gray-300;
|
||||
}
|
||||
}
|
||||
|
||||
.ui-fileupload-content {
|
||||
padding: $lions-spacing-4;
|
||||
}
|
||||
|
||||
.ui-fileupload-files {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: $lions-spacing-3;
|
||||
}
|
||||
|
||||
.ui-fileupload-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: $lions-spacing-3;
|
||||
padding: $lions-spacing-3;
|
||||
@include lions-rounded('md');
|
||||
background: $lions-gray-50;
|
||||
border: 1px solid $lions-gray-200;
|
||||
@include lions-transition();
|
||||
|
||||
&:hover {
|
||||
background: $lions-gray-100;
|
||||
}
|
||||
}
|
||||
|
||||
.ui-fileupload-filename {
|
||||
flex: 1;
|
||||
@include lions-font-size('sm');
|
||||
@include lions-font-weight('medium');
|
||||
color: $lions-text-primary;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.ui-fileupload-size {
|
||||
@include lions-font-size('xs');
|
||||
color: $lions-gray-600;
|
||||
}
|
||||
|
||||
.ui-fileupload-remove {
|
||||
@include lions-button-base;
|
||||
width: 2rem;
|
||||
height: 2rem;
|
||||
min-width: 2rem;
|
||||
padding: 0;
|
||||
@include lions-flex-center;
|
||||
background: transparent;
|
||||
border: 1px solid $lions-gray-300;
|
||||
@include lions-rounded('md');
|
||||
color: $lions-danger-600;
|
||||
cursor: pointer;
|
||||
@include lions-transition();
|
||||
|
||||
&:hover {
|
||||
background: $lions-danger-50;
|
||||
border-color: $lions-danger-300;
|
||||
}
|
||||
|
||||
.ui-icon {
|
||||
font-size: 1rem;
|
||||
}
|
||||
}
|
||||
|
||||
// Drag & Drop zone
|
||||
&.ui-fileupload-advanced {
|
||||
.ui-fileupload-content {
|
||||
position: relative;
|
||||
min-height: 200px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border: 2px dashed $lions-gray-300;
|
||||
@include lions-rounded('lg');
|
||||
margin: $lions-spacing-4;
|
||||
@include lions-transition();
|
||||
|
||||
&.ui-fileupload-highlight {
|
||||
border-color: $lions-blue-500;
|
||||
background: rgba($lions-blue-50, 0.5);
|
||||
}
|
||||
}
|
||||
|
||||
.ui-fileupload-empty {
|
||||
text-align: center;
|
||||
padding: $lions-spacing-6;
|
||||
|
||||
.ui-fileupload-drag-icon {
|
||||
font-size: 3rem;
|
||||
color: $lions-gray-400;
|
||||
margin-bottom: $lions-spacing-3;
|
||||
}
|
||||
|
||||
.ui-fileupload-drag-text {
|
||||
@include lions-font-size('base');
|
||||
color: $lions-gray-600;
|
||||
margin-bottom: $lions-spacing-2;
|
||||
}
|
||||
|
||||
.ui-fileupload-drag-hint {
|
||||
@include lions-font-size('sm');
|
||||
color: $lions-gray-500;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Progress bar
|
||||
.ui-fileupload-progress {
|
||||
height: 0.5rem;
|
||||
@include lions-rounded('full');
|
||||
background: $lions-gray-200;
|
||||
overflow: hidden;
|
||||
margin-top: $lions-spacing-2;
|
||||
|
||||
.ui-fileupload-progress-bar {
|
||||
height: 100%;
|
||||
@include lions-gradient-blue;
|
||||
@include lions-transition();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// 📱 RESPONSIVE ADJUSTMENTS
|
||||
// ============================================
|
||||
|
||||
@media (max-width: $lions-breakpoint-md) {
|
||||
.ui-editor {
|
||||
.ui-editor-toolbar {
|
||||
padding: $lions-spacing-2;
|
||||
}
|
||||
|
||||
.ui-editor-toolbar-button {
|
||||
width: 1.75rem;
|
||||
height: 1.75rem;
|
||||
min-width: 1.75rem;
|
||||
}
|
||||
|
||||
.ui-editor-content {
|
||||
min-height: 150px;
|
||||
}
|
||||
}
|
||||
|
||||
.ui-fileupload {
|
||||
.ui-fileupload-buttonbar {
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
&.ui-fileupload-advanced .ui-fileupload-content {
|
||||
min-height: 150px;
|
||||
}
|
||||
}
|
||||
|
||||
.ui-colorpicker-panel {
|
||||
.ui-colorpicker-color-selector {
|
||||
width: 150px;
|
||||
height: 120px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// ♿ ACCESSIBILITY ENHANCEMENTS
|
||||
// ============================================
|
||||
|
||||
.ui-spinner,
|
||||
.ui-inputmask,
|
||||
.ui-chips,
|
||||
.ui-colorpicker,
|
||||
.ui-editor,
|
||||
.ui-fileupload {
|
||||
// Focus visible
|
||||
&:focus-visible,
|
||||
.ui-state-focus {
|
||||
@include lions-focus-ring();
|
||||
}
|
||||
|
||||
// High contrast mode
|
||||
@media (prefers-contrast: high) {
|
||||
border-width: 2px !important;
|
||||
|
||||
button,
|
||||
.ui-button {
|
||||
border-width: 2px !important;
|
||||
}
|
||||
}
|
||||
|
||||
// Reduced motion
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
* {
|
||||
transition: none !important;
|
||||
animation: none !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,980 @@
|
||||
/*
|
||||
* ═══════════════════════════════════════════════════════════
|
||||
* Lions.dev - Form Controls Component Styles
|
||||
* ═══════════════════════════════════════════════════════════
|
||||
* Styles pour Checkbox, Radio, Switch, Toggle, Slider, Rating
|
||||
*
|
||||
* Version: 1.0.0
|
||||
* Date: 2 Janvier 2026
|
||||
* Author: Lions Development Team
|
||||
* Priority: CRITIQUE #28-33
|
||||
* ═══════════════════════════════════════════════════════════
|
||||
*/
|
||||
|
||||
@import '../variables';
|
||||
@import '../mixins';
|
||||
|
||||
// ============================================
|
||||
// ☑️ CHECKBOX STYLES
|
||||
// ============================================
|
||||
|
||||
.ui-chkbox {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: $lions-spacing-2;
|
||||
cursor: pointer;
|
||||
@include lions-transition();
|
||||
|
||||
.ui-chkbox-box {
|
||||
width: 1.25rem;
|
||||
height: 1.25rem;
|
||||
@include lions-rounded('md');
|
||||
border: 2px solid $lions-gray-400;
|
||||
background: $lions-white;
|
||||
@include lions-flex-center;
|
||||
@include lions-transition();
|
||||
flex-shrink: 0;
|
||||
|
||||
.ui-chkbox-icon {
|
||||
font-size: 0.875rem;
|
||||
color: $lions-white;
|
||||
opacity: 0;
|
||||
transform: scale(0.5);
|
||||
@include lions-transition();
|
||||
}
|
||||
|
||||
&:hover {
|
||||
border-color: $lions-blue-500;
|
||||
background: rgba($lions-blue-50, 0.3);
|
||||
}
|
||||
|
||||
&:focus-visible {
|
||||
@include lions-focus-ring();
|
||||
}
|
||||
|
||||
&.ui-state-active {
|
||||
@include lions-gradient-blue;
|
||||
border-color: $lions-blue-600;
|
||||
box-shadow: 0 2px 4px rgba($lions-blue-500, 0.2);
|
||||
|
||||
.ui-chkbox-icon {
|
||||
opacity: 1;
|
||||
transform: scale(1);
|
||||
}
|
||||
}
|
||||
|
||||
&.ui-state-disabled {
|
||||
background: $lions-gray-100;
|
||||
border-color: $lions-gray-300;
|
||||
cursor: not-allowed;
|
||||
opacity: 0.6;
|
||||
}
|
||||
}
|
||||
|
||||
.ui-chkbox-label {
|
||||
cursor: pointer;
|
||||
color: $lions-text-primary;
|
||||
@include lions-font-size('base');
|
||||
@include lions-font-weight('medium');
|
||||
user-select: none;
|
||||
@include lions-transition();
|
||||
|
||||
&:hover {
|
||||
color: $lions-blue-700;
|
||||
}
|
||||
}
|
||||
|
||||
// Size variants
|
||||
&.ui-chkbox-sm {
|
||||
.ui-chkbox-box {
|
||||
width: 1rem;
|
||||
height: 1rem;
|
||||
|
||||
.ui-chkbox-icon {
|
||||
font-size: 0.75rem;
|
||||
}
|
||||
}
|
||||
|
||||
.ui-chkbox-label {
|
||||
@include lions-font-size('sm');
|
||||
}
|
||||
}
|
||||
|
||||
&.ui-chkbox-lg {
|
||||
.ui-chkbox-box {
|
||||
width: 1.5rem;
|
||||
height: 1.5rem;
|
||||
|
||||
.ui-chkbox-icon {
|
||||
font-size: 1rem;
|
||||
}
|
||||
}
|
||||
|
||||
.ui-chkbox-label {
|
||||
@include lions-font-size('lg');
|
||||
}
|
||||
}
|
||||
|
||||
// State error
|
||||
&.ui-state-error {
|
||||
.ui-chkbox-box {
|
||||
border-color: $lions-danger-500;
|
||||
|
||||
&.ui-state-active {
|
||||
@include lions-gradient-danger;
|
||||
}
|
||||
}
|
||||
|
||||
.ui-chkbox-label {
|
||||
color: $lions-danger-700;
|
||||
}
|
||||
}
|
||||
|
||||
// State disabled
|
||||
&.ui-state-disabled {
|
||||
cursor: not-allowed;
|
||||
opacity: 0.6;
|
||||
|
||||
.ui-chkbox-label {
|
||||
cursor: not-allowed;
|
||||
color: $lions-gray-500;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// 🔘 RADIO BUTTON STYLES
|
||||
// ============================================
|
||||
|
||||
.ui-radiobutton {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: $lions-spacing-2;
|
||||
cursor: pointer;
|
||||
@include lions-transition();
|
||||
|
||||
.ui-radiobutton-box {
|
||||
width: 1.25rem;
|
||||
height: 1.25rem;
|
||||
@include lions-rounded('full');
|
||||
border: 2px solid $lions-gray-400;
|
||||
background: $lions-white;
|
||||
@include lions-flex-center;
|
||||
@include lions-transition();
|
||||
flex-shrink: 0;
|
||||
|
||||
.ui-radiobutton-icon {
|
||||
width: 0.625rem;
|
||||
height: 0.625rem;
|
||||
@include lions-rounded('full');
|
||||
background: $lions-white;
|
||||
opacity: 0;
|
||||
transform: scale(0);
|
||||
@include lions-transition();
|
||||
}
|
||||
|
||||
&:hover {
|
||||
border-color: $lions-blue-500;
|
||||
background: rgba($lions-blue-50, 0.3);
|
||||
}
|
||||
|
||||
&:focus-visible {
|
||||
@include lions-focus-ring();
|
||||
}
|
||||
|
||||
&.ui-state-active {
|
||||
@include lions-gradient-blue;
|
||||
border-color: $lions-blue-600;
|
||||
box-shadow: 0 2px 4px rgba($lions-blue-500, 0.2);
|
||||
|
||||
.ui-radiobutton-icon {
|
||||
opacity: 1;
|
||||
transform: scale(1);
|
||||
}
|
||||
}
|
||||
|
||||
&.ui-state-disabled {
|
||||
background: $lions-gray-100;
|
||||
border-color: $lions-gray-300;
|
||||
cursor: not-allowed;
|
||||
opacity: 0.6;
|
||||
}
|
||||
}
|
||||
|
||||
.ui-radiobutton-label {
|
||||
cursor: pointer;
|
||||
color: $lions-text-primary;
|
||||
@include lions-font-size('base');
|
||||
@include lions-font-weight('medium');
|
||||
user-select: none;
|
||||
@include lions-transition();
|
||||
|
||||
&:hover {
|
||||
color: $lions-blue-700;
|
||||
}
|
||||
}
|
||||
|
||||
// Size variants
|
||||
&.ui-radiobutton-sm {
|
||||
.ui-radiobutton-box {
|
||||
width: 1rem;
|
||||
height: 1rem;
|
||||
|
||||
.ui-radiobutton-icon {
|
||||
width: 0.5rem;
|
||||
height: 0.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
.ui-radiobutton-label {
|
||||
@include lions-font-size('sm');
|
||||
}
|
||||
}
|
||||
|
||||
&.ui-radiobutton-lg {
|
||||
.ui-radiobutton-box {
|
||||
width: 1.5rem;
|
||||
height: 1.5rem;
|
||||
|
||||
.ui-radiobutton-icon {
|
||||
width: 0.75rem;
|
||||
height: 0.75rem;
|
||||
}
|
||||
}
|
||||
|
||||
.ui-radiobutton-label {
|
||||
@include lions-font-size('lg');
|
||||
}
|
||||
}
|
||||
|
||||
// State error
|
||||
&.ui-state-error {
|
||||
.ui-radiobutton-box {
|
||||
border-color: $lions-danger-500;
|
||||
|
||||
&.ui-state-active {
|
||||
@include lions-gradient-danger;
|
||||
}
|
||||
}
|
||||
|
||||
.ui-radiobutton-label {
|
||||
color: $lions-danger-700;
|
||||
}
|
||||
}
|
||||
|
||||
// State disabled
|
||||
&.ui-state-disabled {
|
||||
cursor: not-allowed;
|
||||
opacity: 0.6;
|
||||
|
||||
.ui-radiobutton-label {
|
||||
cursor: not-allowed;
|
||||
color: $lions-gray-500;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// 🔀 SWITCH TOGGLE STYLES
|
||||
// ============================================
|
||||
|
||||
.ui-inputswitch {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: $lions-spacing-2;
|
||||
|
||||
.ui-inputswitch-slider {
|
||||
width: 2.75rem;
|
||||
height: 1.5rem;
|
||||
@include lions-rounded('full');
|
||||
background: $lions-gray-300;
|
||||
border: 2px solid $lions-gray-400;
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
@include lions-transition();
|
||||
flex-shrink: 0;
|
||||
|
||||
&::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 2px;
|
||||
left: 2px;
|
||||
width: 1.125rem;
|
||||
height: 1.125rem;
|
||||
@include lions-rounded('full');
|
||||
background: $lions-white;
|
||||
@include lions-shadow('sm');
|
||||
@include lions-transition();
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background: $lions-gray-400;
|
||||
border-color: $lions-gray-500;
|
||||
}
|
||||
|
||||
&:focus-visible {
|
||||
@include lions-focus-ring();
|
||||
}
|
||||
|
||||
&.ui-inputswitch-checked {
|
||||
@include lions-gradient-blue;
|
||||
border-color: $lions-blue-600;
|
||||
|
||||
&::before {
|
||||
left: calc(100% - 1.125rem - 2px);
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background: linear-gradient(135deg, darken($lions-blue-500, 5%) 0%, darken($lions-blue-700, 5%) 100%);
|
||||
}
|
||||
}
|
||||
|
||||
&.ui-state-disabled {
|
||||
background: $lions-gray-200;
|
||||
border-color: $lions-gray-300;
|
||||
cursor: not-allowed;
|
||||
opacity: 0.6;
|
||||
|
||||
&::before {
|
||||
background: $lions-gray-300;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Size variants
|
||||
&.ui-inputswitch-sm {
|
||||
.ui-inputswitch-slider {
|
||||
width: 2.25rem;
|
||||
height: 1.25rem;
|
||||
|
||||
&::before {
|
||||
width: 0.875rem;
|
||||
height: 0.875rem;
|
||||
}
|
||||
|
||||
&.ui-inputswitch-checked::before {
|
||||
left: calc(100% - 0.875rem - 2px);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.ui-inputswitch-lg {
|
||||
.ui-inputswitch-slider {
|
||||
width: 3.25rem;
|
||||
height: 1.75rem;
|
||||
|
||||
&::before {
|
||||
width: 1.375rem;
|
||||
height: 1.375rem;
|
||||
}
|
||||
|
||||
&.ui-inputswitch-checked::before {
|
||||
left: calc(100% - 1.375rem - 2px);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Success variant
|
||||
&.ui-inputswitch-success {
|
||||
.ui-inputswitch-slider.ui-inputswitch-checked {
|
||||
@include lions-gradient-success;
|
||||
border-color: $lions-success-600;
|
||||
}
|
||||
}
|
||||
|
||||
// Danger variant
|
||||
&.ui-inputswitch-danger {
|
||||
.ui-inputswitch-slider.ui-inputswitch-checked {
|
||||
@include lions-gradient-danger;
|
||||
border-color: $lions-danger-600;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// 🎚️ SLIDER STYLES
|
||||
// ============================================
|
||||
|
||||
.ui-slider {
|
||||
background: $lions-gray-200;
|
||||
border: none;
|
||||
@include lions-rounded('full');
|
||||
position: relative;
|
||||
height: 0.5rem;
|
||||
cursor: pointer;
|
||||
|
||||
.ui-slider-range {
|
||||
@include lions-gradient-blue;
|
||||
@include lions-rounded('full');
|
||||
position: absolute;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.ui-slider-handle {
|
||||
width: 1.25rem;
|
||||
height: 1.25rem;
|
||||
@include lions-rounded('full');
|
||||
background: $lions-white;
|
||||
border: 3px solid $lions-blue-500;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
cursor: grab;
|
||||
@include lions-shadow('md');
|
||||
@include lions-transition();
|
||||
|
||||
&:hover {
|
||||
transform: translateY(-50%) scale(1.1);
|
||||
border-color: $lions-blue-600;
|
||||
@include lions-shadow('lg');
|
||||
}
|
||||
|
||||
&:active {
|
||||
cursor: grabbing;
|
||||
transform: translateY(-50%) scale(1.15);
|
||||
@include lions-shadow('xl');
|
||||
}
|
||||
|
||||
&:focus-visible {
|
||||
@include lions-focus-ring();
|
||||
}
|
||||
}
|
||||
|
||||
// Horizontal slider
|
||||
&.ui-slider-horizontal {
|
||||
width: 100%;
|
||||
|
||||
.ui-slider-handle {
|
||||
left: 0;
|
||||
margin-left: -0.625rem;
|
||||
}
|
||||
|
||||
.ui-slider-range {
|
||||
left: 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Vertical slider
|
||||
&.ui-slider-vertical {
|
||||
width: 0.5rem;
|
||||
height: 200px;
|
||||
|
||||
.ui-slider-handle {
|
||||
left: 50%;
|
||||
bottom: 0;
|
||||
margin-left: -0.625rem;
|
||||
margin-bottom: -0.625rem;
|
||||
}
|
||||
|
||||
.ui-slider-range {
|
||||
bottom: 0;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
// Size variants
|
||||
&.ui-slider-sm {
|
||||
height: 0.375rem;
|
||||
|
||||
.ui-slider-handle {
|
||||
width: 1rem;
|
||||
height: 1rem;
|
||||
}
|
||||
|
||||
&.ui-slider-horizontal .ui-slider-handle {
|
||||
margin-left: -0.5rem;
|
||||
}
|
||||
|
||||
&.ui-slider-vertical {
|
||||
width: 0.375rem;
|
||||
|
||||
.ui-slider-handle {
|
||||
margin-left: -0.5rem;
|
||||
margin-bottom: -0.5rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.ui-slider-lg {
|
||||
height: 0.625rem;
|
||||
|
||||
.ui-slider-handle {
|
||||
width: 1.5rem;
|
||||
height: 1.5rem;
|
||||
}
|
||||
|
||||
&.ui-slider-horizontal .ui-slider-handle {
|
||||
margin-left: -0.75rem;
|
||||
}
|
||||
|
||||
&.ui-slider-vertical {
|
||||
width: 0.625rem;
|
||||
|
||||
.ui-slider-handle {
|
||||
margin-left: -0.75rem;
|
||||
margin-bottom: -0.75rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// State disabled
|
||||
&.ui-state-disabled {
|
||||
opacity: 0.6;
|
||||
cursor: not-allowed;
|
||||
|
||||
.ui-slider-handle {
|
||||
cursor: not-allowed;
|
||||
background: $lions-gray-200;
|
||||
border-color: $lions-gray-400;
|
||||
}
|
||||
|
||||
.ui-slider-range {
|
||||
background: $lions-gray-400;
|
||||
}
|
||||
}
|
||||
|
||||
// Color variants
|
||||
&.ui-slider-success {
|
||||
.ui-slider-range {
|
||||
@include lions-gradient-success;
|
||||
}
|
||||
|
||||
.ui-slider-handle {
|
||||
border-color: $lions-success-500;
|
||||
}
|
||||
}
|
||||
|
||||
&.ui-slider-danger {
|
||||
.ui-slider-range {
|
||||
@include lions-gradient-danger;
|
||||
}
|
||||
|
||||
.ui-slider-handle {
|
||||
border-color: $lions-danger-500;
|
||||
}
|
||||
}
|
||||
|
||||
&.ui-slider-warning {
|
||||
.ui-slider-range {
|
||||
@include lions-gradient-warning;
|
||||
}
|
||||
|
||||
.ui-slider-handle {
|
||||
border-color: $lions-warning-500;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// ⭐ RATING STYLES
|
||||
// ============================================
|
||||
|
||||
.ui-rating {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: $lions-spacing-1;
|
||||
|
||||
.ui-rating-icon {
|
||||
font-size: 1.5rem;
|
||||
color: $lions-gray-300;
|
||||
cursor: pointer;
|
||||
@include lions-transition();
|
||||
|
||||
&:hover {
|
||||
color: $lions-gold-400;
|
||||
transform: scale(1.15);
|
||||
}
|
||||
|
||||
&:active {
|
||||
transform: scale(1.05);
|
||||
}
|
||||
|
||||
&.ui-rating-icon-active {
|
||||
color: $lions-gold-500;
|
||||
text-shadow: 0 2px 4px rgba($lions-gold-500, 0.3);
|
||||
}
|
||||
|
||||
&.ui-rating-icon-hover {
|
||||
color: $lions-gold-400;
|
||||
}
|
||||
}
|
||||
|
||||
.ui-rating-cancel {
|
||||
color: $lions-danger-500;
|
||||
margin-right: $lions-spacing-2;
|
||||
|
||||
&:hover {
|
||||
color: $lions-danger-600;
|
||||
transform: scale(1.15);
|
||||
}
|
||||
}
|
||||
|
||||
// Size variants
|
||||
&.ui-rating-sm {
|
||||
.ui-rating-icon {
|
||||
font-size: 1.125rem;
|
||||
}
|
||||
}
|
||||
|
||||
&.ui-rating-lg {
|
||||
.ui-rating-icon {
|
||||
font-size: 2rem;
|
||||
}
|
||||
}
|
||||
|
||||
// State disabled
|
||||
&.ui-state-disabled {
|
||||
opacity: 0.6;
|
||||
|
||||
.ui-rating-icon {
|
||||
cursor: not-allowed;
|
||||
|
||||
&:hover {
|
||||
transform: none;
|
||||
color: $lions-gray-300;
|
||||
}
|
||||
|
||||
&.ui-rating-icon-active {
|
||||
color: $lions-gold-500;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Readonly
|
||||
&.ui-rating-readonly {
|
||||
.ui-rating-icon {
|
||||
cursor: default;
|
||||
|
||||
&:hover {
|
||||
transform: none;
|
||||
}
|
||||
|
||||
&.ui-rating-icon-active {
|
||||
color: $lions-gold-500;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Heart variant
|
||||
&.ui-rating-hearts {
|
||||
.ui-rating-icon {
|
||||
color: $lions-gray-300;
|
||||
|
||||
&.ui-rating-icon-active {
|
||||
color: $lions-danger-500;
|
||||
text-shadow: 0 2px 4px rgba($lions-danger-500, 0.3);
|
||||
}
|
||||
|
||||
&:hover {
|
||||
color: $lions-danger-400;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// 🔢 SPINNER STYLES
|
||||
// ============================================
|
||||
|
||||
.ui-spinner {
|
||||
display: inline-flex;
|
||||
align-items: stretch;
|
||||
@include lions-rounded('md');
|
||||
overflow: hidden;
|
||||
border: 1px solid $lions-gray-300;
|
||||
background: $lions-white;
|
||||
@include lions-transition();
|
||||
|
||||
&:hover {
|
||||
border-color: $lions-gray-400;
|
||||
}
|
||||
|
||||
&:focus-within {
|
||||
border-color: $lions-blue-500;
|
||||
box-shadow: 0 0 0 3px rgba($lions-blue-500, 0.15);
|
||||
}
|
||||
|
||||
.ui-spinner-input {
|
||||
flex: 1;
|
||||
border: none;
|
||||
padding: $lions-spacing-3 $lions-spacing-4;
|
||||
font-family: $lions-font-family-primary;
|
||||
@include lions-font-size('base');
|
||||
color: $lions-text-primary;
|
||||
text-align: center;
|
||||
min-width: 80px;
|
||||
|
||||
&:focus {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
&::placeholder {
|
||||
color: $lions-gray-400;
|
||||
}
|
||||
}
|
||||
|
||||
.ui-spinner-button {
|
||||
@include lions-button-base;
|
||||
width: 2.5rem;
|
||||
background: $lions-gray-100;
|
||||
border: none;
|
||||
border-left: 1px solid $lions-gray-300;
|
||||
color: $lions-gray-700;
|
||||
@include lions-flex-center;
|
||||
cursor: pointer;
|
||||
@include lions-transition();
|
||||
|
||||
&:hover {
|
||||
background: $lions-blue-50;
|
||||
color: $lions-blue-600;
|
||||
}
|
||||
|
||||
&:active {
|
||||
background: $lions-blue-100;
|
||||
}
|
||||
|
||||
&:first-of-type {
|
||||
border-right: 1px solid $lions-gray-300;
|
||||
border-left: none;
|
||||
}
|
||||
|
||||
.ui-icon {
|
||||
font-size: 1rem;
|
||||
}
|
||||
}
|
||||
|
||||
// Vertical layout
|
||||
&.ui-spinner-vertical {
|
||||
flex-direction: column;
|
||||
|
||||
.ui-spinner-button {
|
||||
border-left: none;
|
||||
border-top: 1px solid $lions-gray-300;
|
||||
width: 100%;
|
||||
height: 1.5rem;
|
||||
|
||||
&:first-of-type {
|
||||
border-top: none;
|
||||
border-bottom: 1px solid $lions-gray-300;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Size variants
|
||||
&.ui-spinner-sm {
|
||||
.ui-spinner-input {
|
||||
padding: $lions-spacing-2 $lions-spacing-3;
|
||||
@include lions-font-size('sm');
|
||||
min-width: 60px;
|
||||
}
|
||||
|
||||
.ui-spinner-button {
|
||||
width: 2rem;
|
||||
}
|
||||
}
|
||||
|
||||
&.ui-spinner-lg {
|
||||
.ui-spinner-input {
|
||||
padding: $lions-spacing-4 $lions-spacing-5;
|
||||
@include lions-font-size('lg');
|
||||
min-width: 100px;
|
||||
}
|
||||
|
||||
.ui-spinner-button {
|
||||
width: 3rem;
|
||||
}
|
||||
}
|
||||
|
||||
// State disabled
|
||||
&.ui-state-disabled {
|
||||
background: $lions-gray-100;
|
||||
opacity: 0.6;
|
||||
cursor: not-allowed;
|
||||
|
||||
.ui-spinner-input {
|
||||
cursor: not-allowed;
|
||||
color: $lions-gray-500;
|
||||
}
|
||||
|
||||
.ui-spinner-button {
|
||||
cursor: not-allowed;
|
||||
background: $lions-gray-100;
|
||||
color: $lions-gray-400;
|
||||
|
||||
&:hover {
|
||||
background: $lions-gray-100;
|
||||
color: $lions-gray-400;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// State error
|
||||
&.ui-state-error {
|
||||
border-color: $lions-danger-500;
|
||||
|
||||
&:focus-within {
|
||||
box-shadow: 0 0 0 3px rgba($lions-danger-500, 0.15);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// 🔄 TOGGLE BUTTON STYLES
|
||||
// ============================================
|
||||
|
||||
.ui-togglebutton {
|
||||
@include lions-button-base;
|
||||
padding: $lions-spacing-3 $lions-spacing-5;
|
||||
@include lions-rounded('md');
|
||||
border: 2px solid $lions-gray-400;
|
||||
background: $lions-white;
|
||||
color: $lions-text-primary;
|
||||
@include lions-font-weight('medium');
|
||||
cursor: pointer;
|
||||
@include lions-transition();
|
||||
|
||||
.ui-button-icon-left {
|
||||
margin-right: $lions-spacing-2;
|
||||
@include lions-transition();
|
||||
}
|
||||
|
||||
.ui-button-text {
|
||||
@include lions-font-size('base');
|
||||
}
|
||||
|
||||
&:hover {
|
||||
border-color: $lions-blue-500;
|
||||
background: rgba($lions-blue-50, 0.5);
|
||||
color: $lions-blue-700;
|
||||
}
|
||||
|
||||
&:active {
|
||||
transform: scale(0.98);
|
||||
}
|
||||
|
||||
&:focus-visible {
|
||||
@include lions-focus-ring();
|
||||
}
|
||||
|
||||
&.ui-state-active {
|
||||
@include lions-gradient-blue;
|
||||
border-color: $lions-blue-600;
|
||||
color: $lions-white;
|
||||
@include lions-shadow('md');
|
||||
|
||||
.ui-button-icon-left {
|
||||
color: $lions-white;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background: linear-gradient(135deg, darken($lions-blue-500, 5%) 0%, darken($lions-blue-700, 5%) 100%);
|
||||
}
|
||||
}
|
||||
|
||||
// Size variants
|
||||
&.ui-togglebutton-sm {
|
||||
padding: $lions-spacing-2 $lions-spacing-4;
|
||||
|
||||
.ui-button-text {
|
||||
@include lions-font-size('sm');
|
||||
}
|
||||
|
||||
.ui-button-icon-left {
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
}
|
||||
|
||||
&.ui-togglebutton-lg {
|
||||
padding: $lions-spacing-4 $lions-spacing-6;
|
||||
|
||||
.ui-button-text {
|
||||
@include lions-font-size('lg');
|
||||
}
|
||||
|
||||
.ui-button-icon-left {
|
||||
font-size: 1.25rem;
|
||||
}
|
||||
}
|
||||
|
||||
// State disabled
|
||||
&.ui-state-disabled {
|
||||
background: $lions-gray-100;
|
||||
border-color: $lions-gray-300;
|
||||
color: $lions-gray-500;
|
||||
cursor: not-allowed;
|
||||
opacity: 0.6;
|
||||
|
||||
&:hover {
|
||||
background: $lions-gray-100;
|
||||
border-color: $lions-gray-300;
|
||||
color: $lions-gray-500;
|
||||
transform: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// 📱 RESPONSIVE ADJUSTMENTS
|
||||
// ============================================
|
||||
|
||||
@media (max-width: $lions-breakpoint-md) {
|
||||
.ui-chkbox,
|
||||
.ui-radiobutton {
|
||||
gap: $lions-spacing-3;
|
||||
|
||||
.ui-chkbox-box,
|
||||
.ui-radiobutton-box {
|
||||
width: 1.375rem;
|
||||
height: 1.375rem;
|
||||
}
|
||||
}
|
||||
|
||||
.ui-rating {
|
||||
.ui-rating-icon {
|
||||
font-size: 1.25rem;
|
||||
}
|
||||
}
|
||||
|
||||
.ui-slider {
|
||||
&.ui-slider-vertical {
|
||||
height: 150px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// ♿ ACCESSIBILITY ENHANCEMENTS
|
||||
// ============================================
|
||||
|
||||
.ui-chkbox,
|
||||
.ui-radiobutton,
|
||||
.ui-inputswitch,
|
||||
.ui-slider,
|
||||
.ui-rating,
|
||||
.ui-spinner,
|
||||
.ui-togglebutton {
|
||||
// Focus visible
|
||||
*:focus-visible {
|
||||
@include lions-focus-ring();
|
||||
}
|
||||
|
||||
// High contrast mode
|
||||
@media (prefers-contrast: high) {
|
||||
border-width: 2px !important;
|
||||
|
||||
.ui-chkbox-box,
|
||||
.ui-radiobutton-box,
|
||||
.ui-inputswitch-slider,
|
||||
.ui-slider-handle {
|
||||
border-width: 3px !important;
|
||||
}
|
||||
}
|
||||
|
||||
// Reduced motion
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
* {
|
||||
transition: none !important;
|
||||
animation: none !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,608 @@
|
||||
/*
|
||||
* ═══════════════════════════════════════════════════════════
|
||||
* Lions.dev - Messages & Notifications Component Styles
|
||||
* ═══════════════════════════════════════════════════════════
|
||||
* Styles pour Messages, Toast, Growl et composants de notification
|
||||
*
|
||||
* Version: 1.0.0
|
||||
* Date: 1er Janvier 2026
|
||||
* Author: Lions Development Team
|
||||
* Priority: CRITIQUE #18-20
|
||||
* ═══════════════════════════════════════════════════════════
|
||||
*/
|
||||
|
||||
@import '../variables';
|
||||
@import '../mixins';
|
||||
|
||||
// ============================================
|
||||
// 💬 MESSAGES BASE STYLES
|
||||
// ============================================
|
||||
|
||||
.ui-messages,
|
||||
.ui-message {
|
||||
@include lions-rounded('md');
|
||||
padding: $lions-spacing-4 $lions-spacing-5;
|
||||
margin-bottom: $lions-spacing-3;
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: $lions-spacing-3;
|
||||
@include lions-transition();
|
||||
border-left: 4px solid;
|
||||
@include lions-shadow('sm');
|
||||
|
||||
// Icon
|
||||
.ui-messages-icon,
|
||||
.ui-message-icon {
|
||||
font-size: 1.5rem;
|
||||
margin-top: 2px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
// Content
|
||||
.ui-messages-detail,
|
||||
.ui-message-detail,
|
||||
.ui-messages-summary,
|
||||
.ui-message-summary {
|
||||
@include lions-font-size('base');
|
||||
margin: 0;
|
||||
line-height: $lions-line-height-normal;
|
||||
}
|
||||
|
||||
.ui-messages-summary,
|
||||
.ui-message-summary {
|
||||
@include lions-font-weight('bold');
|
||||
margin-bottom: $lions-spacing-1;
|
||||
}
|
||||
|
||||
.ui-messages-detail,
|
||||
.ui-message-detail {
|
||||
@include lions-font-weight('normal');
|
||||
}
|
||||
|
||||
// Close button
|
||||
.ui-messages-close,
|
||||
.ui-message-close {
|
||||
@include lions-button-base;
|
||||
width: 2rem;
|
||||
height: 2rem;
|
||||
min-width: 2rem;
|
||||
padding: 0;
|
||||
@include lions-flex-center;
|
||||
background: transparent;
|
||||
border: none;
|
||||
margin-left: auto;
|
||||
flex-shrink: 0;
|
||||
@include lions-transition();
|
||||
|
||||
&:hover {
|
||||
transform: scale(1.1);
|
||||
}
|
||||
|
||||
&:focus-visible {
|
||||
@include lions-focus-ring();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// 🎨 MESSAGE SEVERITY VARIANTS
|
||||
// ============================================
|
||||
|
||||
// Info messages
|
||||
.ui-messages-info,
|
||||
.ui-message-info {
|
||||
background: rgba($lions-info-50, 0.9);
|
||||
border-left-color: $lions-info-500;
|
||||
color: $lions-info-900;
|
||||
|
||||
.ui-messages-icon,
|
||||
.ui-message-icon {
|
||||
color: $lions-info-600;
|
||||
}
|
||||
|
||||
.ui-messages-close,
|
||||
.ui-message-close {
|
||||
color: $lions-info-700;
|
||||
|
||||
&:hover {
|
||||
background: rgba($lions-info-200, 0.8);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Success messages
|
||||
.ui-messages-success,
|
||||
.ui-message-success {
|
||||
background: rgba($lions-success-50, 0.9);
|
||||
border-left-color: $lions-success-500;
|
||||
color: $lions-success-900;
|
||||
|
||||
.ui-messages-icon,
|
||||
.ui-message-icon {
|
||||
color: $lions-success-600;
|
||||
}
|
||||
|
||||
.ui-messages-close,
|
||||
.ui-message-close {
|
||||
color: $lions-success-700;
|
||||
|
||||
&:hover {
|
||||
background: rgba($lions-success-200, 0.8);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Warning messages
|
||||
.ui-messages-warn,
|
||||
.ui-message-warn {
|
||||
background: rgba($lions-warning-50, 0.9);
|
||||
border-left-color: $lions-warning-500;
|
||||
color: $lions-warning-900;
|
||||
|
||||
.ui-messages-icon,
|
||||
.ui-message-icon {
|
||||
color: $lions-warning-600;
|
||||
}
|
||||
|
||||
.ui-messages-close,
|
||||
.ui-message-close {
|
||||
color: $lions-warning-700;
|
||||
|
||||
&:hover {
|
||||
background: rgba($lions-warning-200, 0.8);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Error messages
|
||||
.ui-messages-error,
|
||||
.ui-message-error {
|
||||
background: rgba($lions-danger-50, 0.9);
|
||||
border-left-color: $lions-danger-500;
|
||||
color: $lions-danger-900;
|
||||
|
||||
.ui-messages-icon,
|
||||
.ui-message-icon {
|
||||
color: $lions-danger-600;
|
||||
}
|
||||
|
||||
.ui-messages-close,
|
||||
.ui-message-close {
|
||||
color: $lions-danger-700;
|
||||
|
||||
&:hover {
|
||||
background: rgba($lions-danger-200, 0.8);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// 🔔 TOAST / GROWL NOTIFICATIONS
|
||||
// ============================================
|
||||
|
||||
.ui-toast,
|
||||
.ui-growl {
|
||||
position: fixed;
|
||||
z-index: 9999;
|
||||
|
||||
.ui-toast-message,
|
||||
.ui-growl-item {
|
||||
@include lions-rounded('lg');
|
||||
@include lions-shadow('xl');
|
||||
margin-bottom: $lions-spacing-3;
|
||||
overflow: hidden;
|
||||
background: $lions-white;
|
||||
border-left: 6px solid;
|
||||
min-width: 300px;
|
||||
max-width: 400px;
|
||||
@include lions-transition(all, 300ms);
|
||||
|
||||
&:hover {
|
||||
@include lions-shadow('2xl');
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
}
|
||||
|
||||
.ui-toast-message-content,
|
||||
.ui-growl-item-container {
|
||||
padding: $lions-spacing-4 $lions-spacing-5;
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: $lions-spacing-3;
|
||||
}
|
||||
|
||||
// Icon
|
||||
.ui-toast-icon,
|
||||
.ui-growl-icon {
|
||||
font-size: 1.75rem;
|
||||
flex-shrink: 0;
|
||||
margin-top: 2px;
|
||||
}
|
||||
|
||||
// Text container
|
||||
.ui-toast-message-text,
|
||||
.ui-growl-message {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
|
||||
.ui-toast-summary,
|
||||
.ui-growl-title {
|
||||
@include lions-font-size('lg');
|
||||
@include lions-font-weight('bold');
|
||||
margin: 0 0 $lions-spacing-1 0;
|
||||
line-height: $lions-line-height-tight;
|
||||
}
|
||||
|
||||
.ui-toast-detail,
|
||||
.ui-growl-detail {
|
||||
@include lions-font-size('base');
|
||||
margin: 0;
|
||||
line-height: $lions-line-height-normal;
|
||||
color: $lions-text-secondary;
|
||||
}
|
||||
}
|
||||
|
||||
// Close button
|
||||
.ui-toast-icon-close,
|
||||
.ui-growl-icon-close {
|
||||
@include lions-button-base;
|
||||
width: 2rem;
|
||||
height: 2rem;
|
||||
min-width: 2rem;
|
||||
padding: 0;
|
||||
@include lions-flex-center;
|
||||
background: transparent;
|
||||
border: none;
|
||||
flex-shrink: 0;
|
||||
@include lions-transition();
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
transform: scale(1.15) rotate(90deg);
|
||||
}
|
||||
|
||||
&:focus-visible {
|
||||
@include lions-focus-ring();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// 🎨 TOAST/GROWL SEVERITY VARIANTS
|
||||
// ============================================
|
||||
|
||||
.ui-toast,
|
||||
.ui-growl {
|
||||
// Info notifications
|
||||
.ui-toast-message-info,
|
||||
.ui-growl-item-info {
|
||||
border-left-color: $lions-info-500;
|
||||
background: linear-gradient(135deg, rgba($lions-info-50, 0.95) 0%, $lions-white 100%);
|
||||
|
||||
.ui-toast-icon,
|
||||
.ui-growl-icon {
|
||||
color: $lions-info-600;
|
||||
}
|
||||
|
||||
.ui-toast-summary,
|
||||
.ui-growl-title {
|
||||
color: $lions-info-800;
|
||||
}
|
||||
|
||||
.ui-toast-icon-close,
|
||||
.ui-growl-icon-close {
|
||||
color: $lions-info-700;
|
||||
|
||||
&:hover {
|
||||
background: rgba($lions-info-200, 0.6);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Success notifications
|
||||
.ui-toast-message-success,
|
||||
.ui-growl-item-success {
|
||||
border-left-color: $lions-success-500;
|
||||
background: linear-gradient(135deg, rgba($lions-success-50, 0.95) 0%, $lions-white 100%);
|
||||
|
||||
.ui-toast-icon,
|
||||
.ui-growl-icon {
|
||||
color: $lions-success-600;
|
||||
}
|
||||
|
||||
.ui-toast-summary,
|
||||
.ui-growl-title {
|
||||
color: $lions-success-800;
|
||||
}
|
||||
|
||||
.ui-toast-icon-close,
|
||||
.ui-growl-icon-close {
|
||||
color: $lions-success-700;
|
||||
|
||||
&:hover {
|
||||
background: rgba($lions-success-200, 0.6);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Warning notifications
|
||||
.ui-toast-message-warn,
|
||||
.ui-growl-item-warn {
|
||||
border-left-color: $lions-warning-500;
|
||||
background: linear-gradient(135deg, rgba($lions-warning-50, 0.95) 0%, $lions-white 100%);
|
||||
|
||||
.ui-toast-icon,
|
||||
.ui-growl-icon {
|
||||
color: $lions-warning-600;
|
||||
}
|
||||
|
||||
.ui-toast-summary,
|
||||
.ui-growl-title {
|
||||
color: $lions-warning-800;
|
||||
}
|
||||
|
||||
.ui-toast-icon-close,
|
||||
.ui-growl-icon-close {
|
||||
color: $lions-warning-700;
|
||||
|
||||
&:hover {
|
||||
background: rgba($lions-warning-200, 0.6);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Error notifications
|
||||
.ui-toast-message-error,
|
||||
.ui-growl-item-error {
|
||||
border-left-color: $lions-danger-500;
|
||||
background: linear-gradient(135deg, rgba($lions-danger-50, 0.95) 0%, $lions-white 100%);
|
||||
|
||||
.ui-toast-icon,
|
||||
.ui-growl-icon {
|
||||
color: $lions-danger-600;
|
||||
}
|
||||
|
||||
.ui-toast-summary,
|
||||
.ui-growl-title {
|
||||
color: $lions-danger-800;
|
||||
}
|
||||
|
||||
.ui-toast-icon-close,
|
||||
.ui-growl-icon-close {
|
||||
color: $lions-danger-700;
|
||||
|
||||
&:hover {
|
||||
background: rgba($lions-danger-200, 0.6);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// 📍 TOAST/GROWL POSITIONS
|
||||
// ============================================
|
||||
|
||||
.ui-toast,
|
||||
.ui-growl {
|
||||
&.ui-toast-top-right,
|
||||
&.ui-growl-top-right {
|
||||
top: $lions-spacing-5;
|
||||
right: $lions-spacing-5;
|
||||
}
|
||||
|
||||
&.ui-toast-top-left,
|
||||
&.ui-growl-top-left {
|
||||
top: $lions-spacing-5;
|
||||
left: $lions-spacing-5;
|
||||
}
|
||||
|
||||
&.ui-toast-bottom-right,
|
||||
&.ui-growl-bottom-right {
|
||||
bottom: $lions-spacing-5;
|
||||
right: $lions-spacing-5;
|
||||
}
|
||||
|
||||
&.ui-toast-bottom-left,
|
||||
&.ui-growl-bottom-left {
|
||||
bottom: $lions-spacing-5;
|
||||
left: $lions-spacing-5;
|
||||
}
|
||||
|
||||
&.ui-toast-top-center,
|
||||
&.ui-growl-top-center {
|
||||
top: $lions-spacing-5;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
}
|
||||
|
||||
&.ui-toast-bottom-center,
|
||||
&.ui-growl-bottom-center {
|
||||
bottom: $lions-spacing-5;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
}
|
||||
|
||||
&.ui-toast-center,
|
||||
&.ui-growl-center {
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// ✨ TOAST/GROWL ANIMATIONS
|
||||
// ============================================
|
||||
|
||||
@keyframes lions-toast-fade-in {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(-20px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes lions-toast-fade-out {
|
||||
from {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
to {
|
||||
opacity: 0;
|
||||
transform: translateY(-20px);
|
||||
}
|
||||
}
|
||||
|
||||
.ui-toast-message,
|
||||
.ui-growl-item {
|
||||
animation: lions-toast-fade-in 300ms ease-out;
|
||||
|
||||
&.ui-toast-message-exit,
|
||||
&.ui-state-leaving {
|
||||
animation: lions-toast-fade-out 200ms ease-in;
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// 📱 RESPONSIVE ADJUSTMENTS
|
||||
// ============================================
|
||||
|
||||
@media (max-width: $lions-breakpoint-md) {
|
||||
.ui-toast,
|
||||
.ui-growl {
|
||||
&.ui-toast-top-right,
|
||||
&.ui-toast-top-left,
|
||||
&.ui-toast-bottom-right,
|
||||
&.ui-toast-bottom-left,
|
||||
&.ui-growl-top-right,
|
||||
&.ui-growl-top-left,
|
||||
&.ui-growl-bottom-right,
|
||||
&.ui-growl-bottom-left {
|
||||
left: $lions-spacing-3 !important;
|
||||
right: $lions-spacing-3 !important;
|
||||
transform: none !important;
|
||||
}
|
||||
|
||||
.ui-toast-message,
|
||||
.ui-growl-item {
|
||||
min-width: auto;
|
||||
max-width: none;
|
||||
}
|
||||
}
|
||||
|
||||
.ui-messages,
|
||||
.ui-message {
|
||||
padding: $lions-spacing-3 $lions-spacing-4;
|
||||
@include lions-font-size('sm');
|
||||
|
||||
.ui-messages-icon,
|
||||
.ui-message-icon {
|
||||
font-size: 1.25rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// ♿ ACCESSIBILITY ENHANCEMENTS
|
||||
// ============================================
|
||||
|
||||
.ui-messages,
|
||||
.ui-message,
|
||||
.ui-toast-message,
|
||||
.ui-growl-item {
|
||||
// High contrast mode
|
||||
@media (prefers-contrast: high) {
|
||||
border-width: 3px !important;
|
||||
}
|
||||
|
||||
// Reduced motion
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
transition: none !important;
|
||||
animation: none !important;
|
||||
|
||||
&:hover {
|
||||
transform: none !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ARIA live regions
|
||||
[role="alert"],
|
||||
[aria-live] {
|
||||
// Ensure screen readers can access messages
|
||||
&:empty {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// 🎯 MESSAGE LIST STYLES
|
||||
// ============================================
|
||||
|
||||
.ui-messages {
|
||||
&.ui-messages-list {
|
||||
.ui-messages-item {
|
||||
margin-bottom: $lions-spacing-2;
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// 🔧 UTILITY CLASSES
|
||||
// ============================================
|
||||
|
||||
// Compact messages
|
||||
.ui-messages-compact,
|
||||
.ui-message-compact {
|
||||
padding: $lions-spacing-2 $lions-spacing-3;
|
||||
|
||||
.ui-messages-icon,
|
||||
.ui-message-icon {
|
||||
font-size: 1.25rem;
|
||||
}
|
||||
|
||||
.ui-messages-summary,
|
||||
.ui-message-summary {
|
||||
@include lions-font-size('sm');
|
||||
}
|
||||
|
||||
.ui-messages-detail,
|
||||
.ui-message-detail {
|
||||
@include lions-font-size('xs');
|
||||
}
|
||||
}
|
||||
|
||||
// Large messages
|
||||
.ui-messages-large,
|
||||
.ui-message-large {
|
||||
padding: $lions-spacing-5 $lions-spacing-6;
|
||||
|
||||
.ui-messages-icon,
|
||||
.ui-message-icon {
|
||||
font-size: 2rem;
|
||||
}
|
||||
|
||||
.ui-messages-summary,
|
||||
.ui-message-summary {
|
||||
@include lions-font-size('xl');
|
||||
}
|
||||
|
||||
.ui-messages-detail,
|
||||
.ui-message-detail {
|
||||
@include lions-font-size('lg');
|
||||
}
|
||||
}
|
||||
|
||||
// Inline messages (no margin)
|
||||
.ui-messages-inline,
|
||||
.ui-message-inline {
|
||||
margin: 0;
|
||||
display: inline-flex;
|
||||
}
|
||||
@@ -0,0 +1,633 @@
|
||||
/*
|
||||
* ═══════════════════════════════════════════════════════════
|
||||
* Lions.dev - Navigation Component Styles
|
||||
* ═══════════════════════════════════════════════════════════
|
||||
* Styles pour Menu, Menubar, TabView et composants de navigation
|
||||
*
|
||||
* Version: 1.0.0
|
||||
* Date: 2 Janvier 2026
|
||||
* Author: Lions Development Team
|
||||
* Priority: CRITIQUE #25-27
|
||||
* ═══════════════════════════════════════════════════════════
|
||||
*/
|
||||
|
||||
@import '../variables';
|
||||
@import '../mixins';
|
||||
|
||||
// ============================================
|
||||
// 📋 MENU VERTICAL STYLES
|
||||
// ============================================
|
||||
|
||||
.ui-menu {
|
||||
background: $lions-white;
|
||||
border: 1px solid $lions-gray-300;
|
||||
@include lions-rounded('md');
|
||||
@include lions-shadow('md');
|
||||
padding: $lions-spacing-2 0;
|
||||
|
||||
.ui-menuitem {
|
||||
.ui-menuitem-link {
|
||||
padding: $lions-spacing-3 $lions-spacing-5;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: $lions-spacing-3;
|
||||
color: $lions-text-primary;
|
||||
text-decoration: none;
|
||||
@include lions-transition();
|
||||
cursor: pointer;
|
||||
|
||||
.ui-menuitem-icon {
|
||||
font-size: 1.125rem;
|
||||
color: $lions-gray-600;
|
||||
@include lions-transition();
|
||||
}
|
||||
|
||||
.ui-menuitem-text {
|
||||
@include lions-font-size('base');
|
||||
@include lions-font-weight('medium');
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background: rgba($lions-blue-50, 0.8);
|
||||
color: $lions-blue-700;
|
||||
|
||||
.ui-menuitem-icon {
|
||||
color: $lions-blue-600;
|
||||
}
|
||||
}
|
||||
|
||||
&:focus-visible {
|
||||
@include lions-focus-ring();
|
||||
}
|
||||
|
||||
&.ui-state-active,
|
||||
&.ui-state-highlight {
|
||||
background: linear-gradient(135deg, rgba($lions-blue-100, 0.8) 0%, rgba($lions-blue-50, 0.6) 100%);
|
||||
color: $lions-blue-800;
|
||||
border-left: 4px solid $lions-blue-500;
|
||||
padding-left: calc(#{$lions-spacing-5} - 4px);
|
||||
|
||||
.ui-menuitem-icon {
|
||||
color: $lions-blue-600;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Submenu indicator
|
||||
&.ui-menu-parent {
|
||||
.ui-menuitem-link {
|
||||
position: relative;
|
||||
|
||||
&::after {
|
||||
content: '\e902'; // PrimeIcons chevron-right
|
||||
font-family: 'primeicons';
|
||||
position: absolute;
|
||||
right: $lions-spacing-4;
|
||||
color: $lions-gray-500;
|
||||
@include lions-transition();
|
||||
}
|
||||
|
||||
&:hover::after {
|
||||
color: $lions-blue-600;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Submenu
|
||||
.ui-menu-child {
|
||||
background: $lions-white;
|
||||
border: 1px solid $lions-gray-300;
|
||||
@include lions-rounded('md');
|
||||
@include lions-shadow('lg');
|
||||
padding: $lions-spacing-2 0;
|
||||
margin-left: $lions-spacing-2;
|
||||
|
||||
.ui-menuitem-link {
|
||||
padding-left: $lions-spacing-6;
|
||||
}
|
||||
}
|
||||
|
||||
// Separator
|
||||
.ui-menu-separator {
|
||||
border-top: 1px solid $lions-gray-300;
|
||||
margin: $lions-spacing-2 0;
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// 📊 MENUBAR HORIZONTAL STYLES
|
||||
// ============================================
|
||||
|
||||
.ui-menubar {
|
||||
background: linear-gradient(135deg, rgba($lions-blue-500, 0.08) 0%, rgba($lions-blue-700, 0.04) 100%);
|
||||
border: 1px solid $lions-gray-300;
|
||||
border-left: none;
|
||||
border-right: none;
|
||||
padding: $lions-spacing-2 $lions-spacing-5;
|
||||
@include lions-shadow('sm');
|
||||
|
||||
.ui-menubar-root-list {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: $lions-spacing-1;
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
|
||||
> .ui-menuitem {
|
||||
> .ui-menuitem-link {
|
||||
padding: $lions-spacing-3 $lions-spacing-4;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: $lions-spacing-2;
|
||||
color: $lions-text-primary;
|
||||
text-decoration: none;
|
||||
@include lions-rounded('md');
|
||||
@include lions-transition();
|
||||
cursor: pointer;
|
||||
@include lions-font-weight('medium');
|
||||
|
||||
.ui-menuitem-icon {
|
||||
font-size: 1.125rem;
|
||||
color: $lions-gray-600;
|
||||
@include lions-transition();
|
||||
}
|
||||
|
||||
.ui-menuitem-text {
|
||||
@include lions-font-size('base');
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background: rgba($lions-blue-100, 0.6);
|
||||
color: $lions-blue-700;
|
||||
|
||||
.ui-menuitem-icon {
|
||||
color: $lions-blue-600;
|
||||
}
|
||||
}
|
||||
|
||||
&:focus-visible {
|
||||
@include lions-focus-ring();
|
||||
}
|
||||
|
||||
&.ui-state-active,
|
||||
&.ui-state-highlight {
|
||||
background: linear-gradient(135deg, rgba($lions-blue-500, 0.15) 0%, rgba($lions-blue-600, 0.1) 100%);
|
||||
color: $lions-blue-800;
|
||||
|
||||
.ui-menuitem-icon {
|
||||
color: $lions-blue-600;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Submenu indicator
|
||||
&.ui-menu-parent {
|
||||
> .ui-menuitem-link {
|
||||
&::after {
|
||||
content: '\e933'; // PrimeIcons chevron-down
|
||||
font-family: 'primeicons';
|
||||
margin-left: $lions-spacing-1;
|
||||
color: $lions-gray-500;
|
||||
@include lions-transition();
|
||||
}
|
||||
|
||||
&:hover::after {
|
||||
color: $lions-blue-600;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Dropdown submenu
|
||||
.ui-menu-child {
|
||||
background: $lions-white;
|
||||
border: 1px solid $lions-gray-300;
|
||||
@include lions-rounded('md');
|
||||
@include lions-shadow('xl');
|
||||
padding: $lions-spacing-2 0;
|
||||
margin-top: $lions-spacing-1;
|
||||
min-width: 200px;
|
||||
|
||||
.ui-menuitem-link {
|
||||
padding: $lions-spacing-3 $lions-spacing-5;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: $lions-spacing-3;
|
||||
color: $lions-text-primary;
|
||||
text-decoration: none;
|
||||
@include lions-transition();
|
||||
|
||||
.ui-menuitem-icon {
|
||||
font-size: 1rem;
|
||||
color: $lions-gray-600;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background: rgba($lions-blue-50, 0.8);
|
||||
color: $lions-blue-700;
|
||||
|
||||
.ui-menuitem-icon {
|
||||
color: $lions-blue-600;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.ui-menu-separator {
|
||||
border-top: 1px solid $lions-gray-300;
|
||||
margin: $lions-spacing-2 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Right-aligned items
|
||||
.ui-menubar-options {
|
||||
margin-left: auto;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: $lions-spacing-2;
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// 📑 TABVIEW STYLES
|
||||
// ============================================
|
||||
|
||||
.ui-tabview {
|
||||
background: $lions-white;
|
||||
@include lions-rounded('md');
|
||||
@include lions-shadow('sm');
|
||||
overflow: hidden;
|
||||
|
||||
// Tab navigation
|
||||
.ui-tabview-nav {
|
||||
background: linear-gradient(135deg, rgba($lions-gray-100, 0.8) 0%, rgba($lions-gray-50, 0.6) 100%);
|
||||
border-bottom: 2px solid $lions-gray-300;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
list-style: none;
|
||||
display: flex;
|
||||
gap: $lions-spacing-1;
|
||||
padding: $lions-spacing-2 $lions-spacing-4;
|
||||
|
||||
.ui-tabview-nav-link {
|
||||
padding: $lions-spacing-3 $lions-spacing-5;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: $lions-spacing-2;
|
||||
color: $lions-text-secondary;
|
||||
text-decoration: none;
|
||||
@include lions-rounded('md');
|
||||
@include lions-rounded-bottom(0);
|
||||
@include lions-transition();
|
||||
cursor: pointer;
|
||||
border: 1px solid transparent;
|
||||
border-bottom: none;
|
||||
position: relative;
|
||||
@include lions-font-weight('medium');
|
||||
|
||||
.ui-tabview-left-icon {
|
||||
font-size: 1.125rem;
|
||||
color: $lions-gray-600;
|
||||
@include lions-transition();
|
||||
}
|
||||
|
||||
.ui-tabview-title {
|
||||
@include lions-font-size('base');
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background: rgba($lions-blue-50, 0.6);
|
||||
color: $lions-blue-700;
|
||||
|
||||
.ui-tabview-left-icon {
|
||||
color: $lions-blue-600;
|
||||
}
|
||||
}
|
||||
|
||||
&:focus-visible {
|
||||
@include lions-focus-ring();
|
||||
}
|
||||
}
|
||||
|
||||
.ui-state-active {
|
||||
.ui-tabview-nav-link {
|
||||
background: $lions-white;
|
||||
color: $lions-blue-700;
|
||||
border-color: $lions-gray-300;
|
||||
border-bottom-color: $lions-white;
|
||||
@include lions-font-weight('bold');
|
||||
|
||||
&::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
bottom: -2px;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 3px;
|
||||
background: $lions-blue-500;
|
||||
@include lions-rounded('full');
|
||||
}
|
||||
|
||||
.ui-tabview-left-icon {
|
||||
color: $lions-blue-600;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Close icon
|
||||
.ui-tabview-close {
|
||||
@include lions-button-base;
|
||||
width: 1.5rem;
|
||||
height: 1.5rem;
|
||||
min-width: 1.5rem;
|
||||
padding: 0;
|
||||
@include lions-flex-center;
|
||||
background: transparent;
|
||||
border: none;
|
||||
color: $lions-gray-500;
|
||||
@include lions-transition();
|
||||
margin-left: $lions-spacing-2;
|
||||
|
||||
&:hover {
|
||||
background: rgba($lions-danger-500, 0.1);
|
||||
color: $lions-danger-600;
|
||||
transform: scale(1.1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Tab panels
|
||||
.ui-tabview-panels {
|
||||
padding: $lions-spacing-6;
|
||||
background: $lions-white;
|
||||
|
||||
.ui-tabview-panel {
|
||||
&:not(.ui-state-active) {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Position variants
|
||||
&.ui-tabview-top {
|
||||
.ui-tabview-nav {
|
||||
border-bottom: 2px solid $lions-gray-300;
|
||||
|
||||
.ui-state-active .ui-tabview-nav-link::after {
|
||||
bottom: -2px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.ui-tabview-bottom {
|
||||
display: flex;
|
||||
flex-direction: column-reverse;
|
||||
|
||||
.ui-tabview-nav {
|
||||
border-top: 2px solid $lions-gray-300;
|
||||
border-bottom: none;
|
||||
|
||||
.ui-tabview-nav-link {
|
||||
@include lions-rounded('md');
|
||||
@include lions-rounded-top(0);
|
||||
}
|
||||
|
||||
.ui-state-active .ui-tabview-nav-link {
|
||||
border-bottom-color: $lions-gray-300;
|
||||
border-top-color: $lions-white;
|
||||
|
||||
&::after {
|
||||
bottom: auto;
|
||||
top: -2px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.ui-tabview-left {
|
||||
display: flex;
|
||||
|
||||
.ui-tabview-nav {
|
||||
border-right: 2px solid $lions-gray-300;
|
||||
border-bottom: none;
|
||||
flex-direction: column;
|
||||
padding: $lions-spacing-4 $lions-spacing-2;
|
||||
|
||||
.ui-tabview-nav-link {
|
||||
@include lions-rounded('md');
|
||||
@include lions-rounded-right(0);
|
||||
}
|
||||
|
||||
.ui-state-active .ui-tabview-nav-link {
|
||||
border-right-color: $lions-white;
|
||||
|
||||
&::after {
|
||||
right: -2px;
|
||||
left: auto;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
width: 3px;
|
||||
height: auto;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.ui-tabview-right {
|
||||
display: flex;
|
||||
flex-direction: row-reverse;
|
||||
|
||||
.ui-tabview-nav {
|
||||
border-left: 2px solid $lions-gray-300;
|
||||
border-bottom: none;
|
||||
flex-direction: column;
|
||||
padding: $lions-spacing-4 $lions-spacing-2;
|
||||
|
||||
.ui-tabview-nav-link {
|
||||
@include lions-rounded('md');
|
||||
@include lions-rounded-left(0);
|
||||
}
|
||||
|
||||
.ui-state-active .ui-tabview-nav-link {
|
||||
border-left-color: $lions-white;
|
||||
|
||||
&::after {
|
||||
left: -2px;
|
||||
right: auto;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
width: 3px;
|
||||
height: auto;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// 🎨 MENU SEVERITY VARIANTS
|
||||
// ============================================
|
||||
|
||||
.ui-menu,
|
||||
.ui-menubar {
|
||||
// Primary menu items
|
||||
.ui-menuitem-primary {
|
||||
.ui-menuitem-link {
|
||||
background: rgba($lions-blue-100, 0.3);
|
||||
color: $lions-blue-700;
|
||||
|
||||
.ui-menuitem-icon {
|
||||
color: $lions-blue-600;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background: rgba($lions-blue-100, 0.6);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Success menu items
|
||||
.ui-menuitem-success {
|
||||
.ui-menuitem-link {
|
||||
background: rgba($lions-success-100, 0.3);
|
||||
color: $lions-success-700;
|
||||
|
||||
.ui-menuitem-icon {
|
||||
color: $lions-success-600;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background: rgba($lions-success-100, 0.6);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Danger menu items
|
||||
.ui-menuitem-danger {
|
||||
.ui-menuitem-link {
|
||||
background: rgba($lions-danger-100, 0.3);
|
||||
color: $lions-danger-700;
|
||||
|
||||
.ui-menuitem-icon {
|
||||
color: $lions-danger-600;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background: rgba($lions-danger-100, 0.6);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// 📱 RESPONSIVE ADJUSTMENTS
|
||||
// ============================================
|
||||
|
||||
@media (max-width: $lions-breakpoint-md) {
|
||||
.ui-menubar {
|
||||
.ui-menubar-root-list {
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
gap: $lions-spacing-1;
|
||||
|
||||
> .ui-menuitem {
|
||||
> .ui-menuitem-link {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.ui-menubar-options {
|
||||
margin-left: 0;
|
||||
margin-top: $lions-spacing-2;
|
||||
padding-top: $lions-spacing-2;
|
||||
border-top: 1px solid $lions-gray-300;
|
||||
}
|
||||
}
|
||||
|
||||
.ui-tabview {
|
||||
.ui-tabview-nav {
|
||||
overflow-x: auto;
|
||||
flex-wrap: nowrap;
|
||||
|
||||
&::-webkit-scrollbar {
|
||||
height: 4px;
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar-track {
|
||||
background: $lions-gray-100;
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar-thumb {
|
||||
background: $lions-gray-400;
|
||||
@include lions-rounded('full');
|
||||
}
|
||||
}
|
||||
|
||||
.ui-tabview-panels {
|
||||
padding: $lions-spacing-4;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// ♿ ACCESSIBILITY ENHANCEMENTS
|
||||
// ============================================
|
||||
|
||||
.ui-menu,
|
||||
.ui-menubar,
|
||||
.ui-tabview {
|
||||
// Focus visible
|
||||
.ui-menuitem-link:focus-visible,
|
||||
.ui-tabview-nav-link:focus-visible {
|
||||
@include lions-focus-ring();
|
||||
}
|
||||
|
||||
// High contrast mode
|
||||
@media (prefers-contrast: high) {
|
||||
border-width: 2px !important;
|
||||
|
||||
.ui-menuitem-link,
|
||||
.ui-tabview-nav-link {
|
||||
border-width: 2px !important;
|
||||
}
|
||||
}
|
||||
|
||||
// Reduced motion
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
* {
|
||||
transition: none !important;
|
||||
animation: none !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// 🎯 UTILITY CLASSES
|
||||
// ============================================
|
||||
|
||||
// Compact menus
|
||||
.ui-menu-compact {
|
||||
.ui-menuitem-link {
|
||||
padding: $lions-spacing-2 $lions-spacing-4;
|
||||
@include lions-font-size('sm');
|
||||
}
|
||||
}
|
||||
|
||||
// Large menus
|
||||
.ui-menu-large {
|
||||
.ui-menuitem-link {
|
||||
padding: $lions-spacing-4 $lions-spacing-6;
|
||||
@include lions-font-size('lg');
|
||||
}
|
||||
}
|
||||
|
||||
// Vertical menubar (stacked)
|
||||
.ui-menubar-vertical {
|
||||
.ui-menubar-root-list {
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,704 @@
|
||||
/*
|
||||
* ═══════════════════════════════════════════════════════════
|
||||
* Lions.dev - Overlay Components Styles
|
||||
* ═══════════════════════════════════════════════════════════
|
||||
* Styles pour Dialog, ConfirmDialog, Sidebar, OverlayPanel
|
||||
*
|
||||
* Version: 1.0.0
|
||||
* Date: 1er Janvier 2026
|
||||
* Author: Lions Development Team
|
||||
* Priority: CRITIQUE #21-24
|
||||
* ═══════════════════════════════════════════════════════════
|
||||
*/
|
||||
|
||||
@import '../variables';
|
||||
@import '../mixins';
|
||||
|
||||
// ============================================
|
||||
// 🌑 OVERLAY MASK (BACKDROP)
|
||||
// ============================================
|
||||
|
||||
.ui-dialog-mask,
|
||||
.ui-confirmdialog-mask,
|
||||
.ui-sidebar-mask,
|
||||
.ui-overlaypanel-mask {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: rgba(0, 0, 0, 0.5);
|
||||
z-index: 1000;
|
||||
backdrop-filter: blur(3px);
|
||||
@include lions-transition(opacity, 200ms);
|
||||
|
||||
&.ui-dialog-mask-scrollblocker {
|
||||
overflow: hidden;
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// 💬 DIALOG STYLES
|
||||
// ============================================
|
||||
|
||||
.ui-dialog {
|
||||
position: fixed;
|
||||
background: $lions-white;
|
||||
@include lions-rounded('lg');
|
||||
@include lions-shadow('2xl');
|
||||
z-index: 1001;
|
||||
overflow: hidden;
|
||||
max-height: 90vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
@include lions-transition(transform, 300ms);
|
||||
|
||||
// Dialog header
|
||||
.ui-dialog-titlebar {
|
||||
background: linear-gradient(135deg, rgba($lions-blue-500, 0.1) 0%, rgba($lions-blue-700, 0.05) 100%);
|
||||
border-bottom: 2px solid $lions-blue-500;
|
||||
padding: $lions-spacing-5 $lions-spacing-6;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
|
||||
.ui-dialog-title {
|
||||
@include lions-font-size('xl');
|
||||
@include lions-font-weight('bold');
|
||||
color: $lions-text-primary;
|
||||
margin: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: $lions-spacing-3;
|
||||
|
||||
i {
|
||||
color: $lions-blue-500;
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
.ui-dialog-titlebar-icon {
|
||||
@include lions-button-base;
|
||||
width: 2.5rem;
|
||||
height: 2.5rem;
|
||||
padding: 0;
|
||||
@include lions-flex-center;
|
||||
background: transparent;
|
||||
border: none;
|
||||
color: $lions-gray-600;
|
||||
@include lions-transition();
|
||||
|
||||
&:hover {
|
||||
background: rgba($lions-danger-500, 0.1);
|
||||
color: $lions-danger-600;
|
||||
transform: scale(1.1) rotate(90deg);
|
||||
}
|
||||
|
||||
&:focus-visible {
|
||||
@include lions-focus-ring();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Dialog content
|
||||
.ui-dialog-content {
|
||||
padding: $lions-spacing-6;
|
||||
overflow-y: auto;
|
||||
flex: 1;
|
||||
color: $lions-text-primary;
|
||||
line-height: $lions-line-height-normal;
|
||||
|
||||
// Custom scrollbar
|
||||
&::-webkit-scrollbar {
|
||||
width: 8px;
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar-track {
|
||||
background: $lions-gray-100;
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar-thumb {
|
||||
background: $lions-gray-400;
|
||||
@include lions-rounded('full');
|
||||
|
||||
&:hover {
|
||||
background: $lions-blue-500;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Dialog footer
|
||||
.ui-dialog-footer {
|
||||
background: linear-gradient(135deg, rgba($lions-gray-50, 0.8) 0%, rgba($lions-gray-100, 0.5) 100%);
|
||||
border-top: 1px solid $lions-gray-300;
|
||||
padding: $lions-spacing-4 $lions-spacing-6;
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
gap: $lions-spacing-3;
|
||||
}
|
||||
|
||||
// Resizable handle
|
||||
.ui-resizable-handle {
|
||||
position: absolute;
|
||||
background: transparent;
|
||||
|
||||
&.ui-resizable-se {
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
cursor: se-resize;
|
||||
|
||||
&::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
bottom: 4px;
|
||||
right: 4px;
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
border-right: 2px solid $lions-gray-400;
|
||||
border-bottom: 2px solid $lions-gray-400;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// ✅ CONFIRM DIALOG STYLES
|
||||
// ============================================
|
||||
|
||||
.ui-confirmdialog {
|
||||
@extend .ui-dialog;
|
||||
max-width: 500px;
|
||||
|
||||
.ui-confirmdialog-icon {
|
||||
font-size: 3rem;
|
||||
margin-right: $lions-spacing-4;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.ui-confirmdialog-message {
|
||||
flex: 1;
|
||||
@include lions-font-size('lg');
|
||||
color: $lions-text-primary;
|
||||
line-height: $lions-line-height-relaxed;
|
||||
}
|
||||
|
||||
// Severity variants
|
||||
&.ui-confirmdialog-info {
|
||||
.ui-dialog-titlebar {
|
||||
background: linear-gradient(135deg, rgba($lions-info-500, 0.1) 0%, rgba($lions-info-700, 0.05) 100%);
|
||||
border-bottom-color: $lions-info-500;
|
||||
}
|
||||
|
||||
.ui-confirmdialog-icon {
|
||||
color: $lions-info-600;
|
||||
}
|
||||
}
|
||||
|
||||
&.ui-confirmdialog-warn {
|
||||
.ui-dialog-titlebar {
|
||||
background: linear-gradient(135deg, rgba($lions-warning-500, 0.1) 0%, rgba($lions-warning-700, 0.05) 100%);
|
||||
border-bottom-color: $lions-warning-500;
|
||||
}
|
||||
|
||||
.ui-confirmdialog-icon {
|
||||
color: $lions-warning-600;
|
||||
}
|
||||
}
|
||||
|
||||
&.ui-confirmdialog-error {
|
||||
.ui-dialog-titlebar {
|
||||
background: linear-gradient(135deg, rgba($lions-danger-500, 0.1) 0%, rgba($lions-danger-700, 0.05) 100%);
|
||||
border-bottom-color: $lions-danger-500;
|
||||
}
|
||||
|
||||
.ui-confirmdialog-icon {
|
||||
color: $lions-danger-600;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// 📱 SIDEBAR STYLES
|
||||
// ============================================
|
||||
|
||||
.ui-sidebar {
|
||||
position: fixed;
|
||||
background: $lions-white;
|
||||
@include lions-shadow('2xl');
|
||||
z-index: 1001;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
@include lions-transition(transform, 300ms);
|
||||
|
||||
// Sidebar header
|
||||
.ui-sidebar-header {
|
||||
background: linear-gradient(135deg, rgba($lions-blue-500, 0.1) 0%, rgba($lions-blue-700, 0.05) 100%);
|
||||
border-bottom: 2px solid $lions-blue-500;
|
||||
padding: $lions-spacing-5 $lions-spacing-6;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
|
||||
h3, h4, h5 {
|
||||
margin: 0;
|
||||
@include lions-font-weight('bold');
|
||||
color: $lions-text-primary;
|
||||
}
|
||||
|
||||
.ui-sidebar-close {
|
||||
@include lions-button-base;
|
||||
width: 2.5rem;
|
||||
height: 2.5rem;
|
||||
padding: 0;
|
||||
@include lions-flex-center;
|
||||
background: transparent;
|
||||
border: none;
|
||||
color: $lions-gray-600;
|
||||
@include lions-transition();
|
||||
|
||||
&:hover {
|
||||
background: rgba($lions-danger-500, 0.1);
|
||||
color: $lions-danger-600;
|
||||
transform: scale(1.1) rotate(90deg);
|
||||
}
|
||||
|
||||
&:focus-visible {
|
||||
@include lions-focus-ring();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Sidebar content
|
||||
.ui-sidebar-content {
|
||||
padding: $lions-spacing-6;
|
||||
overflow-y: auto;
|
||||
flex: 1;
|
||||
|
||||
// Custom scrollbar
|
||||
&::-webkit-scrollbar {
|
||||
width: 8px;
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar-track {
|
||||
background: $lions-gray-100;
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar-thumb {
|
||||
background: $lions-gray-400;
|
||||
@include lions-rounded('full');
|
||||
|
||||
&:hover {
|
||||
background: $lions-blue-500;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Sidebar footer
|
||||
.ui-sidebar-footer {
|
||||
background: linear-gradient(135deg, rgba($lions-gray-50, 0.8) 0%, rgba($lions-gray-100, 0.5) 100%);
|
||||
border-top: 1px solid $lions-gray-300;
|
||||
padding: $lions-spacing-4 $lions-spacing-6;
|
||||
}
|
||||
|
||||
// Position variants
|
||||
&.ui-sidebar-left {
|
||||
top: 0;
|
||||
left: 0;
|
||||
height: 100%;
|
||||
width: 400px;
|
||||
border-right: 1px solid $lions-gray-300;
|
||||
|
||||
&.ui-sidebar-active {
|
||||
transform: translateX(0);
|
||||
}
|
||||
|
||||
&:not(.ui-sidebar-active) {
|
||||
transform: translateX(-100%);
|
||||
}
|
||||
}
|
||||
|
||||
&.ui-sidebar-right {
|
||||
top: 0;
|
||||
right: 0;
|
||||
height: 100%;
|
||||
width: 400px;
|
||||
border-left: 1px solid $lions-gray-300;
|
||||
|
||||
&.ui-sidebar-active {
|
||||
transform: translateX(0);
|
||||
}
|
||||
|
||||
&:not(.ui-sidebar-active) {
|
||||
transform: translateX(100%);
|
||||
}
|
||||
}
|
||||
|
||||
&.ui-sidebar-top {
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 300px;
|
||||
border-bottom: 1px solid $lions-gray-300;
|
||||
|
||||
&.ui-sidebar-active {
|
||||
transform: translateY(0);
|
||||
}
|
||||
|
||||
&:not(.ui-sidebar-active) {
|
||||
transform: translateY(-100%);
|
||||
}
|
||||
}
|
||||
|
||||
&.ui-sidebar-bottom {
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 300px;
|
||||
border-top: 1px solid $lions-gray-300;
|
||||
|
||||
&.ui-sidebar-active {
|
||||
transform: translateY(0);
|
||||
}
|
||||
|
||||
&:not(.ui-sidebar-active) {
|
||||
transform: translateY(100%);
|
||||
}
|
||||
}
|
||||
|
||||
&.ui-sidebar-full {
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// 📌 OVERLAY PANEL STYLES
|
||||
// ============================================
|
||||
|
||||
.ui-overlaypanel {
|
||||
position: absolute;
|
||||
background: $lions-white;
|
||||
@include lions-rounded('lg');
|
||||
@include lions-shadow('xl');
|
||||
z-index: 1002;
|
||||
padding: 0;
|
||||
@include lions-transition(opacity, 200ms);
|
||||
|
||||
// Arrow pointer
|
||||
&::before,
|
||||
&::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
width: 0;
|
||||
height: 0;
|
||||
border: solid transparent;
|
||||
}
|
||||
|
||||
&::before {
|
||||
border-width: 11px;
|
||||
}
|
||||
|
||||
&::after {
|
||||
border-width: 10px;
|
||||
}
|
||||
|
||||
// Arrow positions
|
||||
&.ui-overlaypanel-flipped {
|
||||
&::before,
|
||||
&::after {
|
||||
bottom: 100%;
|
||||
left: 50%;
|
||||
}
|
||||
|
||||
&::before {
|
||||
margin-left: -11px;
|
||||
border-bottom-color: $lions-gray-300;
|
||||
}
|
||||
|
||||
&::after {
|
||||
margin-left: -10px;
|
||||
border-bottom-color: $lions-white;
|
||||
}
|
||||
}
|
||||
|
||||
&:not(.ui-overlaypanel-flipped) {
|
||||
&::before,
|
||||
&::after {
|
||||
top: 100%;
|
||||
left: 50%;
|
||||
}
|
||||
|
||||
&::before {
|
||||
margin-left: -11px;
|
||||
border-top-color: $lions-gray-300;
|
||||
}
|
||||
|
||||
&::after {
|
||||
margin-left: -10px;
|
||||
border-top-color: $lions-white;
|
||||
}
|
||||
}
|
||||
|
||||
// Header
|
||||
.ui-overlaypanel-header {
|
||||
background: linear-gradient(135deg, rgba($lions-blue-500, 0.1) 0%, rgba($lions-blue-700, 0.05) 100%);
|
||||
border-bottom: 2px solid $lions-blue-500;
|
||||
padding: $lions-spacing-4 $lions-spacing-5;
|
||||
@include lions-rounded('lg');
|
||||
border-bottom-left-radius: 0;
|
||||
border-bottom-right-radius: 0;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
|
||||
h5, h6 {
|
||||
margin: 0;
|
||||
@include lions-font-weight('bold');
|
||||
color: $lions-text-primary;
|
||||
}
|
||||
|
||||
.ui-overlaypanel-close {
|
||||
@include lions-button-base;
|
||||
width: 2rem;
|
||||
height: 2rem;
|
||||
padding: 0;
|
||||
@include lions-flex-center;
|
||||
background: transparent;
|
||||
border: none;
|
||||
color: $lions-gray-600;
|
||||
@include lions-transition();
|
||||
|
||||
&:hover {
|
||||
background: rgba($lions-danger-500, 0.1);
|
||||
color: $lions-danger-600;
|
||||
transform: scale(1.1);
|
||||
}
|
||||
|
||||
&:focus-visible {
|
||||
@include lions-focus-ring();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Content
|
||||
.ui-overlaypanel-content {
|
||||
padding: $lions-spacing-5;
|
||||
max-height: 400px;
|
||||
overflow-y: auto;
|
||||
color: $lions-text-primary;
|
||||
|
||||
// Custom scrollbar
|
||||
&::-webkit-scrollbar {
|
||||
width: 6px;
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar-track {
|
||||
background: $lions-gray-100;
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar-thumb {
|
||||
background: $lions-gray-400;
|
||||
@include lions-rounded('full');
|
||||
|
||||
&:hover {
|
||||
background: $lions-blue-500;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Footer
|
||||
.ui-overlaypanel-footer {
|
||||
background: linear-gradient(135deg, rgba($lions-gray-50, 0.8) 0%, rgba($lions-gray-100, 0.5) 100%);
|
||||
border-top: 1px solid $lions-gray-300;
|
||||
padding: $lions-spacing-3 $lions-spacing-5;
|
||||
@include lions-rounded('lg');
|
||||
border-top-left-radius: 0;
|
||||
border-top-right-radius: 0;
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// 📱 RESPONSIVE ADJUSTMENTS
|
||||
// ============================================
|
||||
|
||||
@media (max-width: $lions-breakpoint-md) {
|
||||
.ui-dialog {
|
||||
max-width: 95vw !important;
|
||||
max-height: 95vh !important;
|
||||
margin: $lions-spacing-3 !important;
|
||||
|
||||
.ui-dialog-titlebar {
|
||||
padding: $lions-spacing-4 $lions-spacing-5;
|
||||
|
||||
.ui-dialog-title {
|
||||
@include lions-font-size('lg');
|
||||
}
|
||||
}
|
||||
|
||||
.ui-dialog-content {
|
||||
padding: $lions-spacing-5;
|
||||
}
|
||||
|
||||
.ui-dialog-footer {
|
||||
padding: $lions-spacing-3 $lions-spacing-5;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
}
|
||||
|
||||
.ui-sidebar {
|
||||
&.ui-sidebar-left,
|
||||
&.ui-sidebar-right {
|
||||
width: 85vw !important;
|
||||
}
|
||||
|
||||
&.ui-sidebar-top,
|
||||
&.ui-sidebar-bottom {
|
||||
height: 50vh !important;
|
||||
}
|
||||
|
||||
.ui-sidebar-header,
|
||||
.ui-sidebar-content,
|
||||
.ui-sidebar-footer {
|
||||
padding: $lions-spacing-4 $lions-spacing-5;
|
||||
}
|
||||
}
|
||||
|
||||
.ui-overlaypanel {
|
||||
max-width: 90vw !important;
|
||||
|
||||
.ui-overlaypanel-content {
|
||||
max-height: 300px;
|
||||
padding: $lions-spacing-4;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// ✨ ANIMATIONS
|
||||
// ============================================
|
||||
|
||||
@keyframes lions-dialog-fade-in {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: scale(0.9) translateY(-20px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: scale(1) translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes lions-dialog-fade-out {
|
||||
from {
|
||||
opacity: 1;
|
||||
transform: scale(1) translateY(0);
|
||||
}
|
||||
to {
|
||||
opacity: 0;
|
||||
transform: scale(0.9) translateY(-20px);
|
||||
}
|
||||
}
|
||||
|
||||
.ui-dialog,
|
||||
.ui-confirmdialog {
|
||||
animation: lions-dialog-fade-in 300ms ease-out;
|
||||
|
||||
&.ui-dialog-exit {
|
||||
animation: lions-dialog-fade-out 200ms ease-in;
|
||||
}
|
||||
}
|
||||
|
||||
.ui-overlaypanel {
|
||||
animation: lions-dialog-fade-in 200ms ease-out;
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// ♿ ACCESSIBILITY ENHANCEMENTS
|
||||
// ============================================
|
||||
|
||||
.ui-dialog,
|
||||
.ui-confirmdialog,
|
||||
.ui-sidebar,
|
||||
.ui-overlaypanel {
|
||||
// Focus trap
|
||||
&[aria-modal="true"] {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
// High contrast mode
|
||||
@media (prefers-contrast: high) {
|
||||
border: 3px solid currentColor !important;
|
||||
}
|
||||
|
||||
// Reduced motion
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
animation: none !important;
|
||||
transition: none !important;
|
||||
|
||||
* {
|
||||
animation: none !important;
|
||||
transition: none !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// 🎯 DIALOG POSITIONS
|
||||
// ============================================
|
||||
|
||||
.ui-dialog {
|
||||
&.ui-dialog-center {
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
}
|
||||
|
||||
&.ui-dialog-top {
|
||||
top: $lions-spacing-6;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
}
|
||||
|
||||
&.ui-dialog-bottom {
|
||||
bottom: $lions-spacing-6;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
}
|
||||
|
||||
&.ui-dialog-left {
|
||||
top: 50%;
|
||||
left: $lions-spacing-6;
|
||||
transform: translateY(-50%);
|
||||
}
|
||||
|
||||
&.ui-dialog-right {
|
||||
top: 50%;
|
||||
right: $lions-spacing-6;
|
||||
transform: translateY(-50%);
|
||||
}
|
||||
|
||||
&.ui-dialog-top-left {
|
||||
top: $lions-spacing-6;
|
||||
left: $lions-spacing-6;
|
||||
}
|
||||
|
||||
&.ui-dialog-top-right {
|
||||
top: $lions-spacing-6;
|
||||
right: $lions-spacing-6;
|
||||
}
|
||||
|
||||
&.ui-dialog-bottom-left {
|
||||
bottom: $lions-spacing-6;
|
||||
left: $lions-spacing-6;
|
||||
}
|
||||
|
||||
&.ui-dialog-bottom-right {
|
||||
bottom: $lions-spacing-6;
|
||||
right: $lions-spacing-6;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,229 @@
|
||||
/*
|
||||
* ═══════════════════════════════════════════════════════════
|
||||
* Lions.dev - Progress Component Styles
|
||||
* ═══════════════════════════════════════════════════════════
|
||||
* Styles pour ProgressBar (barre de progression)
|
||||
*
|
||||
* Version: 1.0.0
|
||||
* Date: 2 Janvier 2026
|
||||
* Author: Lions Development Team
|
||||
* Priority: MOYENNE #39
|
||||
* ═══════════════════════════════════════════════════════════
|
||||
*/
|
||||
|
||||
@import '../variables';
|
||||
@import '../mixins';
|
||||
|
||||
// ============================================
|
||||
// 📊 PROGRESSBAR STYLES
|
||||
// ============================================
|
||||
|
||||
.ui-progressbar {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
height: 1rem;
|
||||
@include lions-rounded('md');
|
||||
background: $lions-gray-200;
|
||||
@include lions-shadow('sm');
|
||||
|
||||
.ui-progressbar-value {
|
||||
height: 100%;
|
||||
@include lions-gradient-blue;
|
||||
@include lions-rounded('md');
|
||||
@include lions-transition();
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
|
||||
// Animated stripes
|
||||
&::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
background-image: linear-gradient(
|
||||
45deg,
|
||||
rgba($lions-white, 0.15) 25%,
|
||||
transparent 25%,
|
||||
transparent 50%,
|
||||
rgba($lions-white, 0.15) 50%,
|
||||
rgba($lions-white, 0.15) 75%,
|
||||
transparent 75%,
|
||||
transparent
|
||||
);
|
||||
background-size: 1rem 1rem;
|
||||
}
|
||||
}
|
||||
|
||||
.ui-progressbar-label {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
@include lions-font-size('xs');
|
||||
@include lions-font-weight('semibold');
|
||||
color: $lions-white;
|
||||
text-shadow: 0 1px 2px rgba($lions-black, 0.3);
|
||||
z-index: 2;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
// Severity variants
|
||||
&.ui-progressbar-success {
|
||||
.ui-progressbar-value {
|
||||
@include lions-gradient-success;
|
||||
}
|
||||
}
|
||||
|
||||
&.ui-progressbar-info {
|
||||
.ui-progressbar-value {
|
||||
@include lions-gradient-info;
|
||||
}
|
||||
}
|
||||
|
||||
&.ui-progressbar-warning {
|
||||
.ui-progressbar-value {
|
||||
@include lions-gradient-warning;
|
||||
}
|
||||
}
|
||||
|
||||
&.ui-progressbar-danger {
|
||||
.ui-progressbar-value {
|
||||
@include lions-gradient-danger;
|
||||
}
|
||||
}
|
||||
|
||||
// Size variants
|
||||
&.ui-progressbar-sm {
|
||||
height: 0.5rem;
|
||||
|
||||
.ui-progressbar-label {
|
||||
@include lions-font-size('2xs');
|
||||
}
|
||||
|
||||
.ui-progressbar-value::after {
|
||||
background-size: 0.75rem 0.75rem;
|
||||
}
|
||||
}
|
||||
|
||||
&.ui-progressbar-lg {
|
||||
height: 1.5rem;
|
||||
|
||||
.ui-progressbar-label {
|
||||
@include lions-font-size('sm');
|
||||
}
|
||||
|
||||
.ui-progressbar-value::after {
|
||||
background-size: 1.5rem 1.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
// Animated stripes
|
||||
&.ui-progressbar-striped {
|
||||
.ui-progressbar-value::after {
|
||||
animation: lions-progress-stripes 1s linear infinite;
|
||||
}
|
||||
}
|
||||
|
||||
// Indeterminate mode
|
||||
&.ui-progressbar-indeterminate {
|
||||
.ui-progressbar-value {
|
||||
width: 100% !important;
|
||||
background: $lions-gray-200;
|
||||
|
||||
&::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
width: 30%;
|
||||
@include lions-gradient-blue;
|
||||
animation: lions-progress-indeterminate 1.5s cubic-bezier(0.65, 0.815, 0.735, 0.395) infinite;
|
||||
}
|
||||
|
||||
&::after {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
&.ui-progressbar-success .ui-progressbar-value::before {
|
||||
@include lions-gradient-success;
|
||||
}
|
||||
|
||||
&.ui-progressbar-info .ui-progressbar-value::before {
|
||||
@include lions-gradient-info;
|
||||
}
|
||||
|
||||
&.ui-progressbar-warning .ui-progressbar-value::before {
|
||||
@include lions-gradient-warning;
|
||||
}
|
||||
|
||||
&.ui-progressbar-danger .ui-progressbar-value::before {
|
||||
@include lions-gradient-danger;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// 🎬 ANIMATIONS
|
||||
// ============================================
|
||||
|
||||
@keyframes lions-progress-stripes {
|
||||
0% {
|
||||
background-position: 1rem 0;
|
||||
}
|
||||
100% {
|
||||
background-position: 0 0;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes lions-progress-indeterminate {
|
||||
0% {
|
||||
left: -30%;
|
||||
}
|
||||
100% {
|
||||
left: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// 📱 RESPONSIVE ADJUSTMENTS
|
||||
// ============================================
|
||||
|
||||
@media (max-width: $lions-breakpoint-md) {
|
||||
.ui-progressbar {
|
||||
&.ui-progressbar-lg {
|
||||
height: 1.25rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// ♿ ACCESSIBILITY ENHANCEMENTS
|
||||
// ============================================
|
||||
|
||||
.ui-progressbar {
|
||||
// High contrast mode
|
||||
@media (prefers-contrast: high) {
|
||||
border: 2px solid currentColor;
|
||||
|
||||
.ui-progressbar-value {
|
||||
border: 1px solid currentColor;
|
||||
}
|
||||
}
|
||||
|
||||
// Reduced motion
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
.ui-progressbar-value::after {
|
||||
animation: none !important;
|
||||
}
|
||||
|
||||
&.ui-progressbar-indeterminate .ui-progressbar-value::before {
|
||||
animation: none !important;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,677 @@
|
||||
/*
|
||||
* ═══════════════════════════════════════════════════════════
|
||||
* Lions.dev - Tree Component Styles
|
||||
* ═══════════════════════════════════════════════════════════
|
||||
* Styles pour Tree et TreeTable (affichage hiérarchique)
|
||||
*
|
||||
* Version: 1.0.0
|
||||
* Date: 2 Janvier 2026
|
||||
* Author: Lions Development Team
|
||||
* Priority: MOYENNE #31-32
|
||||
* ═══════════════════════════════════════════════════════════
|
||||
*/
|
||||
|
||||
@import '../variables';
|
||||
@import '../mixins';
|
||||
|
||||
// ============================================
|
||||
// 🌳 TREE STYLES
|
||||
// ============================================
|
||||
|
||||
.ui-tree {
|
||||
@include lions-rounded('lg');
|
||||
border: 1px solid $lions-gray-200;
|
||||
background: $lions-white;
|
||||
padding: $lions-spacing-3;
|
||||
@include lions-shadow('sm');
|
||||
|
||||
.ui-tree-container {
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.ui-treenode {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
list-style: none;
|
||||
|
||||
.ui-treenode-content {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: $lions-spacing-2;
|
||||
padding: $lions-spacing-2 $lions-spacing-3;
|
||||
@include lions-rounded('md');
|
||||
@include lions-transition();
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
|
||||
&:hover {
|
||||
background: $lions-gray-50;
|
||||
}
|
||||
|
||||
&.ui-treenode-selectable {
|
||||
&.ui-state-highlight {
|
||||
background: $lions-blue-50;
|
||||
color: $lions-blue-700;
|
||||
@include lions-font-weight('semibold');
|
||||
|
||||
.ui-treenode-icon {
|
||||
color: $lions-blue-600;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&:focus-visible {
|
||||
@include lions-focus-ring();
|
||||
}
|
||||
}
|
||||
|
||||
.ui-tree-toggler {
|
||||
@include lions-button-base;
|
||||
width: 1.5rem;
|
||||
height: 1.5rem;
|
||||
min-width: 1.5rem;
|
||||
padding: 0;
|
||||
@include lions-flex-center;
|
||||
background: transparent;
|
||||
border: 1px solid transparent;
|
||||
@include lions-rounded('sm');
|
||||
color: $lions-gray-600;
|
||||
cursor: pointer;
|
||||
@include lions-transition();
|
||||
|
||||
&:hover {
|
||||
background: $lions-gray-100;
|
||||
border-color: $lions-gray-300;
|
||||
color: $lions-blue-600;
|
||||
}
|
||||
|
||||
.ui-tree-toggler-icon {
|
||||
font-size: 0.875rem;
|
||||
@include lions-transition();
|
||||
}
|
||||
|
||||
&.ui-tree-toggler-expanded {
|
||||
.ui-tree-toggler-icon {
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.ui-treenode-icon {
|
||||
font-size: 1rem;
|
||||
color: $lions-gray-500;
|
||||
@include lions-transition();
|
||||
}
|
||||
|
||||
.ui-treenode-label {
|
||||
flex: 1;
|
||||
@include lions-font-size('base');
|
||||
color: $lions-text-primary;
|
||||
@include lions-transition();
|
||||
}
|
||||
|
||||
// Child nodes
|
||||
.ui-treenode-children {
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding-left: $lions-spacing-6;
|
||||
position: relative;
|
||||
|
||||
// Indent guide lines
|
||||
&::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: $lions-spacing-3;
|
||||
top: 0;
|
||||
bottom: $lions-spacing-2;
|
||||
width: 1px;
|
||||
background: $lions-gray-200;
|
||||
}
|
||||
}
|
||||
|
||||
// Leaf nodes (no children)
|
||||
&.ui-treenode-leaf {
|
||||
.ui-tree-toggler {
|
||||
visibility: hidden;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Drag & Drop support
|
||||
&.ui-tree-dragdrop {
|
||||
.ui-treenode-content {
|
||||
&.ui-treenode-droppable-active {
|
||||
background: rgba($lions-blue-50, 0.5);
|
||||
border: 2px dashed $lions-blue-400;
|
||||
}
|
||||
}
|
||||
|
||||
.ui-treenode-droppoint {
|
||||
height: 2px;
|
||||
background: $lions-blue-500;
|
||||
margin: $lions-spacing-1 0;
|
||||
@include lions-rounded('full');
|
||||
opacity: 0;
|
||||
@include lions-transition();
|
||||
|
||||
&.ui-treenode-droppoint-active {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Filter input
|
||||
.ui-tree-filter-container {
|
||||
margin-bottom: $lions-spacing-3;
|
||||
|
||||
.ui-tree-filter {
|
||||
width: 100%;
|
||||
height: $lions-input-height-base;
|
||||
padding: $lions-spacing-3 $lions-spacing-4;
|
||||
padding-left: 2.5rem;
|
||||
@include lions-rounded('md');
|
||||
border: 1px solid $lions-gray-300;
|
||||
@include lions-font-size('base');
|
||||
color: $lions-text-primary;
|
||||
background: $lions-white;
|
||||
@include lions-transition();
|
||||
|
||||
&::placeholder {
|
||||
color: $lions-gray-400;
|
||||
}
|
||||
|
||||
&:focus {
|
||||
outline: none;
|
||||
border-color: $lions-blue-500;
|
||||
box-shadow: 0 0 0 3px rgba($lions-blue-500, 0.15);
|
||||
}
|
||||
}
|
||||
|
||||
.ui-tree-filter-icon {
|
||||
position: absolute;
|
||||
left: $lions-spacing-3;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
color: $lions-gray-500;
|
||||
font-size: 1rem;
|
||||
}
|
||||
}
|
||||
|
||||
// Checkbox variant
|
||||
&.ui-tree-checkbox {
|
||||
.ui-chkbox {
|
||||
margin-right: $lions-spacing-2;
|
||||
|
||||
.ui-chkbox-box {
|
||||
width: 1.125rem;
|
||||
height: 1.125rem;
|
||||
@include lions-rounded('sm');
|
||||
border: 2px solid $lions-gray-400;
|
||||
background: $lions-white;
|
||||
@include lions-transition();
|
||||
|
||||
&.ui-state-active {
|
||||
@include lions-gradient-blue;
|
||||
border-color: $lions-blue-600;
|
||||
|
||||
.ui-chkbox-icon {
|
||||
color: $lions-white;
|
||||
font-size: 0.75rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Loading state
|
||||
&.ui-tree-loading {
|
||||
position: relative;
|
||||
|
||||
&::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background: rgba($lions-white, 0.8);
|
||||
@include lions-flex-center;
|
||||
z-index: 10;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// 📊 TREE TABLE STYLES
|
||||
// ============================================
|
||||
|
||||
.ui-treetable {
|
||||
@include lions-rounded('lg');
|
||||
border: 1px solid $lions-gray-200;
|
||||
background: $lions-white;
|
||||
overflow: hidden;
|
||||
@include lions-shadow('sm');
|
||||
|
||||
.ui-treetable-header {
|
||||
background: $lions-gray-100;
|
||||
border-bottom: 2px solid $lions-gray-300;
|
||||
}
|
||||
|
||||
.ui-treetable-thead {
|
||||
th {
|
||||
padding: $lions-spacing-4;
|
||||
@include lions-font-size('sm');
|
||||
@include lions-font-weight('semibold');
|
||||
color: $lions-text-primary;
|
||||
text-align: left;
|
||||
background: $lions-gray-100;
|
||||
border-bottom: 2px solid $lions-gray-300;
|
||||
@include lions-transition();
|
||||
|
||||
&.ui-sortable-column {
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
background: $lions-gray-200;
|
||||
}
|
||||
}
|
||||
|
||||
&.ui-state-active {
|
||||
background: $lions-blue-50;
|
||||
color: $lions-blue-700;
|
||||
|
||||
.ui-sortable-column-icon {
|
||||
color: $lions-blue-600;
|
||||
}
|
||||
}
|
||||
|
||||
.ui-sortable-column-icon {
|
||||
margin-left: $lions-spacing-2;
|
||||
color: $lions-gray-400;
|
||||
font-size: 0.875rem;
|
||||
@include lions-transition();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.ui-treetable-tbody {
|
||||
tr {
|
||||
@include lions-transition();
|
||||
|
||||
&:hover {
|
||||
background: rgba($lions-blue-50, 0.3);
|
||||
}
|
||||
|
||||
&.ui-state-highlight {
|
||||
background: $lions-blue-50;
|
||||
|
||||
td {
|
||||
color: $lions-blue-700;
|
||||
@include lions-font-weight('medium');
|
||||
}
|
||||
}
|
||||
|
||||
&.ui-treetable-row-selected {
|
||||
background: $lions-blue-50;
|
||||
border-left: 3px solid $lions-blue-500;
|
||||
}
|
||||
}
|
||||
|
||||
td {
|
||||
padding: $lions-spacing-4;
|
||||
border-bottom: 1px solid $lions-gray-200;
|
||||
@include lions-font-size('base');
|
||||
color: $lions-text-primary;
|
||||
@include lions-transition();
|
||||
|
||||
&.ui-treetable-toggler {
|
||||
width: 3rem;
|
||||
}
|
||||
}
|
||||
|
||||
.ui-treetable-toggler {
|
||||
@include lions-button-base;
|
||||
width: 1.75rem;
|
||||
height: 1.75rem;
|
||||
min-width: 1.75rem;
|
||||
padding: 0;
|
||||
@include lions-flex-center;
|
||||
background: transparent;
|
||||
border: 1px solid transparent;
|
||||
@include lions-rounded('md');
|
||||
color: $lions-gray-600;
|
||||
cursor: pointer;
|
||||
@include lions-transition();
|
||||
|
||||
&:hover {
|
||||
background: $lions-gray-100;
|
||||
border-color: $lions-gray-300;
|
||||
color: $lions-blue-600;
|
||||
}
|
||||
|
||||
.ui-treetable-toggler-icon {
|
||||
font-size: 0.875rem;
|
||||
@include lions-transition();
|
||||
}
|
||||
|
||||
&.ui-icon-triangle-1-e {
|
||||
.ui-treetable-toggler-icon {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
}
|
||||
|
||||
&.ui-icon-triangle-1-s {
|
||||
.ui-treetable-toggler-icon {
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Indent for child rows
|
||||
tr[data-level="1"] .ui-treetable-indent {
|
||||
padding-left: $lions-spacing-6;
|
||||
}
|
||||
|
||||
tr[data-level="2"] .ui-treetable-indent {
|
||||
padding-left: calc(#{$lions-spacing-6} * 2);
|
||||
}
|
||||
|
||||
tr[data-level="3"] .ui-treetable-indent {
|
||||
padding-left: calc(#{$lions-spacing-6} * 3);
|
||||
}
|
||||
|
||||
tr[data-level="4"] .ui-treetable-indent {
|
||||
padding-left: calc(#{$lions-spacing-6} * 4);
|
||||
}
|
||||
}
|
||||
|
||||
.ui-treetable-footer {
|
||||
padding: $lions-spacing-4;
|
||||
background: $lions-gray-50;
|
||||
border-top: 1px solid $lions-gray-200;
|
||||
@include lions-font-size('sm');
|
||||
color: $lions-gray-600;
|
||||
}
|
||||
|
||||
// Scrollable variant
|
||||
&.ui-treetable-scrollable {
|
||||
.ui-treetable-scrollable-header,
|
||||
.ui-treetable-scrollable-footer {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.ui-treetable-scrollable-body {
|
||||
overflow: auto;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.ui-treetable-scrollable-header-box {
|
||||
border-right: 1px solid $lions-gray-200;
|
||||
}
|
||||
}
|
||||
|
||||
// Striped rows
|
||||
&.ui-treetable-striped {
|
||||
.ui-treetable-tbody {
|
||||
tr:nth-child(odd) {
|
||||
background: $lions-gray-50;
|
||||
|
||||
&:hover {
|
||||
background: rgba($lions-blue-50, 0.5);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Resizable columns
|
||||
&.ui-treetable-resizable {
|
||||
.ui-treetable-thead {
|
||||
th {
|
||||
position: relative;
|
||||
|
||||
.ui-column-resizer {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
width: 8px;
|
||||
height: 100%;
|
||||
cursor: col-resize;
|
||||
background: transparent;
|
||||
@include lions-transition();
|
||||
|
||||
&:hover {
|
||||
background: $lions-blue-200;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Checkbox selection
|
||||
&.ui-treetable-checkbox {
|
||||
.ui-chkbox {
|
||||
.ui-chkbox-box {
|
||||
width: 1.125rem;
|
||||
height: 1.125rem;
|
||||
@include lions-rounded('sm');
|
||||
border: 2px solid $lions-gray-400;
|
||||
background: $lions-white;
|
||||
@include lions-transition();
|
||||
|
||||
&.ui-state-active {
|
||||
@include lions-gradient-blue;
|
||||
border-color: $lions-blue-600;
|
||||
|
||||
.ui-chkbox-icon {
|
||||
color: $lions-white;
|
||||
font-size: 0.75rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Loading overlay
|
||||
.ui-treetable-loading-overlay {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background: rgba($lions-white, 0.8);
|
||||
@include lions-flex-center;
|
||||
z-index: 10;
|
||||
|
||||
.ui-treetable-loading-icon {
|
||||
font-size: 2rem;
|
||||
color: $lions-blue-500;
|
||||
animation: lions-spin 1s linear infinite;
|
||||
}
|
||||
}
|
||||
|
||||
// Empty message
|
||||
.ui-treetable-emptymessage {
|
||||
padding: $lions-spacing-8;
|
||||
text-align: center;
|
||||
color: $lions-gray-500;
|
||||
@include lions-font-size('base');
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// 🎬 ANIMATIONS
|
||||
// ============================================
|
||||
|
||||
@keyframes lions-spin {
|
||||
0% {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
100% {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes lions-tree-expand {
|
||||
0% {
|
||||
opacity: 0;
|
||||
transform: translateY(-10px);
|
||||
}
|
||||
100% {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
// Tree node expand animation
|
||||
.ui-treenode-children {
|
||||
animation: lions-tree-expand 0.2s ease-out;
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// 📱 RESPONSIVE ADJUSTMENTS
|
||||
// ============================================
|
||||
|
||||
@media (max-width: $lions-breakpoint-md) {
|
||||
.ui-tree {
|
||||
.ui-treenode-content {
|
||||
padding: $lions-spacing-2;
|
||||
}
|
||||
|
||||
.ui-treenode-children {
|
||||
padding-left: $lions-spacing-4;
|
||||
}
|
||||
|
||||
.ui-tree-filter-container {
|
||||
.ui-tree-filter {
|
||||
@include lions-font-size('sm');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.ui-treetable {
|
||||
.ui-treetable-thead th,
|
||||
.ui-treetable-tbody td {
|
||||
padding: $lions-spacing-3;
|
||||
@include lions-font-size('sm');
|
||||
}
|
||||
|
||||
// Responsive table mode
|
||||
&.ui-treetable-responsive {
|
||||
.ui-treetable-thead {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.ui-treetable-tbody {
|
||||
tr {
|
||||
display: block;
|
||||
margin-bottom: $lions-spacing-3;
|
||||
border: 1px solid $lions-gray-200;
|
||||
@include lions-rounded('md');
|
||||
}
|
||||
|
||||
td {
|
||||
display: block;
|
||||
text-align: right;
|
||||
padding: $lions-spacing-3;
|
||||
border-bottom: 1px solid $lions-gray-100;
|
||||
|
||||
&::before {
|
||||
content: attr(data-label);
|
||||
float: left;
|
||||
font-weight: $lions-font-semibold;
|
||||
color: $lions-gray-700;
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// ♿ ACCESSIBILITY ENHANCEMENTS
|
||||
// ============================================
|
||||
|
||||
.ui-tree,
|
||||
.ui-treetable {
|
||||
// Focus visible
|
||||
.ui-treenode-content:focus-visible,
|
||||
.ui-tree-toggler:focus-visible,
|
||||
.ui-treetable-toggler:focus-visible {
|
||||
@include lions-focus-ring();
|
||||
}
|
||||
|
||||
// High contrast mode
|
||||
@media (prefers-contrast: high) {
|
||||
border-width: 2px !important;
|
||||
|
||||
.ui-treenode-content,
|
||||
.ui-tree-toggler,
|
||||
.ui-treetable-toggler,
|
||||
th,
|
||||
td {
|
||||
border-width: 2px !important;
|
||||
}
|
||||
}
|
||||
|
||||
// Reduced motion
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
* {
|
||||
transition: none !important;
|
||||
animation: none !important;
|
||||
}
|
||||
|
||||
.ui-tree-toggler-icon,
|
||||
.ui-treetable-toggler-icon {
|
||||
transform: none !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// 🎨 UTILITY CLASSES
|
||||
// ============================================
|
||||
|
||||
// Compact tree
|
||||
.ui-tree-compact {
|
||||
.ui-treenode-content {
|
||||
padding: $lions-spacing-1 $lions-spacing-2;
|
||||
}
|
||||
|
||||
.ui-treenode-label {
|
||||
@include lions-font-size('sm');
|
||||
}
|
||||
|
||||
.ui-treenode-children {
|
||||
padding-left: $lions-spacing-4;
|
||||
}
|
||||
}
|
||||
|
||||
// Large tree
|
||||
.ui-tree-lg {
|
||||
.ui-treenode-content {
|
||||
padding: $lions-spacing-3 $lions-spacing-4;
|
||||
}
|
||||
|
||||
.ui-treenode-label {
|
||||
@include lions-font-size('lg');
|
||||
}
|
||||
|
||||
.ui-tree-toggler {
|
||||
width: 2rem;
|
||||
height: 2rem;
|
||||
min-width: 2rem;
|
||||
}
|
||||
}
|
||||
|
||||
// Borderless tree
|
||||
.ui-tree-borderless {
|
||||
border: none;
|
||||
box-shadow: none;
|
||||
padding: 0;
|
||||
}
|
||||
@@ -0,0 +1,670 @@
|
||||
/*
|
||||
* ═══════════════════════════════════════════════════════════
|
||||
* Lions.dev - Utility Component Styles
|
||||
* ═══════════════════════════════════════════════════════════
|
||||
* Styles pour composants utilitaires
|
||||
* Divider, Spacer, Inplace, ThemeSelector
|
||||
*
|
||||
* Version: 1.0.0
|
||||
* Date: 2 Janvier 2026
|
||||
* Author: Lions Development Team
|
||||
* Priority: BASSE #27-28, #45-46
|
||||
* ═══════════════════════════════════════════════════════════
|
||||
*/
|
||||
|
||||
@import '../variables';
|
||||
@import '../mixins';
|
||||
|
||||
// ============================================
|
||||
// ➖ DIVIDER STYLES
|
||||
// ============================================
|
||||
|
||||
.ui-divider {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin: $lions-spacing-5 0;
|
||||
@include lions-transition();
|
||||
|
||||
// Horizontal divider (default)
|
||||
&.ui-divider-horizontal {
|
||||
flex-direction: row;
|
||||
|
||||
&::before,
|
||||
&::after {
|
||||
content: '';
|
||||
flex: 1;
|
||||
height: 1px;
|
||||
background: $lions-gray-300;
|
||||
}
|
||||
|
||||
&.ui-divider-left::before {
|
||||
flex: 0;
|
||||
margin-right: $lions-spacing-4;
|
||||
}
|
||||
|
||||
&.ui-divider-right::after {
|
||||
flex: 0;
|
||||
margin-left: $lions-spacing-4;
|
||||
}
|
||||
|
||||
&.ui-divider-center {
|
||||
&::before {
|
||||
margin-right: $lions-spacing-4;
|
||||
}
|
||||
|
||||
&::after {
|
||||
margin-left: $lions-spacing-4;
|
||||
}
|
||||
}
|
||||
|
||||
// No content variant
|
||||
&.ui-divider-no-content {
|
||||
&::before {
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
&::after {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Vertical divider
|
||||
&.ui-divider-vertical {
|
||||
flex-direction: column;
|
||||
min-height: 100px;
|
||||
margin: 0 $lions-spacing-5;
|
||||
|
||||
&::before,
|
||||
&::after {
|
||||
content: '';
|
||||
flex: 1;
|
||||
width: 1px;
|
||||
background: $lions-gray-300;
|
||||
}
|
||||
|
||||
&.ui-divider-top::before {
|
||||
flex: 0;
|
||||
margin-bottom: $lions-spacing-4;
|
||||
}
|
||||
|
||||
&.ui-divider-bottom::after {
|
||||
flex: 0;
|
||||
margin-top: $lions-spacing-4;
|
||||
}
|
||||
|
||||
&.ui-divider-center {
|
||||
&::before {
|
||||
margin-bottom: $lions-spacing-4;
|
||||
}
|
||||
|
||||
&::after {
|
||||
margin-top: $lions-spacing-4;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Content
|
||||
.ui-divider-content {
|
||||
@include lions-font-size('sm');
|
||||
@include lions-font-weight('medium');
|
||||
color: $lions-gray-600;
|
||||
padding: 0 $lions-spacing-3;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
// Style variants
|
||||
&.ui-divider-solid {
|
||||
&::before,
|
||||
&::after {
|
||||
border-style: solid;
|
||||
}
|
||||
}
|
||||
|
||||
&.ui-divider-dashed {
|
||||
&::before,
|
||||
&::after {
|
||||
border-style: dashed;
|
||||
background: none;
|
||||
border-width: 1px 0 0 0;
|
||||
height: 0;
|
||||
}
|
||||
|
||||
&.ui-divider-vertical {
|
||||
&::before,
|
||||
&::after {
|
||||
border-width: 0 0 0 1px;
|
||||
width: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.ui-divider-dotted {
|
||||
&::before,
|
||||
&::after {
|
||||
border-style: dotted;
|
||||
background: none;
|
||||
border-width: 1px 0 0 0;
|
||||
height: 0;
|
||||
}
|
||||
|
||||
&.ui-divider-vertical {
|
||||
&::before,
|
||||
&::after {
|
||||
border-width: 0 0 0 1px;
|
||||
width: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Color variants
|
||||
&.ui-divider-primary {
|
||||
&::before,
|
||||
&::after {
|
||||
background: $lions-blue-300;
|
||||
border-color: $lions-blue-300;
|
||||
}
|
||||
|
||||
.ui-divider-content {
|
||||
color: $lions-blue-700;
|
||||
}
|
||||
}
|
||||
|
||||
&.ui-divider-success {
|
||||
&::before,
|
||||
&::after {
|
||||
background: $lions-success-300;
|
||||
border-color: $lions-success-300;
|
||||
}
|
||||
|
||||
.ui-divider-content {
|
||||
color: $lions-success-700;
|
||||
}
|
||||
}
|
||||
|
||||
&.ui-divider-warning {
|
||||
&::before,
|
||||
&::after {
|
||||
background: $lions-warning-300;
|
||||
border-color: $lions-warning-300;
|
||||
}
|
||||
|
||||
.ui-divider-content {
|
||||
color: $lions-warning-700;
|
||||
}
|
||||
}
|
||||
|
||||
&.ui-divider-danger {
|
||||
&::before,
|
||||
&::after {
|
||||
background: $lions-danger-300;
|
||||
border-color: $lions-danger-300;
|
||||
}
|
||||
|
||||
.ui-divider-content {
|
||||
color: $lions-danger-700;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// ⬜ SPACER STYLES
|
||||
// ============================================
|
||||
|
||||
.ui-spacer {
|
||||
display: block;
|
||||
|
||||
// Default spacing
|
||||
&.ui-spacer-horizontal {
|
||||
width: 100%;
|
||||
height: $lions-spacing-5;
|
||||
}
|
||||
|
||||
&.ui-spacer-vertical {
|
||||
display: inline-block;
|
||||
width: $lions-spacing-5;
|
||||
height: auto;
|
||||
min-height: 1px;
|
||||
}
|
||||
|
||||
// Size variants
|
||||
&.ui-spacer-xs {
|
||||
&.ui-spacer-horizontal {
|
||||
height: $lions-spacing-1;
|
||||
}
|
||||
|
||||
&.ui-spacer-vertical {
|
||||
width: $lions-spacing-1;
|
||||
}
|
||||
}
|
||||
|
||||
&.ui-spacer-sm {
|
||||
&.ui-spacer-horizontal {
|
||||
height: $lions-spacing-3;
|
||||
}
|
||||
|
||||
&.ui-spacer-vertical {
|
||||
width: $lions-spacing-3;
|
||||
}
|
||||
}
|
||||
|
||||
&.ui-spacer-md {
|
||||
&.ui-spacer-horizontal {
|
||||
height: $lions-spacing-5;
|
||||
}
|
||||
|
||||
&.ui-spacer-vertical {
|
||||
width: $lions-spacing-5;
|
||||
}
|
||||
}
|
||||
|
||||
&.ui-spacer-lg {
|
||||
&.ui-spacer-horizontal {
|
||||
height: $lions-spacing-8;
|
||||
}
|
||||
|
||||
&.ui-spacer-vertical {
|
||||
width: $lions-spacing-8;
|
||||
}
|
||||
}
|
||||
|
||||
&.ui-spacer-xl {
|
||||
&.ui-spacer-horizontal {
|
||||
height: $lions-spacing-12;
|
||||
}
|
||||
|
||||
&.ui-spacer-vertical {
|
||||
width: $lions-spacing-12;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// ✏️ INPLACE STYLES
|
||||
// ============================================
|
||||
|
||||
.ui-inplace {
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
|
||||
.ui-inplace-display {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: $lions-spacing-2;
|
||||
padding: $lions-spacing-2 $lions-spacing-3;
|
||||
@include lions-rounded('md');
|
||||
cursor: pointer;
|
||||
@include lions-transition();
|
||||
@include lions-font-size('base');
|
||||
color: $lions-text-primary;
|
||||
|
||||
&:hover {
|
||||
background: $lions-gray-50;
|
||||
}
|
||||
|
||||
&:focus-visible {
|
||||
@include lions-focus-ring();
|
||||
}
|
||||
|
||||
.ui-inplace-display-icon {
|
||||
font-size: 1rem;
|
||||
color: $lions-gray-500;
|
||||
margin-left: $lions-spacing-2;
|
||||
}
|
||||
}
|
||||
|
||||
.ui-inplace-content {
|
||||
display: inline-block;
|
||||
padding: $lions-spacing-2;
|
||||
|
||||
.ui-inplace-save,
|
||||
.ui-inplace-cancel {
|
||||
@include lions-button-base;
|
||||
padding: $lions-spacing-2 $lions-spacing-3;
|
||||
@include lions-rounded('md');
|
||||
@include lions-font-weight('semibold');
|
||||
@include lions-font-size('sm');
|
||||
margin-left: $lions-spacing-2;
|
||||
cursor: pointer;
|
||||
@include lions-transition();
|
||||
}
|
||||
|
||||
.ui-inplace-save {
|
||||
@include lions-gradient-blue;
|
||||
color: $lions-white;
|
||||
border: none;
|
||||
|
||||
&:hover {
|
||||
@include lions-shadow('md');
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
}
|
||||
|
||||
.ui-inplace-cancel {
|
||||
background: $lions-gray-200;
|
||||
color: $lions-gray-700;
|
||||
border: 1px solid $lions-gray-300;
|
||||
|
||||
&:hover {
|
||||
background: $lions-gray-300;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.ui-inplace-disabled {
|
||||
.ui-inplace-display {
|
||||
cursor: not-allowed;
|
||||
opacity: 0.6;
|
||||
|
||||
&:hover {
|
||||
background: transparent;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// 🎨 THEME SELECTOR STYLES
|
||||
// ============================================
|
||||
|
||||
.ui-theme-selector {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
|
||||
.ui-theme-selector-button {
|
||||
@include lions-button-base;
|
||||
padding: $lions-spacing-2 $lions-spacing-4;
|
||||
@include lions-rounded('md');
|
||||
background: $lions-gray-100;
|
||||
border: 1px solid $lions-gray-300;
|
||||
color: $lions-gray-700;
|
||||
@include lions-flex-center;
|
||||
gap: $lions-spacing-2;
|
||||
cursor: pointer;
|
||||
@include lions-transition();
|
||||
|
||||
&:hover {
|
||||
background: $lions-gray-200;
|
||||
border-color: $lions-gray-400;
|
||||
}
|
||||
|
||||
&:focus-visible {
|
||||
@include lions-focus-ring();
|
||||
}
|
||||
|
||||
.ui-theme-selector-icon {
|
||||
font-size: 1.125rem;
|
||||
color: $lions-blue-600;
|
||||
}
|
||||
|
||||
.ui-theme-selector-label {
|
||||
@include lions-font-size('sm');
|
||||
@include lions-font-weight('medium');
|
||||
}
|
||||
|
||||
.ui-theme-selector-chevron {
|
||||
font-size: 0.875rem;
|
||||
color: $lions-gray-500;
|
||||
@include lions-transition();
|
||||
}
|
||||
|
||||
&.ui-state-active {
|
||||
.ui-theme-selector-chevron {
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.ui-theme-selector-panel {
|
||||
position: absolute;
|
||||
top: calc(100% + $lions-spacing-2);
|
||||
right: 0;
|
||||
min-width: 200px;
|
||||
background: $lions-white;
|
||||
@include lions-rounded('lg');
|
||||
@include lions-shadow('xl');
|
||||
border: 1px solid $lions-gray-200;
|
||||
padding: $lions-spacing-2;
|
||||
z-index: 1000;
|
||||
opacity: 0;
|
||||
visibility: hidden;
|
||||
transform: translateY(-10px);
|
||||
@include lions-transition();
|
||||
|
||||
&.ui-state-active {
|
||||
opacity: 1;
|
||||
visibility: visible;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
.ui-theme-selector-list {
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.ui-theme-selector-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: $lions-spacing-3;
|
||||
padding: $lions-spacing-3;
|
||||
@include lions-rounded('md');
|
||||
cursor: pointer;
|
||||
@include lions-transition();
|
||||
|
||||
&:hover {
|
||||
background: $lions-gray-50;
|
||||
}
|
||||
|
||||
&.ui-state-active {
|
||||
background: $lions-blue-50;
|
||||
|
||||
.ui-theme-selector-item-label {
|
||||
color: $lions-blue-700;
|
||||
@include lions-font-weight('semibold');
|
||||
}
|
||||
|
||||
.ui-theme-selector-item-check {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.ui-theme-selector-item-preview {
|
||||
width: 2rem;
|
||||
height: 2rem;
|
||||
@include lions-rounded('md');
|
||||
border: 2px solid $lions-gray-300;
|
||||
display: flex;
|
||||
overflow: hidden;
|
||||
|
||||
.ui-theme-color {
|
||||
flex: 1;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.ui-theme-selector-item-label {
|
||||
flex: 1;
|
||||
@include lions-font-size('sm');
|
||||
color: $lions-text-primary;
|
||||
}
|
||||
|
||||
.ui-theme-selector-item-check {
|
||||
font-size: 1rem;
|
||||
color: $lions-blue-600;
|
||||
opacity: 0;
|
||||
@include lions-transition();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// 🎯 SKELETON LOADER (Bonus Utility)
|
||||
// ============================================
|
||||
|
||||
.ui-skeleton {
|
||||
background: linear-gradient(
|
||||
90deg,
|
||||
$lions-gray-200 0%,
|
||||
$lions-gray-100 50%,
|
||||
$lions-gray-200 100%
|
||||
);
|
||||
background-size: 200% 100%;
|
||||
animation: lions-skeleton-loading 1.5s ease-in-out infinite;
|
||||
@include lions-rounded('md');
|
||||
|
||||
&.ui-skeleton-circle {
|
||||
@include lions-rounded('full');
|
||||
width: 3rem;
|
||||
height: 3rem;
|
||||
}
|
||||
|
||||
&.ui-skeleton-text {
|
||||
height: 1rem;
|
||||
width: 100%;
|
||||
|
||||
&.ui-skeleton-sm {
|
||||
height: 0.75rem;
|
||||
}
|
||||
|
||||
&.ui-skeleton-lg {
|
||||
height: 1.25rem;
|
||||
}
|
||||
}
|
||||
|
||||
&.ui-skeleton-rect {
|
||||
width: 100%;
|
||||
height: 10rem;
|
||||
}
|
||||
|
||||
&.ui-skeleton-button {
|
||||
width: 8rem;
|
||||
height: 2.5rem;
|
||||
}
|
||||
|
||||
&.ui-skeleton-card {
|
||||
width: 100%;
|
||||
height: 15rem;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes lions-skeleton-loading {
|
||||
0% {
|
||||
background-position: 200% 0;
|
||||
}
|
||||
100% {
|
||||
background-position: -200% 0;
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// 📱 RESPONSIVE ADJUSTMENTS
|
||||
// ============================================
|
||||
|
||||
@media (max-width: $lions-breakpoint-md) {
|
||||
.ui-divider {
|
||||
&.ui-divider-horizontal {
|
||||
margin: $lions-spacing-4 0;
|
||||
|
||||
.ui-divider-content {
|
||||
@include lions-font-size('xs');
|
||||
}
|
||||
}
|
||||
|
||||
&.ui-divider-vertical {
|
||||
min-height: 60px;
|
||||
margin: 0 $lions-spacing-3;
|
||||
}
|
||||
}
|
||||
|
||||
.ui-theme-selector {
|
||||
.ui-theme-selector-panel {
|
||||
right: auto;
|
||||
left: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.ui-inplace {
|
||||
.ui-inplace-display {
|
||||
padding: $lions-spacing-2;
|
||||
@include lions-font-size('sm');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// ♿ ACCESSIBILITY ENHANCEMENTS
|
||||
// ============================================
|
||||
|
||||
.ui-inplace,
|
||||
.ui-theme-selector {
|
||||
// Focus visible
|
||||
button:focus-visible,
|
||||
.ui-inplace-display:focus-visible,
|
||||
.ui-theme-selector-button:focus-visible {
|
||||
@include lions-focus-ring();
|
||||
}
|
||||
|
||||
// High contrast mode
|
||||
@media (prefers-contrast: high) {
|
||||
border-width: 2px !important;
|
||||
|
||||
button,
|
||||
.ui-inplace-display,
|
||||
.ui-theme-selector-button {
|
||||
border-width: 2px !important;
|
||||
}
|
||||
}
|
||||
|
||||
// Reduced motion
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
* {
|
||||
transition: none !important;
|
||||
animation: none !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Skeleton - Reduced motion
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
.ui-skeleton {
|
||||
animation: none !important;
|
||||
background: $lions-gray-200;
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// 🎨 UTILITY CLASSES
|
||||
// ============================================
|
||||
|
||||
// Hidden divider (just for spacing)
|
||||
.ui-divider-hidden {
|
||||
&::before,
|
||||
&::after {
|
||||
display: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
// Thick divider
|
||||
.ui-divider-thick {
|
||||
&::before,
|
||||
&::after {
|
||||
height: 2px !important;
|
||||
|
||||
&.ui-divider-vertical {
|
||||
width: 2px !important;
|
||||
height: auto !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Inplace full width
|
||||
.ui-inplace-fullwidth {
|
||||
display: block;
|
||||
width: 100%;
|
||||
|
||||
.ui-inplace-display {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
* ═══════════════════════════════════════════════════════════
|
||||
* Lions.dev - Main SCSS Entry Point
|
||||
* ═══════════════════════════════════════════════════════════
|
||||
* Système de design complet Lions.dev pour Freya Extension
|
||||
*
|
||||
* Version: 1.0.0
|
||||
* Date: 1er Janvier 2026
|
||||
* Author: Lions Development Team
|
||||
* ═══════════════════════════════════════════════════════════
|
||||
*/
|
||||
|
||||
// ============================================
|
||||
// 🏗️ FONDATIONS
|
||||
// ============================================
|
||||
|
||||
// Variables globales
|
||||
@import 'variables';
|
||||
|
||||
// Mixins utilitaires
|
||||
@import 'mixins';
|
||||
|
||||
// ============================================
|
||||
// 🧩 COMPOSANTS
|
||||
// ============================================
|
||||
|
||||
// Boutons (✅ COMPLÉTÉ - Priority #1-4)
|
||||
@import 'components/button';
|
||||
|
||||
// Champs de formulaire (✅ COMPLÉTÉ - Priority #5-8)
|
||||
@import 'components/field';
|
||||
|
||||
// Layout - Card & Panel (✅ COMPLÉTÉ - Priority #9-10)
|
||||
@import 'components/card';
|
||||
|
||||
// Data Display - DataTable & DataView (✅ COMPLÉTÉ - Priority #16-17)
|
||||
@import 'components/data';
|
||||
|
||||
// Messages & Notifications - Messages, Toast, Growl (✅ COMPLÉTÉ - Priority #18-20)
|
||||
@import 'components/messages';
|
||||
|
||||
// Overlays - Dialog, ConfirmDialog, Sidebar, OverlayPanel (✅ COMPLÉTÉ - Priority #21-24)
|
||||
@import 'components/overlays';
|
||||
|
||||
// Navigation - Menu, Menubar, TabView (✅ COMPLÉTÉ - Priority #25-27)
|
||||
@import 'components/navigation';
|
||||
|
||||
// Form Controls - Checkbox, Radio, Switch, Toggle, Slider, Rating (✅ COMPLÉTÉ - Priority #28-33)
|
||||
@import 'components/form-controls';
|
||||
|
||||
// Display - Avatar, Badge, Tag (✅ COMPLÉTÉ - Priority #34-36)
|
||||
@import 'components/display';
|
||||
|
||||
// Breadcrumb & Steps - Navigation secondaire (✅ COMPLÉTÉ - Priority #37-38)
|
||||
@import 'components/breadcrumb-steps';
|
||||
|
||||
// Progress - Barre de progression (✅ COMPLÉTÉ - Priority #39)
|
||||
@import 'components/progress';
|
||||
|
||||
// Form Advanced - Spinner, Mask, Chips, ColorPicker, Editor, FileUpload (✅ COMPLÉTÉ - Priority #40-44)
|
||||
@import 'components/form-advanced';
|
||||
|
||||
// Tree & TreeTable - Affichage hiérarchique (✅ COMPLÉTÉ - Priority #31-32)
|
||||
@import 'components/tree';
|
||||
|
||||
// Utilities - Divider, Spacer, Inplace, ThemeSelector (✅ COMPLÉTÉ - Priority #27-28, #45-46)
|
||||
@import 'components/utilities';
|
||||
|
||||
// Chart - Visualisation de données avec Chart.js (✅ COMPLÉTÉ - Priority #44)
|
||||
@import 'components/chart';
|
||||
|
||||
// ✅ TOUS LES COMPOSANTS LIONS.DEV COMPLÉTÉS!
|
||||
// Total: 46 composants avec styles complets
|
||||
Reference in New Issue
Block a user