Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,13 +31,17 @@ Le format suit [Keep a Changelog](https://keepachangelog.com/fr/1.1.0/) et le pr

### Changed

- **`ui-datepicker` : la plage sélectionnée (`range`) se lit comme une seule zone continue** (FSHSP-166). Elle était peinte sur la cellule elle-même — fond en dégradé mi-cellule sur les deux extrémités, filets haut et bas en bordure — ce qui la faisait *encadrer* la sélection plutôt que la porter, avec deux débordements visibles : les filets couraient sur toute la largeur des cellules de début et de fin, donc au-delà de la moitié réellement remplie, et la pastille des jours de début/fin, dont la bordure transparente s'ajoutait à sa taille (`content-box`), dépassait la hauteur de la bande. La bande est désormais une couche à part, exactement de la hauteur d'une pastille et arrêtée au centre des deux extrémités : plus rien ne peut la dépasser, ni en hauteur ni sur les côtés. Les filets sont retirés, et seuls les deux bouts d'une ligne de semaine sont arrondis — sur le retrait qu'une pastille laisse dans sa colonne, pas sur le bord de la cellule, pour qu'une plage finissant sur la dernière colonne s'aligne avec la rangée du dessus au lieu de s'arrêter une demi-gouttière trop tôt. Et une date de début dont la fin n'est pas encore choisie n'affiche plus de demi-bande partant vers une extrémité qui n'existe pas : la pastille reste seule jusqu'au second clic. Deux nouveaux réglages, `--ui-datepicker-range-background` et `--ui-datepicker-range-color`, permettent de re-skinner la bande et le chiffre qu'elle porte (voir la table « Theming » de la doc).

- **La boîte de `ui-field` est désormais enveloppée dans un `.ui-field-control`** (FSHSP-157). C'est le contexte de positionnement du libellé flottant, et il est rendu dans les deux modes plutôt que conditionnellement, pour que le DOM d'un champ ne dépende pas de l'option. Aucun impact visuel ni sur les sélecteurs publics ; un consommateur qui aurait écrit du CSS sur l'enchaînement direct `.ui-field > .ui-field-box` doit passer par le descendant.
- **`ui-label` tronque son texte quand il est contraint** au lieu de déborder (`text-overflow: ellipsis` sur `.ui-label-text`, `max-width: 100%` sur la racine). Sans contrainte de largeur, le comportement est inchangé : le texte passe à la ligne comme avant.
- **`ui-datepicker` est saisissable au clavier par défaut** (`allowInput` passe de `false` à `true`, FSHSP-118). Le champ ne proposait la sélection qu'au calendrier dans la grande majorité des configurations ; pour un non-voyant, taper une date est bien plus rapide que naviguer une grille de ~30 cases au lecteur d'écran. Repasser `allowInput` à `false` restaure l'ancien comportement (grille seule). Sans effet en `multiple`/`range` (aucun parseur défini pour deux dates ou une liste — chantier séparé) ni en `timeOnly` (pas de parseur pour une heure seule) : ces modes restent lecture seule comme avant.
- **`showClear` passe de `false` à `true` par défaut, et sa priorité change face à l'icône calendrier** (FSHSP-118). Avant, la croix remplaçait systématiquement l'icône calendrier/horloge dès qu'une valeur était présente (si `showClear`) — au prix de perdre le seul déclencheur focusable capable de rouvrir le panneau. Désormais la croix ne prend le pas que si `showIcon` est à `false` : avec l'icône affichée (le défaut), elle reste cliquable pour changer la date directement, et l'effacement passe par le clavier (`allowInput`, sélectionner + supprimer le texte). Un consommateur qui utilisait déjà `showClear` avec `showIcon` à `true` verra donc la croix disparaître au profit de l'icône calendrier ; passer `showIcon` à `false` restaure son ancien comportement.

### Fixed

- **En thème sombre, les jours situés *dans* une plage `ui-datepicker` étaient illisibles** (FSHSP-166). Le chiffre prenait `--form-high-content-default`, qui bascule au quasi-blanc en sombre, alors que le fond de la bande est le même lavande clair dans les deux thèmes : blanc sur lavande, soit un contraste d'environ 1,3:1. Il prend maintenant le token de contenu apparié à ce fond (`--informative-highlightlow-content-default`, identique en clair et en sombre), ce qui remonte le contraste à ~4,7:1 (AA). Le survol et le focus d'un jour de la plage basculent sur la pastille pleine `informative.highlightHigh`, elle aussi stable d'un thème à l'autre — l'ancien fond neutre virait au gris foncé en sombre, sous un chiffre désormais foncé.

- **`ui-datepicker` en mode `timeOnly` ignorait `hourFormat` et `dateFormat`** (FSHSP-163). L'affichage formatait directement via `Intl` en `timeStyle: 'short'` sur la locale résolue, sans jamais consulter ces deux inputs : `hourFormat="24"` (le défaut) n'avait aucun effet — l'heure basculait en AM/PM dès que la locale résolue en avait un par défaut (ex. `en-US`) — et un `dateFormat` custom n'avait aucune prise sur ce mode. `hourFormat` est maintenant respecté (`hour12` forcé en conséquence, jamais laissé au défaut de la locale), et `dateFormat`, quand fourni, s'applique aussi en `timeOnly` (symétrique de son usage en `date`/`month`).
- **La saisie clavier de `ui-datepicker` (`allowInput`) pouvait mélanger les segments jour/mois/année après une suppression** (FSHSP-118). Le masque re-dérive l'affichage à chaque frappe depuis le flux brut des chiffres tapés ; une vérification de bornes (1-31, 1-12) — pensée pour rejeter un chiffre de tête invalide en cours de frappe — s'appliquait aussi après une suppression, où elle pouvait sauter un chiffre encore valide et décaler tout ce qui suit d'un cran vers le mauvais segment (le jour hérite d'un chiffre du mois, etc.) ; l'année, seule sans borne, n'était jamais concernée — d'où l'observation qu'elle seule se supprimait « proprement ». Cette vérification est maintenant désactivée quand la frappe raccourcit le texte (suppression), et rétablie dès qu'elle le rallonge. Une segmentation en place pleinement fiable quel que soit le point d'édition (clic au milieu du champ, par ex.) reste un chantier plus large, non couvert ici.
- **Effacer une date au clavier (`allowInput`) se bloquait dès que jour et mois étaient complets** (FSHSP-118). Le "/" auto-inséré entre deux segments place le curseur juste après lui, et un Retour arrière à cette position supprimait ce séparateur cosmétique plutôt qu'un chiffre — séparateur aussitôt réinséré au rendu suivant, donnant l'impression que la touche ne fait plus rien (ex. `20/08/2020` s'effaçait normalement jusqu'à `20/08/`, puis restait bloqué indéfiniment). Une suppression en fin de champ positionne maintenant le curseur juste avant ce séparateur, pas après, pour que le Retour arrière suivant retire le dernier chiffre du segment.
Expand Down
2 changes: 1 addition & 1 deletion figma/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ The components' `--ui-*` variables, in the same DTCG shape as
one too. Generated by `npm run docs:config`; `docs:config:check` fails if it is stale.

