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
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,8 @@ Le format suit [Keep a Changelog](https://keepachangelog.com/fr/1.1.0/) et le pr

### Fixed

- **Le panneau de `ui-datepicker` débordait de l'écran sur un viewport étroit**. Trois causes cumulées, corrigées ensemble : le panneau était plafonné à `100vw - units-lg` mais la colonne de mois gardait une largeur fixe de 360px sans pouvoir rétrécir, donc la dernière colonne de jours et le chevron « suivant » passaient sous la bordure ; l'overlay CDK n'avait ni marge de viewport ni repoussage (`push`), donc un champ ancré n'importe où ailleurs qu'à gauche sortait le panneau hors de l'écran ; et en `numberOfMonths > 1` la rangée de mois faisait deux fois 360px, dont le second mois entièrement invisible. La colonne de mois et les grilles mois / année rétrécissent maintenant avec le panneau, la pastille d'un jour suit la largeur de sa colonne quand celle-ci passe sous `size/components/default` (la bande d'une plage la suit aussi), les mois s'empilent verticalement sous 600px avec un filet horizontal en séparateur, et l'overlay garde 12px de gouttière avec les bords du viewport. Le panneau défile désormais au-delà de `100dvh - units-3xl` plutôt que de déborder en hauteur (paysage sur téléphone), et un panneau `inline` ne dépasse plus la largeur de son conteneur.

- **Les `@media` générées depuis `responsive.json` sortaient dans l'ordre du JSON, pas par largeur croissante** — donc le desktop héritait des valeurs tablette. Les modes sont lus dans l'ordre où Figma les exporte (`modeDesktop`, `modeTablet`, `modeMobile`), et le bloc `@media (min-width: 1440px)` était émis **avant** `@media (min-width: 1024px)`. Sur un écran de 1440px les deux règles s'appliquent : à spécificité égale, c'est la dernière écrite qui gagne, et tout le desktop repartait sur l'échelle **tablette** (`--spacing-default: var(--units-md)` au lieu de `var(--units-default)`, `--grid-columns: 8` au lieu de `12`, toute la série `--size-components-*`, `--screen-width`/`--screen-height`). `scripts/tokens.build.mjs` trie maintenant les groupes `@media` par `min-width` croissante, puis par `max-width` décroissante — les deux conventions (mobile-first, desktop-first) placent ainsi le bloc le plus étroit en dernier, quel que soit l'ordre des modes dans le JSON source. Le tri comprend `min-width`/`max-width`, la syntaxe de plage (`width >= 900px`, `900px <= width`) et les unités `px`/`rem`/`em` ; une media query sans contrainte de largeur garde son ordre d'apparition.

- **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é.
Expand Down
2 changes: 1 addition & 1 deletion figma/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ rather than adding them to `semantics` or `metrics`.
variables are `remote`, hence read-only (see `CLAUDE.md`).
- Keep the aliases as aliases: that is what preserves brand, light/dark and responsive.
- Literal values are in `px` (converted from `rem`, base 16), like the token files.
- `$extensions.com.4sh.ui-kit.skipped` lists 52 entries with nothing to create: `calc()`
- `$extensions.com.4sh.ui-kit.skipped` lists 53 entries with nothing to create: `calc()`
expressions, multi-value CSS shorthands, relative units (`em`, `ch`, `%`), duration /
easing / cursor / z-index, and internal plumbing.

