fold-ng 0.6.0 → 0.6.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 CHANGED
@@ -8,6 +8,35 @@ All notable changes to **fold-ng** are documented here. The format follows
8
8
 
9
9
  _Nothing yet._
10
10
 
11
+ ## [0.6.1] - 2026-07-28
12
+
13
+ ### Changed
14
+
15
+ - **`fold-tab-panel` now owns the vertical rhythm between its children.** It was
16
+ a neutral `display: block` with no spacing, so stacked `fold-page-section`s (or
17
+ cards) inside a panel sat flush against each other — the page gap only reaches
18
+ the _direct_ children of `.page-body`, and a tab panel isn't one. A panel is a
19
+ content region, like `.page-body`, so it now lays its children out as a flex
20
+ column with a `gap`. The gap is the new **`--fold-panel-gap`** token — fluid
21
+ `16 → 24px`, deliberately one notch tighter than `--fold-page-gap` (`20 → 32px`)
22
+ so panel content reads as one screen rather than distinct page bands. Escape
23
+ hatch unchanged from `.page-body`: wrap two elements in a container to tighten
24
+ them (a single child gets no gap at all). Consumers that re-established this
25
+ spacing by hand on the panel can drop it.
26
+
27
+ ### Fixed
28
+
29
+ - **`fold-page-layout` header no longer starves the title/description on narrow
30
+ screens.** The head was a permanent `space-between` row: the actions slot held
31
+ its intrinsic width (`flex: none`) while the text column carried `min-width: 0`,
32
+ so wide `[pageActions]` (e.g. two full-label buttons) squeezed the description
33
+ down to its longest word — one word per line, stacked vertically. Below `640px`
34
+ the head now stacks: title/description take the full width and the actions drop
35
+ onto their own row beneath. Keyed to **both** the viewport (`@media`) and an
36
+ ancestor container (`@container`, for the gallery's responsive preview), mirroring
37
+ `app-shell`. The actions slot also gained `flex-wrap` so several wide buttons
38
+ wrap among themselves instead of overflowing the gutter.
39
+
11
40
  ## [0.6.0] - 2026-07-28
12
41
 
13
42
  ### Added
@@ -621,7 +650,8 @@ design-token stylesheet.
621
650
  `currentColor`; `prefers-reduced-motion` + `forced-colors` are respected;
622
651
  strings localise via inputs / providers (`provideFoldPanelLabels`).
623
652
 
624
- [unreleased]: https://github.com/hugoheynard/fold-ng/compare/v0.6.0...HEAD
653
+ [unreleased]: https://github.com/hugoheynard/fold-ng/compare/v0.6.1...HEAD
654
+ [0.6.1]: https://github.com/hugoheynard/fold-ng/releases/tag/v0.6.1
625
655
  [0.6.0]: https://github.com/hugoheynard/fold-ng/releases/tag/v0.6.0
626
656
  [0.5.2]: https://github.com/hugoheynard/fold-ng/releases/tag/v0.5.2
627
657
  [0.5.1]: https://github.com/hugoheynard/fold-ng/releases/tag/v0.5.1
@@ -7375,11 +7375,11 @@ class FoldPageLayoutComponent {
7375
7375
  customTitle = contentChild(FoldPageTitleDirective, /* @ts-ignore */
7376
7376
  ...(ngDevMode ? [{ debugName: "customTitle" }] : /* istanbul ignore next */ []));
7377
7377
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: FoldPageLayoutComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
7378
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.8", type: FoldPageLayoutComponent, isStandalone: true, selector: "fold-page-layout", inputs: { title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: false, transformFunction: null }, icon: { classPropertyName: "icon", publicName: "icon", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "attr.title": "null" } }, queries: [{ propertyName: "customTitle", first: true, predicate: FoldPageTitleDirective, descendants: true, isSignal: true }], ngImport: i0, template: "@if (title() || customTitle()) {\n <header class=\"page-head\">\n <div class=\"page-head-text\">\n <h1 class=\"page-title\">\n @if (title(); as t) {\n @if (icon(); as ic) {\n <fold-icon class=\"page-icon\" [name]=\"ic\" [size]=\"22\" />\n }\n <span>{{ t }}</span>\n }\n <ng-content select=\"[pageTitle]\" />\n <span class=\"page-title-badge\"\n ><ng-content select=\"[titleBadge]\"\n /></span>\n </h1>\n <div class=\"page-desc\"><ng-content select=\"p[description]\" /></div>\n </div>\n <div class=\"page-actions\"><ng-content select=\"[pageActions]\" /></div>\n </header>\n}\n<div class=\"page-body\"><ng-content /></div>\n", styles: ["@charset \"UTF-8\";:host{display:flex;flex-direction:column;gap:var(--fold-page-gap, 32px);box-sizing:border-box;padding-block:var(--fold-page-pad-top, 28px) var(--fold-page-pad-bottom, 40px);padding-inline:var(--fold-page-gutter, 32px);flex:1 1 auto;min-height:0;overflow-y:auto;overscroll-behavior:contain}.page-head{display:flex;align-items:flex-start;justify-content:space-between;gap:var(--fold-space-lg)}.page-head-text{display:flex;flex-direction:column;gap:var(--fold-space-xs);min-width:0}.page-title{margin:0;display:flex;align-items:center;gap:var(--fold-space-sm);font-size:var(--fold-text-xl);font-weight:700;letter-spacing:-.02em;line-height:1.2;color:var(--fold-color-text)}.page-icon{flex:none;color:var(--fold-color-primary-text)}.page-title-badge{display:inline-flex;align-items:center;gap:var(--fold-space-sm)}.page-title-badge:empty{display:none}.page-desc{font-size:var(--fold-text-sm);line-height:1.5;max-width:var(--fold-page-desc-measure, none);color:var(--fold-color-text-muted)}.page-desc:empty{display:none}.page-desc>*{margin:0}.page-actions{flex:none;display:inline-flex;align-items:center;gap:var(--fold-space-sm)}.page-actions:empty{display:none}.page-body{display:flex;flex-direction:column;gap:var(--fold-page-gap, 32px)}\n"], dependencies: [{ kind: "component", type: FoldIconComponent, selector: "fold-icon", inputs: ["name", "size", "tone", "title"] }] });
7378
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.8", type: FoldPageLayoutComponent, isStandalone: true, selector: "fold-page-layout", inputs: { title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: false, transformFunction: null }, icon: { classPropertyName: "icon", publicName: "icon", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "attr.title": "null" } }, queries: [{ propertyName: "customTitle", first: true, predicate: FoldPageTitleDirective, descendants: true, isSignal: true }], ngImport: i0, template: "@if (title() || customTitle()) {\n <header class=\"page-head\">\n <div class=\"page-head-text\">\n <h1 class=\"page-title\">\n @if (title(); as t) {\n @if (icon(); as ic) {\n <fold-icon class=\"page-icon\" [name]=\"ic\" [size]=\"22\" />\n }\n <span>{{ t }}</span>\n }\n <ng-content select=\"[pageTitle]\" />\n <span class=\"page-title-badge\"\n ><ng-content select=\"[titleBadge]\"\n /></span>\n </h1>\n <div class=\"page-desc\"><ng-content select=\"p[description]\" /></div>\n </div>\n <div class=\"page-actions\"><ng-content select=\"[pageActions]\" /></div>\n </header>\n}\n<div class=\"page-body\"><ng-content /></div>\n", styles: ["@charset \"UTF-8\";:host{display:flex;flex-direction:column;gap:var(--fold-page-gap, 32px);box-sizing:border-box;padding-block:var(--fold-page-pad-top, 28px) var(--fold-page-pad-bottom, 40px);padding-inline:var(--fold-page-gutter, 32px);flex:1 1 auto;min-height:0;overflow-y:auto;overscroll-behavior:contain}.page-head{display:flex;align-items:flex-start;justify-content:space-between;gap:var(--fold-space-lg)}.page-head-text{display:flex;flex-direction:column;gap:var(--fold-space-xs);min-width:0}.page-title{margin:0;display:flex;align-items:center;gap:var(--fold-space-sm);font-size:var(--fold-text-xl);font-weight:700;letter-spacing:-.02em;line-height:1.2;color:var(--fold-color-text)}.page-icon{flex:none;color:var(--fold-color-primary-text)}.page-title-badge{display:inline-flex;align-items:center;gap:var(--fold-space-sm)}.page-title-badge:empty{display:none}.page-desc{font-size:var(--fold-text-sm);line-height:1.5;max-width:var(--fold-page-desc-measure, none);color:var(--fold-color-text-muted)}.page-desc:empty{display:none}.page-desc>*{margin:0}.page-actions{flex:none;display:inline-flex;align-items:center;flex-wrap:wrap;gap:var(--fold-space-sm)}.page-actions:empty{display:none}@media(max-width:640px){.page-head{flex-direction:column;align-items:stretch;gap:var(--fold-space-md)}.page-actions{justify-content:flex-start}}@container (max-width: 640px){.page-head{flex-direction:column;align-items:stretch;gap:var(--fold-space-md)}.page-actions{justify-content:flex-start}}.page-body{display:flex;flex-direction:column;gap:var(--fold-page-gap, 32px)}\n"], dependencies: [{ kind: "component", type: FoldIconComponent, selector: "fold-icon", inputs: ["name", "size", "tone", "title"] }] });
7379
7379
  }