**These are not design tokens.** A token belongs to the system (`--units-sm`); a
component variable belongs to one component, and 459 of the 580 entries merely _alias_ a
component variable belongs to one component, and 461 of the 582 entries merely _alias_ a
token. Hence a **dedicated collection** (`$extensions.com.4sh.ui-kit.figmaCollection`)
rather than adding them to `semantics` or `metrics`.

Expand Down
51 changes: 49 additions & 2 deletions figma/component-vars.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@
"Mode 1"
],
"counts": {
"tokens": 580,
"alias": 459,
"tokens": 582,
"alias": 461,
"literal": 121,
"skipped": 46,
"derivedDescriptions": 197
Expand Down Expand Up @@ -3753,6 +3753,53 @@
}
}
},
"range": {
"background": {
"$value": "{semantics.informative.highlightLow.surface.hover}",
"$type": "color",
"$description": "Fond continu de la plage sélectionnée (`range`).",
"$extensions": {
"com.figma": {
"resolvedType": "COLOR",
"scopes": [
"FRAME_FILL",
"SHAPE_FILL"
],
"codeSyntax": {
"WEB": "var(--ui-datepicker-range-background)"
}
},
"com.4sh.ui-kit": {
"cssVar": "--ui-datepicker-range-background",
"component": "ui-datepicker",
"entryPoint": "@4sh/ui-kit/forms/ui-datepicker",
"descriptionSource": "scss"
}
}
},
"color": {
"$value": "{semantics.informative.highlightLow.content.default}",
"$type": "color",
"$description": "Couleur du chiffre d'un jour posé sur la plage.",
"$extensions": {
"com.figma": {
"resolvedType": "COLOR",
"scopes": [
"ALL_FILLS"
],
"codeSyntax": {
"WEB": "var(--ui-datepicker-range-color)"
}
},
"com.4sh.ui-kit": {
"cssVar": "--ui-datepicker-range-color",
"component": "ui-datepicker",
"entryPoint": "@4sh/ui-kit/forms/ui-datepicker",
"descriptionSource": "scss"
}
}
}
},
"stroke-width": {
"$value": "{metrics.stroke.default}",
"$type": "dimension",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
class="ui-datepicker-panel"
[class]="panelStyleClass()"
[class._inline]="inline()"
[class._range-complete]="rangeComplete()"
[attr.id]="panelId"
role="dialog"
[attr.aria-modal]="inline() ? null : 'true'"
Expand Down
65 changes: 53 additions & 12 deletions projects/ui-kit/forms/ui-datepicker/src/lib/ui-datepicker.scss
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ $day-radius: var(--ui-datepicker-day-radius, var(--radius-full));
$day-font-size: var(--ui-datepicker-day-font-size, var(--size-typography-text-default)); /// Taille du chiffre d'une cellule jour.
$day-opacity-other-month: var(--ui-datepicker-day-opacity-outside, 0.6); /// Opacité d'un jour appartenant au mois adjacent.
$day-opacity-disabled: var(--ui-datepicker-day-opacity-disabled, 0.45); /// Opacité d'un jour désactivé.

$range-background: var(--ui-datepicker-range-background, var(--informative-highlightlow-surface-hover)); /// Fond continu de la plage sélectionnée (`range`).
$range-color: var(--ui-datepicker-range-color, var(--informative-highlightlow-content-default)); /// Couleur du chiffre d'un jour posé sur la plage.
$nav-size: var(--ui-datepicker-nav-size, var(--size-components-default)); /// Taille des boutons de navigation mensuelle
$time-field-width: var(--ui-datepicker-time-field-width, #{utils.rem-calc(48)}); /// Largeur des champs heures / minutes / AM-PM
$focus-ring-width: var(--ui-datepicker-focus-ring-width, #{utils.$focus-ring-width}); /// Anneau de focus (jours, navigation)
Expand Down Expand Up @@ -239,7 +242,8 @@ $buttonbar-padding-top: var(--ui-datepicker-buttonbar-padding-top, var(--units-s
&-grid { display: flex; flex-direction: column; gap: $grid-gap; }

// --- Day cell ------------------------------------------------------
$range-bg: var(--informative-highlightlow-surface-hover); /// Fond continu de la plage sélectionnée (`range`).
// Gutter left by a chip inside its (wider) grid column — where the range band's caps land.
$cap-inset: calc((100% - #{$day-size}) / 2);

&-day {
position: relative;
Expand All @@ -265,6 +269,7 @@ $buttonbar-padding-top: var(--ui-datepicker-buttonbar-padding-top, var(--units-s
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
box-sizing: border-box;
width: $day-size;
height: $day-size;
border: $stroke-width solid transparent;
Expand Down Expand Up @@ -303,18 +308,54 @@ $buttonbar-padding-top: var(--ui-datepicker-buttonbar-padding-top, var(--units-s
}
}

// Range: continuous bar on the full-width cell. The top/bottom rule reuses the same
// stroke token as `._today` so the range reads even without color (WCAG: not color-only).
&._in-range,
&._range-start,
&._range-end {
border-top: $stroke-width solid var(--actions-high-stroke-default);
border-bottom: $stroke-width solid var(--actions-high-stroke-default);
// Range: the band is its own layer, sized exactly like the endpoint chip and stopped
// at its centre — it can no longer overrun the zone, in height or on the sides.
&._in-range::after,
&._range-start::after,
&._range-end::after {
content: '';
position: absolute;
top: 50%;
inset-inline: 0;
height: $day-size;
transform: translateY(-50%);
background-color: $range-background;
z-index: -2;
}
&._range-start::after { inset-inline-start: 50%; }
&._range-end::after { inset-inline-end: 50%; }
&._range-start._range-end::after { content: none; }
// A start whose end is not picked yet is not a zone — the chip stands alone until the
// second click lands. Only the panel knows: the grid alone cannot tell this cell from
// the start of a finished range whose end sits in a month it is not showing.
.ui-datepicker-panel:not(._range-complete) &._range-start::after { content: none; }

// Only the two ends of a week row are capped, and they stop where a chip's edge falls
// rather than at the cell's: a zone ending on the last column then lines up with the row
// above it, instead of stopping half a gutter short of it.
&:first-child::after {
inset-inline-start: $cap-inset;
border-start-start-radius: $day-radius;
border-end-start-radius: $day-radius;
}
&:last-child::after {
inset-inline-end: $cap-inset;
border-start-end-radius: $day-radius;
border-end-end-radius: $day-radius;
}

// Inside the zone the digit sits on the band, never on the panel surface: it takes the
// content token paired with it, which is identical in light and dark — unlike
// `--form-high-content-default`, which turns near-white in dark over a light band.
&._in-range {
color: $range-color;
&:disabled { color: $range-color; }
&:hover:not(:disabled),
&:focus-visible {
color: var(--informative-highlighthigh-content-default);
&::before { background-color: var(--informative-highlighthigh-surface-default); }
}
}
&._in-range { background-color: $range-bg; }
&._range-start { background: linear-gradient(to right, transparent 50%, #{$range-bg} 50%); }
&._range-end { background: linear-gradient(to left, transparent 50%, #{$range-bg} 50%); }
&._range-start._range-end { background: transparent; }

&:disabled {
color: var(--form-high-content-disabled);
Expand Down
10 changes: 10 additions & 0 deletions projects/ui-kit/forms/ui-datepicker/src/lib/ui-datepicker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -559,6 +559,16 @@ export class UiDatepicker extends BaseFormField<DatepickerValue> {

/** @ignore A value is currently set. */
protected readonly hasValue = computed(() => this.selectedDates().length > 0);
/**
* @ignore A `range` whose two ends are both picked. Between the first and the second click
* the start is selected but there is no zone yet: the grid must then show its chip alone,
* never a half band running off toward an end that does not exist.
*/
protected readonly rangeComplete = computed(() => {
if (this.selectionMode() !== 'range') return false;
const [start, end] = this.selectedDates();
return !!start && !!end;
});
/**
* @ignore The trigger's right action clears the value (instead of toggling the panel). Gated
* on `!showIcon()` (FSHSP-118): the calendar/clock toggle always wins the trigger's single icon
Expand Down
5 changes: 4 additions & 1 deletion projects/ui-kit/styles/component-vars.scss
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
// design token instead — it follows brand, light/dark and viewport.
//
// Generated by `npm run docs:config`, values read from the compiled CSS — do not
// edit here. 607 variables; 19 more exist whose value depends on the
// edit here. 609 variables; 19 more exist whose value depends on the
// rendered variant, listed in each component’s “Theming” section in Storybook.
// =====================================================================

Expand Down Expand Up @@ -176,6 +176,9 @@
--ui-datepicker-time-font-size: var(--size-typography-title-md);
--ui-datepicker-title-font-size: var(--size-typography-title-md);
--ui-datepicker-weekday-font-size: var(--size-typography-text-md);
// Couleurs
--ui-datepicker-range-background: var(--informative-highlightlow-surface-hover);
--ui-datepicker-range-color: var(--informative-highlightlow-content-default);
// Divers
--ui-datepicker-day-opacity-disabled: 0.45;
--ui-datepicker-day-opacity-outside: 0.6;
Expand Down
Loading