18 KiB
Guide d'Utilisation - Freya avec PrimeFaces
Version : Freya 5.0.0 + PrimeFaces 14.0.0 Date : 2025-12-25 Sources : Documentation officielle Freya et exemples
🎯 Introduction
Ce guide montre comment utiliser les composants PrimeFaces standards avec le thème Freya dans vos applications Quarkus, basé sur les exemples officiels de Freya 5.0.0.
Important : Freya est un thème CSS pour PrimeFaces, pas une bibliothèque de composants. Vous utilisez les composants PrimeFaces standards (p:inputText, p:dataTable, etc.) avec les classes CSS de Freya.
📦 Installation
1. Dépendances Maven
<dependency>
<groupId>dev.lions</groupId>
<artifactId>primefaces-freya-extension</artifactId>
<version>1.0.0-SNAPSHOT</version>
</dependency>
Cette extension fournit :
- ✅ Thème Freya
- ✅ Templates de layout
- ✅ Composant
FreyaMenu - ✅ Bean
GuestPreferences
2. Configuration
application.properties :
primefaces.THEME=freya
primefaces.FONT_AWESOME=true
primefaces.CLIENT_SIDE_VALIDATION=true
primefaces.MOVE_SCRIPTS_TO_BOTTOM=true
🎨 Classes CSS Freya
Layout Classes (PrimeFlex)
Freya utilise PrimeFlex pour le layout :
<div class="grid"> <!-- Grille responsive -->
<div class="col-12 md:col-6"> <!-- Colonne : 12 cols mobile, 6 desktop -->
<div class="card"> <!-- Carte Freya -->
<!-- Contenu -->
</div>
</div>
</div>
Classes principales :
grid- Conteneur grillecol-{n}- Colonnes (1-12)md:col-{n},lg:col-{n}- Breakpoints responsivecard- Carte Freya avec ombre et border-radiusui-fluid- Inputs 100% width
Form Classes
Pattern Field (recommandé) :
<div class="field">
<p:outputLabel for="name" value="Nom" />
<p:inputText id="name" value="#{bean.nom}" />
<p:message for="name" />
</div>
Grid Formgrid :
<div class="grid formgrid">
<div class="col-12 md:col-6">
<div class="field">
<p:outputLabel for="firstname" value="Prénom" />
<p:inputText id="firstname" value="#{bean.prenom}" />
</div>
</div>
<div class="col-12 md:col-6">
<div class="field">
<p:outputLabel for="lastname" value="Nom" />
<p:inputText id="lastname" value="#{bean.nom}" />
</div>
</div>
</div>
Float Label :
<span class="ui-float-label">
<p:inputText id="float-input" value="#{bean.username}" />
<p:outputLabel for="@previous" value="Username" />
</span>
Icons in Inputs
<span class="ui-input-icon-left">
<i class="pi pi-user"></i>
<p:inputText placeholder="Username" />
</span>
<span class="ui-input-icon-right">
<p:inputText placeholder="Search" />
<i class="pi pi-search"></i>
</span>
📝 Composants de Formulaire
InputText
Basique :
<p:inputText value="#{bean.nom}" placeholder="Nom" />
Avec field pattern :
<div class="field">
<p:outputLabel for="nom" value="Nom" />
<p:inputText id="nom" value="#{bean.nom}" required="true" />
<p:message for="nom" />
</div>
Disabled / Erreur :
<p:inputText value="#{bean.nom}" disabled="true" />
<p:inputText value="#{bean.nom}" styleClass="ui-state-error" />
InputTextarea
<div class="field">
<p:outputLabel for="description" value="Description" />
<p:inputTextarea id="description"
value="#{bean.description}"
rows="5"
autoResize="false" />
</div>
SelectOneMenu (Dropdown)
<div class="field">
<p:outputLabel for="category" value="Catégorie" />
<p:selectOneMenu id="category" value="#{bean.category}">
<f:selectItem itemLabel="Sélectionnez..." itemValue="" />
<f:selectItem itemLabel="Option 1" itemValue="1" />
<f:selectItem itemLabel="Option 2" itemValue="2" />
</p:selectOneMenu>
</div>
Avec filtre :
<p:selectOneMenu value="#{bean.category}" filter="true" filterMatchMode="contains">
<f:selectItems value="#{bean.categories}" />
</p:selectOneMenu>
Calendar (DatePicker)
<div class="field">
<p:outputLabel for="date" value="Date" />
<p:datePicker id="date" value="#{bean.date}" showIcon="true" />
</div>
SelectManyCheckbox
<div class="field">
<p:outputLabel value="Options" />
<p:selectManyCheckbox value="#{bean.selectedOptions}" layout="responsive" columns="3">
<f:selectItem itemLabel="Option 1" itemValue="1" />
<f:selectItem itemLabel="Option 2" itemValue="2" />
<f:selectItem itemLabel="Option 3" itemValue="3" />
</p:selectManyCheckbox>
</div>
SelectOneRadio
<div class="field">
<p:outputLabel value="Catégorie" />
<p:selectOneRadio value="#{bean.category}" layout="responsive" columns="2">
<f:selectItem itemLabel="Accessories" itemValue="Accessories" />
<f:selectItem itemLabel="Clothing" itemValue="Clothing" />
</p:selectOneRadio>
</div>
📊 Composants de Données
DataTable
Basique :
<p:dataTable value="#{bean.users}" var="user" paginator="true" rows="10">
<p:column headerText="Nom">
<h:outputText value="#{user.nom}" />
</p:column>
<p:column headerText="Email">
<h:outputText value="#{user.email}" />
</p:column>
</p:dataTable>
Complet (Pattern Freya CRUD) :
<h:form id="form">
<p:growl id="messages" showDetail="true" />
<!-- Toolbar -->
<p:toolbar styleClass="mb-4">
<p:toolbarGroup>
<p:commandButton value="Nouveau"
icon="pi pi-plus"
actionListener="#{bean.openNew}"
update="dialog-content"
oncomplete="PF('dialog').show()"
styleClass="ui-button-success" />
<p:commandButton value="Supprimer"
icon="pi pi-trash"
actionListener="#{bean.deleteSelected}"
styleClass="ui-button-danger"
disabled="#{!bean.hasSelected()}"
update="@this">
<p:confirm header="Confirmation"
message="Supprimer les éléments sélectionnés ?"
icon="pi pi-exclamation-triangle" />
</p:commandButton>
</p:toolbarGroup>
<p:toolbarGroup align="right">
<p:fileUpload mode="simple" skinSimple="true" label="Import" />
<p:commandButton value="Export" icon="pi pi-upload" ajax="false">
<p:dataExporter type="pdf" target="dt" fileName="data"/>
</p:commandButton>
</p:toolbarGroup>
</p:toolbar>
<!-- DataTable -->
<p:dataTable id="dt"
widgetVar="dtTable"
var="item"
value="#{bean.items}"
selection="#{bean.selectedItems}"
rowKey="#{item.id}"
paginator="true"
rows="10"
reflow="true">
<f:facet name="header">
<div class="products-table-header">
<span style="font-weight: bold">Gestion des Données</span>
<span class="filter-container ui-input-icon-left">
<i class="pi pi-search"></i>
<p:inputText id="globalFilter"
onkeyup="PF('dtTable').filter()"
placeholder="Rechercher" />
</span>
</div>
</f:facet>
<p:ajax event="rowSelect" update=":form:toolbar" />
<p:ajax event="rowUnselect" update=":form:toolbar" />
<p:column selectionMode="multiple" exportable="false" />
<p:column headerText="Nom" sortBy="#{item.nom}" filterBy="#{item.nom}">
<h:outputText value="#{item.nom}" />
</p:column>
<p:column exportable="false">
<p:commandButton icon="pi pi-pencil"
update=":form:dialog-content"
oncomplete="PF('dialog').show()"
styleClass="edit-button rounded-button ui-button-success">
<f:setPropertyActionListener value="#{item}" target="#{bean.selectedItem}" />
</p:commandButton>
<p:commandButton icon="pi pi-trash"
styleClass="ui-button-warning rounded-button"
oncomplete="PF('deleteDialog').show()">
<f:setPropertyActionListener value="#{item}" target="#{bean.selectedItem}" />
</p:commandButton>
</p:column>
</p:dataTable>
</h:form>
🗂️ Composants Panel
Dialog
Basique :
<p:dialog header="Détails"
widgetVar="dialog"
modal="true"
showEffect="fade"
responsive="true">
<p:outputPanel id="dialog-content">
<!-- Contenu -->
</p:outputPanel>
</p:dialog>
Avec formulaire et footer :
<p:dialog header="Éditer"
widgetVar="editDialog"
modal="true"
responsive="true">
<p:outputPanel id="edit-content" class="ui-fluid">
<p:outputPanel rendered="#{not empty bean.selectedItem}">
<div class="field">
<p:outputLabel for="name" value="Nom" />
<p:inputText id="name" value="#{bean.selectedItem.nom}" required="true" />
</div>
<div class="field">
<p:outputLabel for="description" value="Description" />
<p:inputTextarea id="description" value="#{bean.selectedItem.description}" />
</div>
</p:outputPanel>
</p:outputPanel>
<f:facet name="footer">
<p:commandButton value="Annuler"
icon="pi pi-times"
onclick="PF('editDialog').hide()"
styleClass="ui-button-secondary" />
<p:commandButton value="Sauvegarder"
icon="pi pi-check"
actionListener="#{bean.save}"
update="edit-content"
oncomplete="if(!args.validationFailed) PF('editDialog').hide()"
styleClass="ui-button-success" />
</f:facet>
</p:dialog>
Panel
<p:panel header="Titre du Panel" toggleable="true" collapsed="false">
<p>Contenu du panel...</p>
</p:panel>
AccordionPanel
<p:accordionPanel multiple="false">
<p:tab title="Section 1">
<p>Contenu de la section 1...</p>
</p:tab>
<p:tab title="Section 2">
<p>Contenu de la section 2...</p>
</p:tab>
</p:accordionPanel>
🔘 Composants Button
CommandButton
Basique :
<p:commandButton value="Sauvegarder"
action="#{bean.save}"
update="form" />
Avec icône :
<p:commandButton value="Nouveau"
icon="pi pi-plus"
actionListener="#{bean.create}" />
Avec styles Freya :
<!-- Success -->
<p:commandButton value="Créer"
icon="pi pi-check"
styleClass="ui-button-success" />
<!-- Warning -->
<p:commandButton value="Attention"
icon="pi pi-exclamation-triangle"
styleClass="ui-button-warning" />
<!-- Danger -->
<p:commandButton value="Supprimer"
icon="pi pi-trash"
styleClass="ui-button-danger" />
<!-- Secondary -->
<p:commandButton value="Annuler"
icon="pi pi-times"
styleClass="ui-button-secondary" />
<!-- Rounded -->
<p:commandButton icon="pi pi-pencil"
styleClass="rounded-button ui-button-success" />
Groupe de boutons :
<f:facet name="footer">
<p:commandButton value="Annuler"
icon="pi pi-times"
styleClass="ui-button-secondary" />
<p:commandButton value="Sauvegarder"
icon="pi pi-check"
styleClass="ui-button-success" />
</f:facet>
💬 Composants Message
Message (pour un champ)
<div class="field">
<p:outputLabel for="email" value="Email" />
<p:inputText id="email" value="#{bean.email}" required="true" />
<p:message for="email" />
</div>
Messages (tous les messages)
<h:form>
<p:messages id="messages" showDetail="true" closable="true" />
<!-- Formulaire -->
</h:form>
Growl (notifications toast)
<h:form>
<p:growl id="growl" showDetail="true" />
<p:commandButton value="Save"
action="#{bean.save}"
update="growl" />
</h:form>
Dans le bean :
public void save() {
// Logic...
FacesContext.getCurrentInstance().addMessage(null,
new FacesMessage(FacesMessage.SEVERITY_INFO,
"Succès",
"Données sauvegardées avec succès"));
}
📐 Patterns de Layout
Page Complète avec Card
<ui:composition template="/WEB-INF/template.xhtml">
<ui:define name="title">Ma Page</ui:define>
<ui:define name="content">
<div class="grid">
<div class="col-12">
<div class="card">
<h5>Titre</h5>
<p>Contenu...</p>
</div>
</div>
</div>
</ui:define>
</ui:composition>
Deux Colonnes
<div class="grid">
<div class="col-12 lg:col-6">
<div class="card">
<h5>Colonne Gauche</h5>
<!-- Contenu -->
</div>
</div>
<div class="col-12 lg:col-6">
<div class="card">
<h5>Colonne Droite</h5>
<!-- Contenu -->
</div>
</div>
</div>
Formulaire Complet
<div class="grid">
<div class="col-12">
<div class="card">
<h:form id="userForm" class="ui-fluid">
<h5>Nouvel Utilisateur</h5>
<div class="grid formgrid">
<div class="col-12 md:col-6">
<div class="field">
<p:outputLabel for="firstname" value="Prénom" />
<p:inputText id="firstname" value="#{bean.user.firstname}" required="true" />
<p:message for="firstname" />
</div>
</div>
<div class="col-12 md:col-6">
<div class="field">
<p:outputLabel for="lastname" value="Nom" />
<p:inputText id="lastname" value="#{bean.user.lastname}" required="true" />
<p:message for="lastname" />
</div>
</div>
</div>
<div class="field">
<p:outputLabel for="email" value="Email" />
<p:inputText id="email" value="#{bean.user.email}" required="true" />
<p:message for="email" />
</div>
<div class="field">
<p:outputLabel for="role" value="Rôle" />
<p:selectOneMenu id="role" value="#{bean.user.role}">
<f:selectItem itemLabel="Sélectionnez..." itemValue="" />
<f:selectItems value="#{bean.roles}" />
</p:selectOneMenu>
</div>
<div class="field">
<p:commandButton value="Annuler"
icon="pi pi-times"
styleClass="ui-button-secondary"
style="margin-right: .5rem" />
<p:commandButton value="Sauvegarder"
icon="pi pi-check"
action="#{bean.save}"
update="userForm"
styleClass="ui-button-success" />
</div>
</h:form>
</div>
</div>
</div>
🎨 Composant FreyaMenu
Usage :
<html xmlns:fr="http://primefaces.org/freya">
<fr:menu model="#{menuBean.model}" />
Bean :
@Named
@SessionScoped
public class MenuBean implements Serializable {
private MenuModel model;
@PostConstruct
public void init() {
model = new DefaultMenuModel();
DefaultMenuItem item1 = DefaultMenuItem.builder()
.value("Dashboard")
.icon("pi pi-home")
.outcome("/dashboard.xhtml")
.build();
model.getElements().add(item1);
DefaultSubMenu subMenu = DefaultSubMenu.builder()
.label("Gestion")
.icon("pi pi-cog")
.build();
DefaultMenuItem subItem = DefaultMenuItem.builder()
.value("Utilisateurs")
.outcome("/users/list.xhtml")
.build();
subMenu.getElements().add(subItem);
model.getElements().add(subMenu);
}
public MenuModel getModel() {
return model;
}
}
📚 Ressources
Documentation Officielle
- PrimeFaces : https://primefaces.github.io/primefaces/14_0_0/
- PrimeFlex : https://www.primefaces.org/primeflex/
- PrimeIcons : https://www.primefaces.org/primeicons/
Exemples Sources Freya
- Localisation :
C:/Users/dadyo/PersonalProjects/lions-workspace/freya/tag/src/main/webapp/ - Fichiers :
input.xhtml,crud.xhtml,formlayout.xhtml,button.xhtml, etc.
✅ Bonnes Pratiques
- Utiliser les classes Freya :
grid,card,field,formgrid - Pattern field cohérent : Label + Input + Message
- ui-fluid pour formulaires : Inputs 100% width
- Responsive design : Utiliser
col-12 lg:col-6etc. - Icônes PrimeIcons :
pi pi-* - Styles de boutons :
ui-button-success,ui-button-danger, etc. - Messages appropriés :
p:messagepour champs,p:growlpour notifications - Dialog modal : Toujours avec
modal="true"etresponsive="true"
Version : 1.0.0 Dernière mise à jour : 2025-12-25