7380
7380
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: FoldPageLayoutComponent, decorators: [{
7381
7381
  type: Component,
7382
- args: [{ selector: "fold-page-layout", standalone: true, imports: [FoldIconComponent], host: { "[attr.title]": "null" }, template: "@if (title() || customTitle()) {\n <header class=\"page-head\">\n <div class=\"page-head-text\">\n <h1 class=\"page-title\">\n @if (title(); as t) {\n @if (icon(); as ic) {\n <fold-icon class=\"page-icon\" [name]=\"ic\" [size]=\"22\" />\n }\n <span>{{ t }}</span>\n }\n <ng-content select=\"[pageTitle]\" />\n <span class=\"page-title-badge\"\n ><ng-content select=\"[titleBadge]\"\n /></span>\n </h1>\n <div class=\"page-desc\"><ng-content select=\"p[description]\" /></div>\n </div>\n <div class=\"page-actions\"><ng-content select=\"[pageActions]\" /></div>\n </header>\n}\n<div class=\"page-body\"><ng-content /></div>\n", styles: ["@charset \"UTF-8\";:host{display:flex;flex-direction:column;gap:var(--fold-page-gap, 32px);box-sizing:border-box;padding-block:var(--fold-page-pad-top, 28px) var(--fold-page-pad-bottom, 40px);padding-inline:var(--fold-page-gutter, 32px);flex:1 1 auto;min-height:0;overflow-y:auto;overscroll-behavior:contain}.page-head{display:flex;align-items:flex-start;justify-content:space-between;gap:var(--fold-space-lg)}.page-head-text{display:flex;flex-direction:column;gap:var(--fold-space-xs);min-width:0}.page-title{margin:0;display:flex;align-items:center;gap:var(--fold-space-sm);font-size:var(--fold-text-xl);font-weight:700;letter-spacing:-.02em;line-height:1.2;color:var(--fold-color-text)}.page-icon{flex:none;color:var(--fold-color-primary-text)}.page-title-badge{display:inline-flex;align-items:center;gap:var(--fold-space-sm)}.page-title-badge:empty{display:none}.page-desc{font-size:var(--fold-text-sm);line-height:1.5;max-width:var(--fold-page-desc-measure, none);color:var(--fold-color-text-muted)}.page-desc:empty{display:none}.page-desc>*{margin:0}.page-actions{flex:none;display:inline-flex;align-items:center;gap:var(--fold-space-sm)}.page-actions:empty{display:none}.page-body{display:flex;flex-direction:column;gap:var(--fold-page-gap, 32px)}\n"] }]
7382
+ args: [{ selector: "fold-page-layout", standalone: true, imports: [FoldIconComponent], host: { "[attr.title]": "null" }, template: "@if (title() || customTitle()) {\n <header class=\"page-head\">\n <div class=\"page-head-text\">\n <h1 class=\"page-title\">\n @if (title(); as t) {\n @if (icon(); as ic) {\n <fold-icon class=\"page-icon\" [name]=\"ic\" [size]=\"22\" />\n }\n <span>{{ t }}</span>\n }\n <ng-content select=\"[pageTitle]\" />\n <span class=\"page-title-badge\"\n ><ng-content select=\"[titleBadge]\"\n /></span>\n </h1>\n <div class=\"page-desc\"><ng-content select=\"p[description]\" /></div>\n </div>\n <div class=\"page-actions\"><ng-content select=\"[pageActions]\" /></div>\n </header>\n}\n<div class=\"page-body\"><ng-content /></div>\n", styles: ["@charset \"UTF-8\";:host{display:flex;flex-direction:column;gap:var(--fold-page-gap, 32px);box-sizing:border-box;padding-block:var(--fold-page-pad-top, 28px) var(--fold-page-pad-bottom, 40px);padding-inline:var(--fold-page-gutter, 32px);flex:1 1 auto;min-height:0;overflow-y:auto;overscroll-behavior:contain}.page-head{display:flex;align-items:flex-start;justify-content:space-between;gap:var(--fold-space-lg)}.page-head-text{display:flex;flex-direction:column;gap:var(--fold-space-xs);min-width:0}.page-title{margin:0;display:flex;align-items:center;gap:var(--fold-space-sm);font-size:var(--fold-text-xl);font-weight:700;letter-spacing:-.02em;line-height:1.2;color:var(--fold-color-text)}.page-icon{flex:none;color:var(--fold-color-primary-text)}.page-title-badge{display:inline-flex;align-items:center;gap:var(--fold-space-sm)}.page-title-badge:empty{display:none}.page-desc{font-size:var(--fold-text-sm);line-height:1.5;max-width:var(--fold-page-desc-measure, none);color:var(--fold-color-text-muted)}.page-desc:empty{display:none}.page-desc>*{margin:0}.page-actions{flex:none;display:inline-flex;align-items:center;flex-wrap:wrap;gap:var(--fold-space-sm)}.page-actions:empty{display:none}@media(max-width:640px){.page-head{flex-direction:column;align-items:stretch;gap:var(--fold-space-md)}.page-actions{justify-content:flex-start}}@container (max-width: 640px){.page-head{flex-direction:column;align-items:stretch;gap:var(--fold-space-md)}.page-actions{justify-content:flex-start}}.page-body{display:flex;flex-direction:column;gap:var(--fold-page-gap, 32px)}\n"] }]
7383
7383
  }], propDecorators: { title: [{ type: i0.Input, args: [{ isSignal: true, alias: "title", required: false }] }], icon: [{ type: i0.Input, args: [{ isSignal: true, alias: "icon", required: false }] }], customTitle: [{ type: i0.ContentChild, args: [i0.forwardRef(() => FoldPageTitleDirective), { isSignal: true }] }] } });