Expand Down
7 changes: 6 additions & 1 deletion figma/component-vars.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
"tokens": 646,
"alias": 521,
"literal": 125,
"skipped": 52,
"skipped": 53,
"derivedDescriptions": 206
},
"aliasTargets": [
Expand Down Expand Up @@ -54,6 +54,11 @@
"component": "ui-config",
"reason": "sans équivalent variable Figma (durée, easing, curseur, plan d’empilement, ratio)."
},
{
"cssVar": "--ui-datepicker-panel-max-height",
"component": "ui-datepicker",
"reason": "expression calc() : une variable Figma ne porte pas de calcul."
},
{
"cssVar": "--ui-datepicker-panel-max-width",
"component": "ui-datepicker",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -395,6 +395,8 @@
[cdkConnectedOverlayOrigin]="overlayOrigin()!"
[cdkConnectedOverlayOpen]="panelOpen()"
[cdkConnectedOverlayPositions]="overlayPositions()"
[cdkConnectedOverlayViewportMargin]="overlayViewportMargin"
[cdkConnectedOverlayPush]="true"
[cdkConnectedOverlayHasBackdrop]="true"
cdkConnectedOverlayBackdropClass="cdk-overlay-transparent-backdrop"
(backdropClick)="close(false)"
Expand Down
42 changes: 35 additions & 7 deletions projects/ui-kit/forms/ui-datepicker/src/lib/ui-datepicker.scss
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ $panel-padding: var(--ui-datepicker-panel-padding, #{utils.$overlay-panel-paddin
$panel-gap: var(--ui-datepicker-panel-gap, var(--units-sm)); /// Espace entre sections (header / grille / heure)
$panel-shadow: var(--ui-datepicker-panel-shadow, #{utils.$overlay-panel-shadow}); /// Ombre du panneau en overlay (retirée en inline)
$panel-max-width: var(--ui-datepicker-panel-max-width, calc(100vw - var(--units-lg))); /// Largeur maximale du panneau, gouttière laissée au viewport.
$panel-max-height: var(--ui-datepicker-panel-max-height, calc(100dvh - var(--units-3xl))); /// Hauteur maximale du panneau en overlay, au-delà de laquelle il défile.

$day-size: var(--ui-datepicker-day-size, var(--size-components-default)); /// Diamètre d'une cellule jour
$day-radius: var(--ui-datepicker-day-radius, var(--radius-full)); /// Rayon des cellules jour (cercle)
Expand All @@ -36,6 +37,8 @@ $title-padding-y: var(--ui-datepicker-title-padding-y, var(--units-2xs));
$title-padding-x: var(--ui-datepicker-title-padding-x, var(--units-sm)); /// Inset horizontal du titre cliquable.
$title-radius: var(--ui-datepicker-title-radius, var(--radius-sm)); /// Rayon du titre cliquable.

$months-stack-below: 37.5rem; // = breakpoint tabletPortrait : sous ce seuil deux mois ne tiennent plus côte à côte.

$months-gap: var(--ui-datepicker-months-gap, var(--units-lg)); /// Espacement entre les mois affichés côte à côte.
$month-gap: var(--ui-datepicker-month-gap, var(--units-sm)); /// Espacement entre les sections d'une colonne de mois.
$month-divider-padding-left: var(--ui-datepicker-month-divider-padding-left, var(--units-lg)); /// Retrait après le filet séparant deux mois.
Expand Down Expand Up @@ -96,7 +99,15 @@ $buttonbar-padding-top: var(--ui-datepicker-buttonbar-padding-top, var(--units-s
box-shadow: $panel-shadow;
font-family: var(--fontfamily-base);

&._inline { box-shadow: none; }
&:not(._inline) {
max-height: $panel-max-height;
overflow: auto;
overscroll-behavior: contain;
}
&._inline {
box-shadow: none;
max-width: min(100%, #{$panel-max-width});
}
}

// --- Header (month navigation) -------------------------------------
Expand Down Expand Up @@ -134,6 +145,7 @@ $buttonbar-padding-top: var(--ui-datepicker-buttonbar-padding-top, var(--units-s

&-title {
flex: 1 1 auto;
min-width: 0;
text-align: center;
font-size: $title-font-size;
font-weight: var(--weight-bold);
Expand Down Expand Up @@ -164,15 +176,27 @@ $buttonbar-padding-top: var(--ui-datepicker-buttonbar-padding-top, var(--units-s
display: flex;
flex-direction: column;
gap: $month-gap;
flex: 0 0 auto;
flex: 0 1 auto;
width: $panel-width;
min-width: 0;
max-width: 100%;

&:not(:first-child) {
border-left: $stroke-width solid var(--form-high-stroke-default);
padding-left: $month-divider-padding-left;
}
}

@media (width < #{$months-stack-below}) {
&-months { flex-direction: column; }
&-month:not(:first-child) {
border-left: none;
padding-left: 0;
border-top: $stroke-width solid var(--form-high-stroke-default);
padding-top: $month-divider-padding-left;
}
}

// Per-panel header: [prev|spacer] title [next|spacer] — arrows at the outer
// edges, title centred over its column and aligned with the chevrons.
&-subheader {
Expand All @@ -187,6 +211,7 @@ $buttonbar-padding-top: var(--ui-datepicker-buttonbar-padding-top, var(--units-s
display: grid;
gap: $picker-gap;
width: $panel-width;
max-width: 100%;

// The `role="row"` wrappers exist for ARIA only: the cells stay direct grid items.
&-row { display: contents; }
Expand Down Expand Up @@ -247,7 +272,7 @@ $buttonbar-padding-top: var(--ui-datepicker-buttonbar-padding-top, var(--units-s

// --- Day cell ------------------------------------------------------
// Gutter left by a chip inside its (wider) grid column — where the range band's caps land.
$cap-inset: calc((100% - #{$day-size}) / 2);
$cap-inset: calc(max(0px, (100% - #{$day-size}) / 2));

&-day {
position: relative;
Expand All @@ -257,7 +282,8 @@ $buttonbar-padding-top: var(--ui-datepicker-buttonbar-padding-top, var(--units-s
justify-content: center;
box-sizing: border-box;
width: 100%;
height: $day-size;
aspect-ratio: 1;
max-height: $day-size;
padding: 0;
border: none;
background: transparent;
Expand All @@ -274,8 +300,8 @@ $buttonbar-padding-top: var(--ui-datepicker-buttonbar-padding-top, var(--units-s
left: 50%;
transform: translate(-50%, -50%);
box-sizing: border-box;
width: $day-size;
height: $day-size;
width: min(100%, #{$day-size});
height: 100%;
border: $stroke-width solid transparent;
border-radius: $day-radius;
z-index: -1;
Expand Down Expand Up @@ -321,7 +347,7 @@ $buttonbar-padding-top: var(--ui-datepicker-buttonbar-padding-top, var(--units-s
position: absolute;
top: 50%;
inset-inline: 0;
height: $day-size;
height: 100%;
transform: translateY(-50%);
background-color: $range-background;
z-index: -2;
Expand Down Expand Up @@ -373,6 +399,7 @@ $buttonbar-padding-top: var(--ui-datepicker-buttonbar-padding-top, var(--units-s
display: flex;
align-items: center;
justify-content: center;
flex-wrap: wrap;
gap: $time-gap;
padding-top: $time-padding-top;
border-top: $stroke-width solid var(--form-high-stroke-default);
Expand Down Expand Up @@ -449,6 +476,7 @@ $buttonbar-padding-top: var(--ui-datepicker-buttonbar-padding-top, var(--units-s
display: flex;
align-items: center;
justify-content: space-between;
flex-wrap: wrap;
gap: $buttonbar-gap;
padding-top: $buttonbar-padding-top;
border-top: $stroke-width solid var(--form-high-stroke-default);
Expand Down
3 changes: 3 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 @@ -375,6 +375,9 @@ export class UiDatepicker extends BaseFormField<DatepickerValue> {

/** @ignore Below the trigger, flipping above when `autoFlip` and space is lacking. */
protected readonly overlayPositions = computed(() => dropdownOverlayPositions(this.autoFlip()));
/** @ignore Gutter kept with the viewport edges, matching the one `$panel-max-width`
* reserves; with `cdkConnectedOverlayPush` it keeps the panel fully on screen. */
protected readonly overlayViewportMargin = 12;

/** @ignore Bound (stable) callbacks exposed to the `#buttonbar` template. */
protected readonly todayCallback = (event?: Event): void => {
Expand Down
8 changes: 7 additions & 1 deletion projects/ui-kit/forms/ui-datepicker/ui-datepicker.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,8 @@ un mois / une année.

`numberOfMonths` affiche N panneaux mois côte à côte, séparés par un filet, avec les titres de
mois alignés en face des flèches ← → (aux extrémités). Le changement de vue à tiroir reste
réservé au mode mono-mois.
réservé au mode mono-mois. Sous 600px de viewport, les mois s'empilent verticalement et le
filet passe à l'horizontale.

<Canvas of={DatepickerStories.TwoMonths} />

Expand All @@ -82,6 +83,11 @@ réservé au mode mono-mois.
Le panneau s'ouvre sous le champ et **se retourne automatiquement au-dessus** quand la place
manque en bas (CDK Overlay). `autoFlip="false"` verrouille l'ouverture vers le bas.

Sur un viewport étroit, le panneau reste entièrement à l'écran : il garde 12px de gouttière
avec les bords (l'overlay est repoussé quand l'ancrage seul n'y suffit pas), sa largeur se
plafonne à celle du viewport, et la grille des jours rétrécit avec lui plutôt que de déborder.
Au-delà de `--ui-datepicker-panel-max-height`, il défile.

<Canvas of={DatepickerStories.SmartPosition} />

## Date & heure
Expand Down
3 changes: 2 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. 678 variables; 20 more exist whose value depends on the
// edit here. 679 variables; 20 more exist whose value depends on the
// rendered variant, listed in each component’s “Theming” section in Storybook.
// =====================================================================

Expand Down Expand Up @@ -146,6 +146,7 @@
--ui-datepicker-cell-min-height: var(--size-components-sm);
--ui-datepicker-day-size: var(--size-components-default);
--ui-datepicker-nav-size: var(--size-components-default);
--ui-datepicker-panel-max-height: calc(100dvh - var(--units-3xl));
--ui-datepicker-panel-max-width: calc(100vw - var(--units-lg));
--ui-datepicker-panel-width: 22.5rem;
--ui-datepicker-time-btn-height: var(--size-components-2xs);
Expand Down