@altazion/commerce-sdk-htmx
v26.519.8051
Published
Altazion Commerce SDK — Extension HTMX + helpers Handlebars
Readme
@altazion/commerce-sdk-htmx
Extension HTMX et helpers Handlebars pour l'Altazion Commerce SDK. Permet d'intégrer le SDK dans des projets à rendu serveur utilisant htmx et/ou Handlebars, sans framework JavaScript.
Installation
npm install @altazion/commerce-sdk-htmx @altazion/commerce-sdk-core
npm install htmx.org handlebars # peer dependencies (optionnelles)Initialisation
import { CommerceClient } from '@altazion/commerce-sdk-core'
import { initAltazionHtmx } from '@altazion/commerce-sdk-htmx'
import Handlebars from 'handlebars'
const client = new CommerceClient({
baseUrl: 'https://votre-api.altazion.com',
})
initAltazionHtmx({
client,
handlebars: Handlebars, // optionnel — active les helpers HBS
offlineSelector: '#app', // optionnel — élément qui reçoit la classe altz-offline
terminalMode: {
interactiveSelectors: ['#app-main', '.altz-action-bar'],
offlineScreenSelectors: '#offline-screen',
},
})Mode CDN (sans bundler)
<script src="https://cdn.jsdelivr.net/npm/htmx.org@2/dist/htmx.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/handlebars/dist/handlebars.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/@altazion/commerce-sdk-core/dist/index.iife.js"></script>
<script src="https://cdn.jsdelivr.net/npm/@altazion/commerce-sdk-htmx/dist/index.iife.js"></script>
<script>
const client = new AltazionCommerceCore.CommerceClient({
baseUrl: 'https://votre-api.altazion.com'
})
AltazionCommerceSdkHtmx.initAltazionHtmx({ client, handlebars: Handlebars })
</script>Helpers Handlebars
Prix
| Helper | Description | Exemple |
|---|---|---|
| {{formatPrice value}} | Prix formaté selon la locale | 12,90 € |
| {{formatPriceNoSymbol value}} | Prix sans symbole monétaire | 12,90 |
Produit
| Helper | Description |
|---|---|
| {{productUrl sku}} | URL canonique d'un produit |
| {{productImageUrl url size}} | URL d'image redimensionnée |
Panier
| Helper | Description |
|---|---|
| {{cartItemCount}} | Nombre total d'articles dans le panier |
| {{cartTotal}} | Total TTC formaté |
| {{#cartHasItems}}...{{/cartHasItems}} | Bloc conditionnel si panier non vide |
| {{cartLineCount}} | Nombre de lignes dans le panier |
Templates pré-compilés
Le package expose des templates Handlebars prêts à l'emploi accessibles via :
import { templates } from '@altazion/commerce-sdk-htmx'
// templates.productCard — carte produit
// templates.cartMini — mini panier
// templates.productList — liste de produitsExtension HTMX Altazion
L'initialisation enregistre automatiquement une extension HTMX altazion qui :
- injecte les headers de session sur les requêtes HTMX ;
- interprète les attributs
hx-altazion-cart-actionpour appeler directementclient.cart.*du SDK core ; - interprète les attributs
hx-altazion-session-actionpour appeler directementclient.session.*du SDK core ; - interprète les attributs
hx-altazion-marketing-actionpour appeler directementclient.marketing.*du SDK core ; - interprète les attributs
hx-altazion-stores-actionpour appeler directementclient.stores.*du SDK core ; - émet des événements DOM
altazion:cart:*exploitables par HTMX pour recharger des fragments.
Convention de nommage
La convention officielle du DSL HTMX Altazion est la suivante :
- activation :
hx-ext="altazion"; - action métier :
hx-altazion-<module>-action; - paramètres simples :
hx-altazion-<module>-<parametre-kebab-case>; - payload riche : formulaire ou
hx-vals; - refresh partagé :
hx-altazion-refresh; - événements :
altazion:<module>:before|after|error, avec des événements complémentaires selon le module (updated,loaded,changed, etc.).
La convention détaillée est décrite dans specs-temp/sdk.htmx-naming-convention.md.
Exemple addItem
<div hx-ext="altazion">
<button
hx-altazion-cart-action="addItem"
hx-vals='{"reference":"REF-001","quantity":1}'
hx-altazion-refresh="#cart-mini; #cart-badge"
>
Ajouter au panier
</button>
<aside
id="cart-mini"
hx-get="/fragments/cart-mini"
hx-swap="outerHTML"
></aside>
</div>Actions supportées
| Action | Méthode SDK | Payload attendu |
|---|---|---|
| getCart | client.cart.getCart() | aucun |
| getValidationStatus | client.cart.getValidationStatus() | aucun |
| addItem | client.cart.addItem(reference, quantity, options?) | reference, quantity |
| updateItem | client.cart.updateItem(lineId, quantity) | lineId, quantity |
| removeItem | client.cart.removeItem(lineId) | lineId |
| applyCoupon | client.cart.applyCoupon(code) | code |
| removeCoupon | client.cart.removeCoupon(code) | code |
Le payload peut être fourni via hx-vals, via les champs d'un formulaire, ou avec les attributs dédiés hx-altazion-cart-reference, hx-altazion-cart-quantity, hx-altazion-cart-line-id, hx-altazion-cart-code.
Session: lecture de la session courante
Pour les sites en server rendering, la session peut déjà exister côté serveur et être portée par cookie. Le premier besoin côté HTMX est donc souvent simplement de relire cet état pour rafraîchir des fragments.
<section
hx-ext="altazion"
hx-altazion-session-action="getSession"
hx-trigger="load, altazion:session:refresh from:body"
hx-altazion-refresh="#header-session; #mini-cart"
>
</section>
<header id="header-session" hx-get="/fragments/header-session" hx-swap="outerHTML"></header>
<aside id="mini-cart" hx-get="/fragments/cart-mini" hx-swap="outerHTML"></aside>Action supportée pour l'instant :
| Action | Méthode SDK | Payload attendu |
|---|---|---|
| getSession | client.session.getSession() | aucun |
Événements émis :
| Événement | Description |
|---|---|
| altazion:session:before | avant l'appel au SDK |
| altazion:session:after | après un appel réussi |
| altazion:session:loaded | après getSession |
| altazion:session:error | en cas d'erreur |
Rendu déclaratif inline
Quand handlebars est fourni à initAltazionHtmx, le package sait aussi piloter un rendu inline déclaratif dans le DOM.
Le pattern visé est :
<section
hx-ext="altazion"
hx-altazion-marketing-action="getItems"
hx-altazion-marketing-codes='["HOMEPAGEHERO"]'
hx-altazion-data-slot="homepageHero"
></section>
<aside id="store-detail" class="panel">
<p
hx-altazion-data-source="slot:homepageHero"
hx-altazion-data-status="null"
class="placeholder"
>
Aucun contenu marketing chargé.
</p>
<div
hx-altazion-data-source="slot:homepageHero"
hx-altazion-data-status="not-empty"
hx-render-template="homepage-hero-template"
></div>
</aside>
<script id="homepage-hero-template" type="text/x-handlebars-template">
{{#each slot}}
<article>
<h2>{{linkItem.altData}}</h2>
</article>
{{/each}}
</script>Attributs actuellement pris en charge :
hx-altazion-data-source="cart|session|store|stores|marketing-item|marketing-items"hx-altazion-data-source="slot:nom-libre"hx-altazion-data-status="null|not-null|empty|not-empty"hx-altazion-data-slot="nom-libre"sur un élément d'action Altazion pour ranger son résultat dans un slot nomméhx-render-template="template-id"
Le contexte Handlebars exposé au template contient aujourd'hui : cart, session, store, stores, marketingItem, marketingItems, slots et slot.
Marketing: items de widgets et d'animation
Le module marketing sert bien au cas d'usage widgets de page: récupérer un item marketing isolé ou un lot d'items, puis laisser HTMX rafraîchir les fragments de rendu serveur associés.
<section
hx-ext="altazion"
hx-altazion-marketing-action="getItems"
hx-altazion-marketing-codes='["home-hero", "home-sidebar", "home-footer"]'
hx-trigger="load, altazion:marketing:refresh from:body"
hx-altazion-refresh="#widget-hero; #widget-sidebar; #widget-footer"
>
</section>
<section id="widget-hero" hx-get="/fragments/widgets/hero" hx-swap="outerHTML"></section>
<aside id="widget-sidebar" hx-get="/fragments/widgets/sidebar" hx-swap="outerHTML"></aside>
<footer id="widget-footer" hx-get="/fragments/widgets/footer" hx-swap="outerHTML"></footer>Actions supportées :
| Action | Méthode SDK | Payload attendu |
|---|---|---|
| getItem | client.marketing.getItem(code) | code |
| getItems | client.marketing.getItems(codes) | codes |
Le payload peut être fourni via hx-vals, via un formulaire, ou avec :
hx-altazion-marketing-codepour un item unique ;hx-altazion-marketing-codespour une liste JSON de codes ;- une chaîne séparée par des virgules dans
codessi c'est plus simple côté template.
Événements émis :
| Événement | Description |
|---|---|
| altazion:marketing:before | avant l'appel au SDK |
| altazion:marketing:after | après un appel réussi |
| altazion:marketing:loaded | après une lecture réussie |
| altazion:marketing:item-loaded | après getItem |
| altazion:marketing:items-loaded | après getItems |
| altazion:marketing:error | en cas d'erreur |
Stores: store locator et détail magasin
Le domaine stores est un bon candidat HTMX parce qu'il est purement orienté lecture pour l'instant: localiser une liste de magasins ou charger la fiche d'un magasin pour un widget, une modale ou une page de retrait.
<form
hx-ext="altazion"
hx-altazion-stores-action="findByPostalCode"
hx-trigger="submit"
hx-altazion-refresh="#store-results"
>
<input type="text" name="postalCode" value="75002">
<input type="hidden" name="countryCode" value="FR">
<button type="submit">Chercher un magasin</button>
</form>
<section id="store-results" hx-get="/fragments/stores/results" hx-swap="outerHTML"></section><button
hx-ext="altazion"
hx-altazion-stores-action="getStore"
hx-altazion-stores-store-guid="STORE-GUID-001"
hx-altazion-refresh="#store-detail"
>
Voir ce magasin
</button>
<aside id="store-detail" hx-get="/fragments/stores/detail" hx-swap="outerHTML"></aside>Actions supportées :
| Action | Méthode SDK | Payload attendu |
|---|---|---|
| findByLocation | client.stores.findByLocation(latitude, longitude, radiusKm?) | latitude, longitude |
| findByPostalCode | client.stores.findByPostalCode(postalCode, countryCode?) | postalCode |
| getStore | client.stores.getStore(storeGuid) | storeGuid |
Paramètres HTML pris en charge :
hx-altazion-stores-latitudehx-altazion-stores-longitudehx-altazion-stores-radius-kmhx-altazion-stores-postal-codehx-altazion-stores-country-codehx-altazion-stores-store-guid
Événements émis :
| Événement | Description |
|---|---|
| altazion:stores:before | avant l'appel au SDK |
| altazion:stores:after | après un appel réussi |
| altazion:stores:loaded | après une lecture réussie |
| altazion:stores:stores-loaded | après une recherche de magasins |
| altazion:stores:store-loaded | après getStore |
| altazion:stores:error | en cas d'erreur |
Rafraîchissement déclaratif
L'attribut hx-altazion-refresh accepte une ou plusieurs cibles CSS séparées par ;.
- si la cible porte un attribut
hx-get,hx-post,hx-put,hx-patchouhx-delete, l'extension force immédiatement la requête HTMX correspondante ; - sinon l'extension émet
altazion:refreshsur la cible, ce qui laisse la main à du rendu client ou à un autre binding HTMX.
<button
hx-ext="altazion"
hx-altazion-cart-action="applyCoupon"
hx-altazion-cart-code="WELCOME10"
hx-altazion-refresh="#cart-mini; #cart-totals"
>
Appliquer le coupon
</button>
<section id="cart-totals" hx-get="/fragments/cart-totals" hx-swap="outerHTML"></section>Événements émis
| Événement | Description |
|---|---|
| altazion:cart:before | avant l'appel au SDK |
| altazion:cart:after | après un appel réussi |
| altazion:cart:loaded | après une lecture réussie, par exemple getCart |
| altazion:cart:updated | après une action retournant un panier |
| altazion:cart:changed | après une action réussie sans payload de retour |
| altazion:cart:status | après getValidationStatus |
| altazion:cart:error | en cas d'erreur, avec detail.errorDetail normalisé |
| altazion:refresh | fallback émis sur une cible sans requête HTMX |
Le moteur HTMX générique applique désormais une sémantique commune :
loadedpour les actions de lecture ;updatedpour les mutations avec résultat métier ;changedpour les mutations qui réussissent sans retourner de payload ;erroravec un objetdetail.errorDetailstructuré :name,message,status,problem,isOffline,isConflict.
Détection offline
Lorsque la connexion est perdue, l'élément ciblé par offlineSelector (par défaut body) reçoit :
- la classe
altz-offline; - la classe
altz-terminal-offlineoualtz-terminal-online; - l'attribut
data-altazion-terminal-state="offline|online"; - les événements
altazion:connectivity:changed,altazion:offlineetaltazion:online.
Le mode borne peut aussi masquer des zones interactives et afficher un écran passif via terminalMode.interactiveSelectors et terminalMode.offlineScreenSelectors.
Exemple de bascule borne
<body>
<main id="app-main">
<!-- interface interactive -->
</main>
<aside id="offline-screen" hidden>
<!-- pub, branding, message d'attente -->
</aside>
</body>body.altz-terminal-offline {
background: #111;
color: #fff;
}
#offline-screen[hidden] {
display: none;
}L'écran offline est affiché automatiquement quand la borne passe hors ligne, et les zones interactives ciblées sont masquées.
Exemple d'écoute des événements
document.body.addEventListener('altazion:offline', () => {
console.log('Le terminal est hors ligne')
})
document.body.addEventListener('altazion:online', () => {
console.log('Le terminal est de nouveau en ligne')
})Les mutations métier continuent d'échouer immédiatement hors ligne avec OfflineError. Le mode offline borne ne réintroduit aucune synchronisation différée.
Exemple CSS minimal
Lorsque seule l'indication visuelle suffit, la classe altz-offline permet d'afficher un bandeau ou de masquer des actions :
body.altz-offline .altz-cart-actions {
opacity: 0.5;
pointer-events: none;
}Licence
Propriétaire — © Altazion SAS. Tous droits réservés.