7384
7384
 
7385
7385
  /**
@@ -8060,7 +8060,7 @@ class FoldTabPanelComponent {
8060
8060
  labelledBy = computed(() => this.tabs().tabId(this.key()), /* @ts-ignore */
8061
8061
  ...(ngDevMode ? [{ debugName: "labelledBy" }] : /* istanbul ignore next */ []));
8062
8062
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: FoldTabPanelComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
8063
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.0.8", type: FoldTabPanelComponent, isStandalone: true, selector: "fold-tab-panel", inputs: { tabs: { classPropertyName: "tabs", publicName: "tabs", isSignal: true, isRequired: true, transformFunction: null }, key: { classPropertyName: "key", publicName: "key", isSignal: true, isRequired: true, transformFunction: null } }, host: { attributes: { "role": "tabpanel" }, properties: { "id": "panelId()", "attr.aria-labelledby": "labelledBy()", "attr.tabindex": "isActive() ? 0 : null", "hidden": "!isActive()" } }, ngImport: i0, template: "<ng-content />", isInline: true, styles: [":host{display:block}:host([hidden]){display:none}\n"] });
8063
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.0.8", type: FoldTabPanelComponent, isStandalone: true, selector: "fold-tab-panel", inputs: { tabs: { classPropertyName: "tabs", publicName: "tabs", isSignal: true, isRequired: true, transformFunction: null }, key: { classPropertyName: "key", publicName: "key", isSignal: true, isRequired: true, transformFunction: null } }, host: { attributes: { "role": "tabpanel" }, properties: { "id": "panelId()", "attr.aria-labelledby": "labelledBy()", "attr.tabindex": "isActive() ? 0 : null", "hidden": "!isActive()" } }, ngImport: i0, template: "<ng-content />", isInline: true, styles: [":host{display:flex;flex-direction:column;gap:var(--fold-panel-gap)}:host([hidden]){display:none}\n"] });
8064
8064
  }
8065
8065
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: FoldTabPanelComponent, decorators: [{
8066
8066
  type: Component,
@@ -8070,7 +8070,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
8070
8070
  "[attr.aria-labelledby]": "labelledBy()",
8071
8071
  "[attr.tabindex]": "isActive() ? 0 : null",
8072
8072
  "[hidden]": "!isActive()",
8073
- }, template: "<ng-content />", styles: [":host{display:block}:host([hidden]){display:none}\n"] }]
8073
+ }, template: "<ng-content />", styles: [":host{display:flex;flex-direction:column;gap:var(--fold-panel-gap)}:host([hidden]){display:none}\n"] }]
8074
8074
  }], propDecorators: { tabs: [{ type: i0.Input, args: [{ isSignal: true, alias: "tabs", required: true }] }], key: [{ type: i0.Input, args: [{ isSignal: true, alias: "key", required: true }] }] } });
8075
8075
 
8076
8076
  /** Variant → leading status icon. */