fold-ng 0.28.0 → 0.28.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +18 -1
- package/fesm2022/fold-ng.mjs +28 -10
- package/fesm2022/fold-ng.mjs.map +1 -1
- package/package.json +1 -1
- package/tmp-esm2022/tsconfig.lib.tsbuildinfo +1 -1
- package/types/fold-ng.d.ts +15 -4
- package/types/fold-ng.d.ts.map +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -8,6 +8,22 @@ All notable changes to **fold-ng** are documented here. The format follows
|
|
|
8
8
|
|
|
9
9
|
_Nothing yet._
|
|
10
10
|
|
|
11
|
+
## [0.28.1] - 2026-10-06
|
|
12
|
+
|
|
13
|
+
### Fixed
|
|
14
|
+
|
|
15
|
+
- **`fold-nav-launcher` défile au niveau 1.** La grille portait les tuiles dans
|
|
16
|
+
un corps `overflow: hidden` : au-delà d'un écran, les dernières tuiles étaient
|
|
17
|
+
coupées et inatteignables (iPad). C'est la grille qui défile désormais
|
|
18
|
+
(`overflow-y: auto; overscroll-behavior: contain`) — pas le corps, sans quoi
|
|
19
|
+
la feuille du niveau 2 s'ouvrirait décalée — et le pied reste en place.
|
|
20
|
+
- **`fold-nav-launcher` : des tuiles de taille bornée sur tablette.** En
|
|
21
|
+
`columns="auto"` (défaut) la largeur décide : `auto-fill` avec un minimum de
|
|
22
|
+
96 px (144 px pour ≤4 éléments). Téléphone inchangé (3 colonnes, ou 2), iPad
|
|
23
|
+
7 colonnes de ~96 px au lieu de 2 de ~370 px. La grille est plafonnée à
|
|
24
|
+
`--fold-nav-launcher-max-width` (720 px), centrée — y compris avec un
|
|
25
|
+
`columns` numérique, qui reste prioritaire.
|
|
26
|
+
|
|
11
27
|
## [0.28.0] - 2026-10-05
|
|
12
28
|
|
|
13
29
|
### Added
|
|
@@ -2622,7 +2638,8 @@ design-token stylesheet.
|
|
|
2622
2638
|
`currentColor`; `prefers-reduced-motion` + `forced-colors` are respected;
|
|
2623
2639
|
strings localise via inputs / providers (`provideFoldPanelLabels`).
|
|
2624
2640
|
|
|
2625
|
-
[unreleased]: https://github.com/hugoheynard/fold-ng/compare/v0.28.
|
|
2641
|
+
[unreleased]: https://github.com/hugoheynard/fold-ng/compare/v0.28.1...HEAD
|
|
2642
|
+
[0.28.1]: https://github.com/hugoheynard/fold-ng/releases/tag/v0.28.1
|
|
2626
2643
|
[0.28.0]: https://github.com/hugoheynard/fold-ng/releases/tag/v0.28.0
|
|
2627
2644
|
[0.27.2]: https://github.com/hugoheynard/fold-ng/releases/tag/v0.27.2
|
|
2628
2645
|
[0.27.1]: https://github.com/hugoheynard/fold-ng/releases/tag/v0.27.1
|
package/fesm2022/fold-ng.mjs
CHANGED
|
@@ -4930,6 +4930,8 @@ const DRILL_MS = 260;
|
|
|
4930
4930
|
const BACK_MS = 200;
|
|
4931
4931
|
/** When the entrance cascade is over and `:active` gets the transform back (ms). */
|
|
4932
4932
|
const SETTLE_MS = 760;
|
|
4933
|
+
/** Up to this many level-1 items, `"auto"` lays out large tiles. */
|
|
4934
|
+
const FEW_ITEMS = 4;
|
|
4933
4935
|
/** Horizontal travel that counts as a "swipe right" (px). */
|
|
4934
4936
|
const SWIPE_PX = 56;
|
|
4935
4937
|
/**
|
|
@@ -4999,8 +5001,14 @@ class FoldNavLauncherComponent {
|
|
|
4999
5001
|
*/
|
|
5000
5002
|
glass = input(false, { ...(ngDevMode ? { debugName: "glass" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
5001
5003
|
/**
|
|
5002
|
-
* Grid column count. `"auto"` (default)
|
|
5003
|
-
*
|
|
5004
|
+
* Grid column count. `"auto"` (default) lets the WIDTH decide: tiles fill the
|
|
5005
|
+
* row at a minimum size, so a phone keeps 2 (≤4 items) or 3 columns while a
|
|
5006
|
+
* tablet gets more, smaller columns instead of four 370 px squares. The
|
|
5007
|
+
* minimum depends on the item count — few items, large tiles.
|
|
5008
|
+
*
|
|
5009
|
+
* A number pins the count. Either way the grid never grows past
|
|
5010
|
+
* `--fold-nav-launcher-max-width` (default `720px`), centred, so a pinned
|
|
5011
|
+
* count cannot blow tiles up on a wide screen either.
|
|
5004
5012
|
*/
|
|
5005
5013
|
columns = input("auto", /* @ts-ignore */
|
|
5006
5014
|
...(ngDevMode ? [{ debugName: "columns" }] : /* istanbul ignore next */ []));
|
|
@@ -5032,15 +5040,25 @@ class FoldNavLauncherComponent {
|
|
|
5032
5040
|
/** The group whose tile is the anchor: the open one, or the one being opened. */
|
|
5033
5041
|
anchor = computed(() => this.openGroup() ?? this.pending(), /* @ts-ignore */
|
|
5034
5042
|
...(ngDevMode ? [{ debugName: "anchor" }] : /* istanbul ignore next */ []));
|
|
5035
|
-
/** The
|
|
5036
|
-
|
|
5043
|
+
/** The pinned column count, or `null` when the width decides (`"auto"`). */
|
|
5044
|
+
fixedCols = computed(() => {
|
|
5037
5045
|
const cols = this.columns();
|
|
5038
|
-
|
|
5039
|
-
|
|
5046
|
+
return cols === "auto" ? null : cols;
|
|
5047
|
+
}, /* @ts-ignore */
|
|
5048
|
+
...(ngDevMode ? [{ debugName: "fixedCols" }] : /* istanbul ignore next */ []));
|
|
5049
|
+
/**
|
|
5050
|
+
* Tile density in `"auto"` mode — picks the tile's minimum width in CSS:
|
|
5051
|
+
* `few` (≤4 items) → large tiles, `many` → compact ones. `null` when pinned.
|
|
5052
|
+
*/
|
|
5053
|
+
density = computed(() => {
|
|
5054
|
+
if (this.fixedCols() !== null) {
|
|
5055
|
+
return null;
|
|
5040
5056
|
}
|
|
5041
|
-
return this.tiles().length + this.groups().length <=
|
|
5057
|
+
return this.tiles().length + this.groups().length <= FEW_ITEMS
|
|
5058
|
+
? "few"
|
|
5059
|
+
: "many";
|
|
5042
5060
|
}, /* @ts-ignore */
|
|
5043
|
-
...(ngDevMode ? [{ debugName: "
|
|
5061
|
+
...(ngDevMode ? [{ debugName: "density" }] : /* istanbul ignore next */ []));
|
|
5044
5062
|
/** The sheet's subtitle — derived from the group's own children, never passed. */
|
|
5045
5063
|
entryCountText = computed(() => {
|
|
5046
5064
|
const group = this.openGroup();
|
|
@@ -5223,13 +5241,13 @@ class FoldNavLauncherComponent {
|
|
|
5223
5241
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: FoldNavLauncherComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
5224
5242
|
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.8", type: FoldNavLauncherComponent, isStandalone: true, selector: "fold-nav-launcher", inputs: { open: { classPropertyName: "open", publicName: "open", isSignal: true, isRequired: false, transformFunction: null }, label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, eyebrow: { classPropertyName: "eyebrow", publicName: "eyebrow", isSignal: true, isRequired: false, transformFunction: null }, heading: { classPropertyName: "heading", publicName: "heading", isSignal: true, isRequired: false, transformFunction: null }, closeLabel: { classPropertyName: "closeLabel", publicName: "closeLabel", isSignal: true, isRequired: false, transformFunction: null }, backLabel: { classPropertyName: "backLabel", publicName: "backLabel", isSignal: true, isRequired: false, transformFunction: null }, glass: { classPropertyName: "glass", publicName: "glass", isSignal: true, isRequired: false, transformFunction: null }, columns: { classPropertyName: "columns", publicName: "columns", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { open: "openChange" }, host: { listeners: { "document:keydown.escape": "onEscape()" } }, providers: [
|
|
5225
5243
|
{ provide: FOLD_NAV_LAUNCHER, useExisting: FoldNavLauncherComponent },
|
|
5226
|
-
], queries: [{ propertyName: "tiles", predicate: FoldNavTileComponent, isSignal: true }, { propertyName: "groups", predicate: FoldNavGroupComponent, isSignal: true }], ngImport: i0, template: "@if (open()) {\n <div class=\"nl-scrim\" aria-hidden=\"true\"></div>\n <!-- Le gestionnaire de renvoi vit sur le DIALOGUE, pas sur le voile : le\n dialogue est `position: fixed; inset: 0` par-dessus, donc un clic\n n'atteignait jamais le voile. Le clavier sort par `Escape`. -->\n <!-- Une SURFACE DE CHROME, et pas seulement un fond sombre : sans cette\n d\u00E9claration le lanceur peignait le sol du rail tout en lisant l'encre de\n la PAGE \u2014 texte sombre sur sombre, et des tuiles claires au milieu d'un\n panneau noir, sous tout th\u00E8me mixte (navi). Peindre le sol d'une famille\n sans en rejoindre la polarit\u00E9, c'est en revendiquer la moiti\u00E9. -->\n <div\n class=\"nl-dialog\"\n foldSurface=\"chrome\"\n role=\"dialog\"\n aria-modal=\"true\"\n [attr.aria-label]=\"label()\"\n [attr.data-level]=\"level()\"\n [attr.data-glass]=\"glass() ? '' : null\"\n [foldFocusTrap]=\"open()\"\n tabindex=\"-1\"\n (click)=\"onSurfaceClick($event)\"\n >\n <!-- Le voile de l'option verre. Sans lui, le flou n'a rien \u00E0 flouter et on\n n'obtient que des panneaux plus sombres. -->\n <div class=\"nl-veil\" aria-hidden=\"true\"></div>\n\n <header class=\"nl-head\">\n @if (anchor(); as group) {\n <button\n type=\"button\"\n class=\"nl-back\"\n [attr.aria-label]=\"backLabel()\"\n (click)=\"back()\"\n >\n <fold-icon name=\"chevron-left\" size=\"sm\" />\n </button>\n <!-- L'ANCRE. La tuile touch\u00E9e ne dispara\u00EEt pas : elle remonte ici, \u00E0\n sa taille de pastille. Cliquable, parce que l'\u00E9l\u00E9ment qui a servi\n de porte doit aussi servir de sortie. -->\n <button\n type=\"button\"\n class=\"nl-anchor\"\n [attr.aria-label]=\"backLabel()\"\n (click)=\"back()\"\n >\n @if (group.icon(); as name) {\n <fold-icon [name]=\"name\" size=\"sm\" />\n } @else {\n <span aria-hidden=\"true\">{{ group.label().charAt(0) }}</span>\n }\n </button>\n <span class=\"nl-titles\">\n <span class=\"nl-title\">{{ group.label() }}</span>\n <span class=\"nl-sub\">{{ entryCountText() }}</span>\n </span>\n } @else {\n <span class=\"nl-titles\">\n @if (eyebrow(); as e) {\n <span class=\"nl-eyebrow\">{{ e }}</span>\n }\n @if (heading(); as h) {\n <span class=\"nl-heading\">{{ h }}</span>\n }\n </span>\n }\n\n <button\n type=\"button\"\n class=\"nl-close\"\n [attr.aria-label]=\"closeLabel()\"\n (click)=\"close()\"\n >\n <fold-icon name=\"close\" size=\"md\" />\n </button>\n </header>\n\n <div\n class=\"nl-body\"\n (pointerdown)=\"onPointerDown($event)\"\n (pointerup)=\"onPointerUp($event)\"\n >\n <!-- La grille porte les DEUX niveaux : un groupe ouvert sort de la\n grille et couvre ce corps, sans que rien ne bouge dans le DOM. -->\n <div
|
|
5244
|
+
], queries: [{ propertyName: "tiles", predicate: FoldNavTileComponent, isSignal: true }, { propertyName: "groups", predicate: FoldNavGroupComponent, isSignal: true }], ngImport: i0, template: "@if (open()) {\n <div class=\"nl-scrim\" aria-hidden=\"true\"></div>\n <!-- Le gestionnaire de renvoi vit sur le DIALOGUE, pas sur le voile : le\n dialogue est `position: fixed; inset: 0` par-dessus, donc un clic\n n'atteignait jamais le voile. Le clavier sort par `Escape`. -->\n <!-- Une SURFACE DE CHROME, et pas seulement un fond sombre : sans cette\n d\u00E9claration le lanceur peignait le sol du rail tout en lisant l'encre de\n la PAGE \u2014 texte sombre sur sombre, et des tuiles claires au milieu d'un\n panneau noir, sous tout th\u00E8me mixte (navi). Peindre le sol d'une famille\n sans en rejoindre la polarit\u00E9, c'est en revendiquer la moiti\u00E9. -->\n <div\n class=\"nl-dialog\"\n foldSurface=\"chrome\"\n role=\"dialog\"\n aria-modal=\"true\"\n [attr.aria-label]=\"label()\"\n [attr.data-level]=\"level()\"\n [attr.data-glass]=\"glass() ? '' : null\"\n [foldFocusTrap]=\"open()\"\n tabindex=\"-1\"\n (click)=\"onSurfaceClick($event)\"\n >\n <!-- Le voile de l'option verre. Sans lui, le flou n'a rien \u00E0 flouter et on\n n'obtient que des panneaux plus sombres. -->\n <div class=\"nl-veil\" aria-hidden=\"true\"></div>\n\n <header class=\"nl-head\">\n @if (anchor(); as group) {\n <button\n type=\"button\"\n class=\"nl-back\"\n [attr.aria-label]=\"backLabel()\"\n (click)=\"back()\"\n >\n <fold-icon name=\"chevron-left\" size=\"sm\" />\n </button>\n <!-- L'ANCRE. La tuile touch\u00E9e ne dispara\u00EEt pas : elle remonte ici, \u00E0\n sa taille de pastille. Cliquable, parce que l'\u00E9l\u00E9ment qui a servi\n de porte doit aussi servir de sortie. -->\n <button\n type=\"button\"\n class=\"nl-anchor\"\n [attr.aria-label]=\"backLabel()\"\n (click)=\"back()\"\n >\n @if (group.icon(); as name) {\n <fold-icon [name]=\"name\" size=\"sm\" />\n } @else {\n <span aria-hidden=\"true\">{{ group.label().charAt(0) }}</span>\n }\n </button>\n <span class=\"nl-titles\">\n <span class=\"nl-title\">{{ group.label() }}</span>\n <span class=\"nl-sub\">{{ entryCountText() }}</span>\n </span>\n } @else {\n <span class=\"nl-titles\">\n @if (eyebrow(); as e) {\n <span class=\"nl-eyebrow\">{{ e }}</span>\n }\n @if (heading(); as h) {\n <span class=\"nl-heading\">{{ h }}</span>\n }\n </span>\n }\n\n <button\n type=\"button\"\n class=\"nl-close\"\n [attr.aria-label]=\"closeLabel()\"\n (click)=\"close()\"\n >\n <fold-icon name=\"close\" size=\"md\" />\n </button>\n </header>\n\n <div\n class=\"nl-body\"\n (pointerdown)=\"onPointerDown($event)\"\n (pointerup)=\"onPointerUp($event)\"\n >\n <!-- La grille porte les DEUX niveaux : un groupe ouvert sort de la\n grille et couvre ce corps, sans que rien ne bouge dans le DOM. -->\n <div\n class=\"nl-grid\"\n [style.--nav-cols]=\"fixedCols()\"\n [attr.data-cols]=\"fixedCols() === null ? null : ''\"\n [attr.data-density]=\"density()\"\n >\n <ng-content />\n </div>\n </div>\n\n <div class=\"nl-foot\">\n <ng-content select=\"[footer]\" />\n </div>\n\n <!-- Les deux crans de profondeur. `aria-hidden` : la r\u00E9gion vive dit la\n m\u00EAme chose en mots, et un lecteur d'\u00E9cran n'a que faire de deux\n pastilles. -->\n <div class=\"nl-dots\" aria-hidden=\"true\">\n <span class=\"nl-dot\" [class.is-on]=\"level() === 1\"></span>\n <span class=\"nl-dot\" [class.is-on]=\"level() === 2\"></span>\n </div>\n\n <!-- La descente est MUETTE sans ceci : l'animation ne compense rien. -->\n <p class=\"nl-live\" aria-live=\"polite\">{{ announcement() }}</p>\n </div>\n}\n", styles: ["@charset \"UTF-8\";:host{display:contents}.nl-scrim{position:fixed;inset:0;z-index:200;background:var(--fold-color-scrim);backdrop-filter:blur(var(--fold-blur-glass));-webkit-backdrop-filter:blur(var(--fold-blur-glass));animation:fold-nl-fade .2s ease}.nl-dialog{--fold-nl-gutter: var(--fold-space-xl);--fold-nav-launcher-max-width: 720px;--fold-nl-ease-in: cubic-bezier(.22, 1, .36, 1);--fold-nl-tile-bg: var(--fold-color-surface-raised);--fold-nl-accent-bg: var(--fold-color-primary-surface);--fold-nl-sheet-bg: var(--fold-color-bg-rail-secondary);--fold-nl-row-bg: var(--fold-color-surface-card);--fold-nl-backdrop: none;--fold-nl-sheet-backdrop: none;position:fixed;inset:0;z-index:201;display:flex;flex-direction:column;padding:var(--fold-space-3xl) var(--fold-nl-gutter) var(--fold-space-xl);background:var(--fold-color-bg-rail-primary);color:var(--fold-color-text)}.nl-head{flex:none;display:flex;align-items:center;justify-content:space-between;gap:var(--fold-space-md);min-height:44px;margin-bottom:var(--fold-space-xl)}.nl-titles{display:flex;min-width:0;flex-direction:column}.nl-eyebrow{font-family:var(--fold-font-mono);font-size:var(--fold-text-xs);letter-spacing:var(--fold-tracking-caps);text-transform:uppercase;color:var(--fold-color-text-muted)}.nl-heading{font-size:var(--fold-text-xl);font-weight:var(--fold-weight-bold);letter-spacing:var(--fold-tracking-tighter);color:var(--fold-color-text)}.nl-title{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:var(--fold-text-lg);font-weight:var(--fold-weight-bold);letter-spacing:var(--fold-tracking-tighter);color:var(--fold-color-text);animation:fold-nl-crumb .32s var(--fold-nl-ease-in) both;animation-delay:40ms}.nl-sub{font-size:var(--fold-text-xs);color:var(--fold-color-text-muted);animation:fold-nl-crumb .32s var(--fold-nl-ease-in) both;animation-delay:40ms}.nl-back,.nl-close,.nl-anchor{flex:none;display:grid;place-items:center;width:30px;height:30px;padding:0;border-radius:var(--fold-radius-sm);background:var(--fold-color-surface-raised);border:1px solid var(--fold-color-border);color:var(--fold-color-text-secondary);cursor:pointer;transition:background .14s ease,color .14s ease}.nl-back:hover,.nl-close:hover{background:var(--fold-color-surface-hover);color:var(--fold-color-text)}.nl-anchor{background:var(--fold-color-primary);border-color:transparent;color:var(--fold-color-on-primary);animation:fold-nl-anchor-in .3s var(--fold-nl-ease-in) both}.nl-close{margin-inline-start:auto}.nl-body{position:relative;display:flex;flex-direction:column;flex:1 1 auto;min-height:0;margin-inline:calc(-1 * var(--fold-nl-gutter));overflow:hidden}.nl-grid{--nav-tile-min: 96px;flex:1 1 auto;min-height:0;overflow-y:auto;overscroll-behavior:contain;-webkit-overflow-scrolling:touch;padding-inline:max(var(--fold-nl-gutter),(100% - var(--fold-nav-launcher-max-width)) / 2);padding-block-end:var(--fold-space-sm);display:grid;grid-template-columns:repeat(auto-fill,minmax(min(100%,var(--nav-tile-min)),1fr));grid-auto-rows:max-content;align-content:start;gap:var(--fold-space-sm)}.nl-grid[data-density=few]{--nav-tile-min: 144px}.nl-grid[data-cols]{grid-template-columns:repeat(var(--nav-cols),minmax(0,1fr))}@media(max-width:360px){.nl-grid,.nl-grid[data-cols]{grid-template-columns:repeat(2,minmax(0,1fr))}}.nl-dialog[data-level=\"2\"] .nl-grid{visibility:hidden}.nl-foot:not(:empty){flex:none;margin-top:auto;padding-top:var(--fold-space-md);border-top:1px solid var(--fold-color-border-subtle)}.nl-dots{flex:none;display:flex;align-items:center;justify-content:center;gap:var(--fold-space-xs);margin-top:var(--fold-space-md)}.nl-dot{width:22px;height:3px;border-radius:var(--fold-radius-pill);background:var(--fold-color-border);transition:background .28s ease}.nl-dot.is-on{background:var(--fold-color-primary)}.nl-live{position:absolute;width:1px;height:1px;margin:0;overflow:hidden;clip-path:inset(50%);white-space:nowrap}.nl-veil{position:absolute;inset:0;opacity:0;pointer-events:none;transition:opacity .3s ease;background:radial-gradient(120% 70% at 12% 4%,color-mix(in srgb,var(--fold-color-primary) 34%,transparent),transparent 58%),radial-gradient(90% 60% at 88% 22%,color-mix(in srgb,var(--fold-color-primary-strong) 30%,transparent),transparent 60%),radial-gradient(80% 55% at 70% 96%,color-mix(in srgb,var(--fold-color-warning) 16%,transparent),transparent 62%)}.nl-dialog[data-glass]{--fold-nl-tile-bg: color-mix( in srgb, var(--fold-color-bg-rail-secondary) 52%, transparent );--fold-nl-accent-bg: color-mix( in srgb, var(--fold-color-primary) 14%, transparent );--fold-nl-sheet-bg: color-mix( in srgb, var(--fold-color-bg-rail-secondary) 60%, transparent );--fold-nl-row-bg: color-mix( in srgb, var(--fold-color-surface-card) 50%, transparent );--fold-nl-backdrop: blur(20px) saturate(150%);--fold-nl-sheet-backdrop: blur(26px) saturate(150%);background:var(--fold-color-bg-header)}.nl-dialog[data-glass] .nl-veil{opacity:1}@keyframes fold-nl-fade{0%{opacity:0}to{opacity:1}}@keyframes fold-nl-crumb{0%{opacity:0;transform:translate(-12px)}to{opacity:1;transform:none}}@keyframes fold-nl-anchor-in{0%{opacity:0;transform:scale(.6) rotate(-8deg)}to{opacity:1;transform:none}}@media(prefers-reduced-motion:reduce){.nl-scrim,.nl-title,.nl-sub,.nl-anchor{animation:none}}\n"], dependencies: [{ kind: "directive", type: FocusTrapDirective, selector: "[foldFocusTrap]", inputs: ["foldFocusTrap"] }, { kind: "component", type: FoldIconComponent, selector: "fold-icon", inputs: ["name", "size", "tone", "title"] }, { kind: "directive", type: FoldSurfaceDirective, selector: "[foldSurface]", inputs: ["foldSurface"] }] });
|
|
5227
5245
|
}
|
|
5228
5246
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: FoldNavLauncherComponent, decorators: [{
|
|
5229
5247
|
type: Component,
|
|
5230
5248
|
args: [{ selector: "fold-nav-launcher", standalone: true, imports: [FocusTrapDirective, FoldIconComponent, FoldSurfaceDirective], providers: [
|
|
5231
5249
|
{ provide: FOLD_NAV_LAUNCHER, useExisting: FoldNavLauncherComponent },
|
|
5232
|
-
], template: "@if (open()) {\n <div class=\"nl-scrim\" aria-hidden=\"true\"></div>\n <!-- Le gestionnaire de renvoi vit sur le DIALOGUE, pas sur le voile : le\n dialogue est `position: fixed; inset: 0` par-dessus, donc un clic\n n'atteignait jamais le voile. Le clavier sort par `Escape`. -->\n <!-- Une SURFACE DE CHROME, et pas seulement un fond sombre : sans cette\n d\u00E9claration le lanceur peignait le sol du rail tout en lisant l'encre de\n la PAGE \u2014 texte sombre sur sombre, et des tuiles claires au milieu d'un\n panneau noir, sous tout th\u00E8me mixte (navi). Peindre le sol d'une famille\n sans en rejoindre la polarit\u00E9, c'est en revendiquer la moiti\u00E9. -->\n <div\n class=\"nl-dialog\"\n foldSurface=\"chrome\"\n role=\"dialog\"\n aria-modal=\"true\"\n [attr.aria-label]=\"label()\"\n [attr.data-level]=\"level()\"\n [attr.data-glass]=\"glass() ? '' : null\"\n [foldFocusTrap]=\"open()\"\n tabindex=\"-1\"\n (click)=\"onSurfaceClick($event)\"\n >\n <!-- Le voile de l'option verre. Sans lui, le flou n'a rien \u00E0 flouter et on\n n'obtient que des panneaux plus sombres. -->\n <div class=\"nl-veil\" aria-hidden=\"true\"></div>\n\n <header class=\"nl-head\">\n @if (anchor(); as group) {\n <button\n type=\"button\"\n class=\"nl-back\"\n [attr.aria-label]=\"backLabel()\"\n (click)=\"back()\"\n >\n <fold-icon name=\"chevron-left\" size=\"sm\" />\n </button>\n <!-- L'ANCRE. La tuile touch\u00E9e ne dispara\u00EEt pas : elle remonte ici, \u00E0\n sa taille de pastille. Cliquable, parce que l'\u00E9l\u00E9ment qui a servi\n de porte doit aussi servir de sortie. -->\n <button\n type=\"button\"\n class=\"nl-anchor\"\n [attr.aria-label]=\"backLabel()\"\n (click)=\"back()\"\n >\n @if (group.icon(); as name) {\n <fold-icon [name]=\"name\" size=\"sm\" />\n } @else {\n <span aria-hidden=\"true\">{{ group.label().charAt(0) }}</span>\n }\n </button>\n <span class=\"nl-titles\">\n <span class=\"nl-title\">{{ group.label() }}</span>\n <span class=\"nl-sub\">{{ entryCountText() }}</span>\n </span>\n } @else {\n <span class=\"nl-titles\">\n @if (eyebrow(); as e) {\n <span class=\"nl-eyebrow\">{{ e }}</span>\n }\n @if (heading(); as h) {\n <span class=\"nl-heading\">{{ h }}</span>\n }\n </span>\n }\n\n <button\n type=\"button\"\n class=\"nl-close\"\n [attr.aria-label]=\"closeLabel()\"\n (click)=\"close()\"\n >\n <fold-icon name=\"close\" size=\"md\" />\n </button>\n </header>\n\n <div\n class=\"nl-body\"\n (pointerdown)=\"onPointerDown($event)\"\n (pointerup)=\"onPointerUp($event)\"\n >\n <!-- La grille porte les DEUX niveaux : un groupe ouvert sort de la\n grille et couvre ce corps, sans que rien ne bouge dans le DOM. -->\n <div
|
|
5250
|
+
], template: "@if (open()) {\n <div class=\"nl-scrim\" aria-hidden=\"true\"></div>\n <!-- Le gestionnaire de renvoi vit sur le DIALOGUE, pas sur le voile : le\n dialogue est `position: fixed; inset: 0` par-dessus, donc un clic\n n'atteignait jamais le voile. Le clavier sort par `Escape`. -->\n <!-- Une SURFACE DE CHROME, et pas seulement un fond sombre : sans cette\n d\u00E9claration le lanceur peignait le sol du rail tout en lisant l'encre de\n la PAGE \u2014 texte sombre sur sombre, et des tuiles claires au milieu d'un\n panneau noir, sous tout th\u00E8me mixte (navi). Peindre le sol d'une famille\n sans en rejoindre la polarit\u00E9, c'est en revendiquer la moiti\u00E9. -->\n <div\n class=\"nl-dialog\"\n foldSurface=\"chrome\"\n role=\"dialog\"\n aria-modal=\"true\"\n [attr.aria-label]=\"label()\"\n [attr.data-level]=\"level()\"\n [attr.data-glass]=\"glass() ? '' : null\"\n [foldFocusTrap]=\"open()\"\n tabindex=\"-1\"\n (click)=\"onSurfaceClick($event)\"\n >\n <!-- Le voile de l'option verre. Sans lui, le flou n'a rien \u00E0 flouter et on\n n'obtient que des panneaux plus sombres. -->\n <div class=\"nl-veil\" aria-hidden=\"true\"></div>\n\n <header class=\"nl-head\">\n @if (anchor(); as group) {\n <button\n type=\"button\"\n class=\"nl-back\"\n [attr.aria-label]=\"backLabel()\"\n (click)=\"back()\"\n >\n <fold-icon name=\"chevron-left\" size=\"sm\" />\n </button>\n <!-- L'ANCRE. La tuile touch\u00E9e ne dispara\u00EEt pas : elle remonte ici, \u00E0\n sa taille de pastille. Cliquable, parce que l'\u00E9l\u00E9ment qui a servi\n de porte doit aussi servir de sortie. -->\n <button\n type=\"button\"\n class=\"nl-anchor\"\n [attr.aria-label]=\"backLabel()\"\n (click)=\"back()\"\n >\n @if (group.icon(); as name) {\n <fold-icon [name]=\"name\" size=\"sm\" />\n } @else {\n <span aria-hidden=\"true\">{{ group.label().charAt(0) }}</span>\n }\n </button>\n <span class=\"nl-titles\">\n <span class=\"nl-title\">{{ group.label() }}</span>\n <span class=\"nl-sub\">{{ entryCountText() }}</span>\n </span>\n } @else {\n <span class=\"nl-titles\">\n @if (eyebrow(); as e) {\n <span class=\"nl-eyebrow\">{{ e }}</span>\n }\n @if (heading(); as h) {\n <span class=\"nl-heading\">{{ h }}</span>\n }\n </span>\n }\n\n <button\n type=\"button\"\n class=\"nl-close\"\n [attr.aria-label]=\"closeLabel()\"\n (click)=\"close()\"\n >\n <fold-icon name=\"close\" size=\"md\" />\n </button>\n </header>\n\n <div\n class=\"nl-body\"\n (pointerdown)=\"onPointerDown($event)\"\n (pointerup)=\"onPointerUp($event)\"\n >\n <!-- La grille porte les DEUX niveaux : un groupe ouvert sort de la\n grille et couvre ce corps, sans que rien ne bouge dans le DOM. -->\n <div\n class=\"nl-grid\"\n [style.--nav-cols]=\"fixedCols()\"\n [attr.data-cols]=\"fixedCols() === null ? null : ''\"\n [attr.data-density]=\"density()\"\n >\n <ng-content />\n </div>\n </div>\n\n <div class=\"nl-foot\">\n <ng-content select=\"[footer]\" />\n </div>\n\n <!-- Les deux crans de profondeur. `aria-hidden` : la r\u00E9gion vive dit la\n m\u00EAme chose en mots, et un lecteur d'\u00E9cran n'a que faire de deux\n pastilles. -->\n <div class=\"nl-dots\" aria-hidden=\"true\">\n <span class=\"nl-dot\" [class.is-on]=\"level() === 1\"></span>\n <span class=\"nl-dot\" [class.is-on]=\"level() === 2\"></span>\n </div>\n\n <!-- La descente est MUETTE sans ceci : l'animation ne compense rien. -->\n <p class=\"nl-live\" aria-live=\"polite\">{{ announcement() }}</p>\n </div>\n}\n", styles: ["@charset \"UTF-8\";:host{display:contents}.nl-scrim{position:fixed;inset:0;z-index:200;background:var(--fold-color-scrim);backdrop-filter:blur(var(--fold-blur-glass));-webkit-backdrop-filter:blur(var(--fold-blur-glass));animation:fold-nl-fade .2s ease}.nl-dialog{--fold-nl-gutter: var(--fold-space-xl);--fold-nav-launcher-max-width: 720px;--fold-nl-ease-in: cubic-bezier(.22, 1, .36, 1);--fold-nl-tile-bg: var(--fold-color-surface-raised);--fold-nl-accent-bg: var(--fold-color-primary-surface);--fold-nl-sheet-bg: var(--fold-color-bg-rail-secondary);--fold-nl-row-bg: var(--fold-color-surface-card);--fold-nl-backdrop: none;--fold-nl-sheet-backdrop: none;position:fixed;inset:0;z-index:201;display:flex;flex-direction:column;padding:var(--fold-space-3xl) var(--fold-nl-gutter) var(--fold-space-xl);background:var(--fold-color-bg-rail-primary);color:var(--fold-color-text)}.nl-head{flex:none;display:flex;align-items:center;justify-content:space-between;gap:var(--fold-space-md);min-height:44px;margin-bottom:var(--fold-space-xl)}.nl-titles{display:flex;min-width:0;flex-direction:column}.nl-eyebrow{font-family:var(--fold-font-mono);font-size:var(--fold-text-xs);letter-spacing:var(--fold-tracking-caps);text-transform:uppercase;color:var(--fold-color-text-muted)}.nl-heading{font-size:var(--fold-text-xl);font-weight:var(--fold-weight-bold);letter-spacing:var(--fold-tracking-tighter);color:var(--fold-color-text)}.nl-title{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:var(--fold-text-lg);font-weight:var(--fold-weight-bold);letter-spacing:var(--fold-tracking-tighter);color:var(--fold-color-text);animation:fold-nl-crumb .32s var(--fold-nl-ease-in) both;animation-delay:40ms}.nl-sub{font-size:var(--fold-text-xs);color:var(--fold-color-text-muted);animation:fold-nl-crumb .32s var(--fold-nl-ease-in) both;animation-delay:40ms}.nl-back,.nl-close,.nl-anchor{flex:none;display:grid;place-items:center;width:30px;height:30px;padding:0;border-radius:var(--fold-radius-sm);background:var(--fold-color-surface-raised);border:1px solid var(--fold-color-border);color:var(--fold-color-text-secondary);cursor:pointer;transition:background .14s ease,color .14s ease}.nl-back:hover,.nl-close:hover{background:var(--fold-color-surface-hover);color:var(--fold-color-text)}.nl-anchor{background:var(--fold-color-primary);border-color:transparent;color:var(--fold-color-on-primary);animation:fold-nl-anchor-in .3s var(--fold-nl-ease-in) both}.nl-close{margin-inline-start:auto}.nl-body{position:relative;display:flex;flex-direction:column;flex:1 1 auto;min-height:0;margin-inline:calc(-1 * var(--fold-nl-gutter));overflow:hidden}.nl-grid{--nav-tile-min: 96px;flex:1 1 auto;min-height:0;overflow-y:auto;overscroll-behavior:contain;-webkit-overflow-scrolling:touch;padding-inline:max(var(--fold-nl-gutter),(100% - var(--fold-nav-launcher-max-width)) / 2);padding-block-end:var(--fold-space-sm);display:grid;grid-template-columns:repeat(auto-fill,minmax(min(100%,var(--nav-tile-min)),1fr));grid-auto-rows:max-content;align-content:start;gap:var(--fold-space-sm)}.nl-grid[data-density=few]{--nav-tile-min: 144px}.nl-grid[data-cols]{grid-template-columns:repeat(var(--nav-cols),minmax(0,1fr))}@media(max-width:360px){.nl-grid,.nl-grid[data-cols]{grid-template-columns:repeat(2,minmax(0,1fr))}}.nl-dialog[data-level=\"2\"] .nl-grid{visibility:hidden}.nl-foot:not(:empty){flex:none;margin-top:auto;padding-top:var(--fold-space-md);border-top:1px solid var(--fold-color-border-subtle)}.nl-dots{flex:none;display:flex;align-items:center;justify-content:center;gap:var(--fold-space-xs);margin-top:var(--fold-space-md)}.nl-dot{width:22px;height:3px;border-radius:var(--fold-radius-pill);background:var(--fold-color-border);transition:background .28s ease}.nl-dot.is-on{background:var(--fold-color-primary)}.nl-live{position:absolute;width:1px;height:1px;margin:0;overflow:hidden;clip-path:inset(50%);white-space:nowrap}.nl-veil{position:absolute;inset:0;opacity:0;pointer-events:none;transition:opacity .3s ease;background:radial-gradient(120% 70% at 12% 4%,color-mix(in srgb,var(--fold-color-primary) 34%,transparent),transparent 58%),radial-gradient(90% 60% at 88% 22%,color-mix(in srgb,var(--fold-color-primary-strong) 30%,transparent),transparent 60%),radial-gradient(80% 55% at 70% 96%,color-mix(in srgb,var(--fold-color-warning) 16%,transparent),transparent 62%)}.nl-dialog[data-glass]{--fold-nl-tile-bg: color-mix( in srgb, var(--fold-color-bg-rail-secondary) 52%, transparent );--fold-nl-accent-bg: color-mix( in srgb, var(--fold-color-primary) 14%, transparent );--fold-nl-sheet-bg: color-mix( in srgb, var(--fold-color-bg-rail-secondary) 60%, transparent );--fold-nl-row-bg: color-mix( in srgb, var(--fold-color-surface-card) 50%, transparent );--fold-nl-backdrop: blur(20px) saturate(150%);--fold-nl-sheet-backdrop: blur(26px) saturate(150%);background:var(--fold-color-bg-header)}.nl-dialog[data-glass] .nl-veil{opacity:1}@keyframes fold-nl-fade{0%{opacity:0}to{opacity:1}}@keyframes fold-nl-crumb{0%{opacity:0;transform:translate(-12px)}to{opacity:1;transform:none}}@keyframes fold-nl-anchor-in{0%{opacity:0;transform:scale(.6) rotate(-8deg)}to{opacity:1;transform:none}}@media(prefers-reduced-motion:reduce){.nl-scrim,.nl-title,.nl-sub,.nl-anchor{animation:none}}\n"] }]
|
|
5233
5251
|
}], ctorParameters: () => [], propDecorators: { open: [{ type: i0.Input, args: [{ isSignal: true, alias: "open", required: false }] }, { type: i0.Output, args: ["openChange"] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], eyebrow: [{ type: i0.Input, args: [{ isSignal: true, alias: "eyebrow", required: false }] }], heading: [{ type: i0.Input, args: [{ isSignal: true, alias: "heading", required: false }] }], closeLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "closeLabel", required: false }] }], backLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "backLabel", required: false }] }], glass: [{ type: i0.Input, args: [{ isSignal: true, alias: "glass", required: false }] }], columns: [{ type: i0.Input, args: [{ isSignal: true, alias: "columns", required: false }] }], tiles: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => FoldNavTileComponent), { isSignal: true }] }], groups: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => FoldNavGroupComponent), { isSignal: true }] }], onEscape: [{
|
|
5234
5252
|
type: HostListener,
|
|
5235
5253
|
args: ["document:keydown.escape"]
|