@frame-kit/ui-ng 0.2.3 → 0.2.5

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.
@@ -30,11 +30,11 @@ class NavBrandComponent {
30
30
  return this.classes();
31
31
  }
32
32
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: NavBrandComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
33
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.9", type: NavBrandComponent, isStandalone: true, selector: "fk-nav-brand", inputs: { brandText: { classPropertyName: "brandText", publicName: "brandText", isSignal: true, isRequired: true, transformFunction: null }, href: { classPropertyName: "href", publicName: "href", isSignal: true, isRequired: false, transformFunction: null }, description: { classPropertyName: "description", publicName: "description", isSignal: true, isRequired: false, transformFunction: null }, id: { classPropertyName: "id", publicName: "id", isSignal: true, isRequired: false, transformFunction: null }, className: { classPropertyName: "className", publicName: "className", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class": "this.hostClass" } }, ngImport: i0, template: "<a\n class=\"fk-nav-brand__link\"\n [routerLink]=\"href()\"\n [attr.id]=\"id()\"\n [attr.aria-label]=\"ariaLabel()\"\n>\n <span class=\"fk-nav-brand__logo\">\n <ng-content select=\"[navLogo]\"></ng-content>\n </span>\n <span class=\"fk-nav-brand__body\">\n <span class=\"fk-nav-brand__text\">{{ brandText() }}</span>\n @if (description(); as brandDescription) {\n <span class=\"fk-nav-brand__description\">{{ brandDescription }}</span>\n }\n </span>\n</a>\n", styles: [":host{display:inline-block;max-width:100%}.fk-nav-brand__link{display:flex;width:100%;align-items:center;gap:var(--fk-nav-brand-gap, .5rem);text-decoration:none;color:var(--fk-nav-brand-color, inherit);min-width:0}.fk-nav-brand__link:hover{color:var(--fk-nav-brand-color-hover, inherit)}.fk-nav-brand__logo{display:inline-flex;flex:none;align-items:center}.fk-nav-brand__body{display:flex;flex:1 1 auto;flex-direction:column;justify-content:center;align-items:flex-start;min-width:0;gap:var(--fk-nav-brand-description-gap, .125rem)}.fk-nav-brand__text{font-family:var(--fk-nav-brand-font-family, var(--fk-font-family-display, var(--fk-font-family-base, sans-serif)));font-size:var(--fk-nav-brand-font-size, 1.125rem);font-weight:var(--fk-nav-brand-font-weight, 600);line-height:var(--fk-nav-brand-line-height, 1);white-space:nowrap;max-width:100%;overflow:hidden;text-overflow:ellipsis}.fk-nav-brand__description{font-family:var(--fk-nav-brand-description-font-family, var(--fk-font-family-base, sans-serif));font-size:var(--fk-nav-brand-description-font-size, .6875rem);font-weight:var(--fk-nav-brand-description-font-weight, 500);line-height:var(--fk-nav-brand-description-line-height, 1.2);letter-spacing:var(--fk-nav-brand-description-letter-spacing, .02em);color:var(--fk-nav-brand-description-color, var(--fk-color-muted, inherit));white-space:nowrap;max-width:100%;overflow:hidden;text-overflow:ellipsis}\n"], dependencies: [{ kind: "ngmodule", type: RouterModule }, { kind: "directive", type: i1.RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
33
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.9", type: NavBrandComponent, isStandalone: true, selector: "fk-nav-brand", inputs: { brandText: { classPropertyName: "brandText", publicName: "brandText", isSignal: true, isRequired: true, transformFunction: null }, href: { classPropertyName: "href", publicName: "href", isSignal: true, isRequired: false, transformFunction: null }, description: { classPropertyName: "description", publicName: "description", isSignal: true, isRequired: false, transformFunction: null }, id: { classPropertyName: "id", publicName: "id", isSignal: true, isRequired: false, transformFunction: null }, className: { classPropertyName: "className", publicName: "className", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class": "this.hostClass" } }, ngImport: i0, template: "<a\n class=\"fk-nav-brand__link\"\n [routerLink]=\"href()\"\n [attr.id]=\"id()\"\n [attr.aria-label]=\"ariaLabel()\"\n>\n <span class=\"fk-nav-brand__logo\">\n <ng-content select=\"[navLogo]\"></ng-content>\n </span>\n <span class=\"fk-nav-brand__body\">\n <span class=\"fk-nav-brand__text\">{{ brandText() }}</span>\n @if (description(); as brandDescription) {\n <span class=\"fk-nav-brand__description\">{{ brandDescription }}</span>\n }\n </span>\n</a>\n", styles: [":host{display:inline-block;max-width:100%;min-width:0}.fk-nav-brand__link{display:flex;width:100%;align-items:center;gap:var(--fk-nav-brand-gap, .5rem);text-decoration:none;color:var(--fk-nav-brand-color, inherit);min-width:0}.fk-nav-brand__link:hover{color:var(--fk-nav-brand-color-hover, inherit)}.fk-nav-brand__logo{display:inline-flex;flex:none;align-items:center}.fk-nav-brand__body{display:flex;flex:1 1 auto;flex-direction:column;justify-content:center;align-items:flex-start;min-width:0;gap:var(--fk-nav-brand-description-gap, .125rem)}.fk-nav-brand__text{font-family:var(--fk-nav-brand-font-family, var(--fk-font-family-display, var(--fk-font-family-base, sans-serif)));font-size:var(--fk-nav-brand-font-size, 1.125rem);font-weight:var(--fk-nav-brand-font-weight, 600);line-height:var(--fk-nav-brand-line-height, 1);white-space:nowrap;max-width:100%;overflow:hidden;text-overflow:ellipsis}.fk-nav-brand__description{font-family:var(--fk-nav-brand-description-font-family, var(--fk-font-family-base, sans-serif));font-size:var(--fk-nav-brand-description-font-size, .6875rem);font-weight:var(--fk-nav-brand-description-font-weight, 500);line-height:var(--fk-nav-brand-description-line-height, 1.2);letter-spacing:var(--fk-nav-brand-description-letter-spacing, .02em);color:var(--fk-nav-brand-description-color, var(--fk-color-muted, inherit));white-space:nowrap;max-width:100%;overflow:hidden;text-overflow:ellipsis}\n"], dependencies: [{ kind: "ngmodule", type: RouterModule }, { kind: "directive", type: i1.RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
34
34
  }
35
35
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: NavBrandComponent, decorators: [{
36
36
  type: Component,
37
- args: [{ selector: 'fk-nav-brand', standalone: true, imports: [RouterModule], changeDetection: ChangeDetectionStrategy.OnPush, template: "<a\n class=\"fk-nav-brand__link\"\n [routerLink]=\"href()\"\n [attr.id]=\"id()\"\n [attr.aria-label]=\"ariaLabel()\"\n>\n <span class=\"fk-nav-brand__logo\">\n <ng-content select=\"[navLogo]\"></ng-content>\n </span>\n <span class=\"fk-nav-brand__body\">\n <span class=\"fk-nav-brand__text\">{{ brandText() }}</span>\n @if (description(); as brandDescription) {\n <span class=\"fk-nav-brand__description\">{{ brandDescription }}</span>\n }\n </span>\n</a>\n", styles: [":host{display:inline-block;max-width:100%}.fk-nav-brand__link{display:flex;width:100%;align-items:center;gap:var(--fk-nav-brand-gap, .5rem);text-decoration:none;color:var(--fk-nav-brand-color, inherit);min-width:0}.fk-nav-brand__link:hover{color:var(--fk-nav-brand-color-hover, inherit)}.fk-nav-brand__logo{display:inline-flex;flex:none;align-items:center}.fk-nav-brand__body{display:flex;flex:1 1 auto;flex-direction:column;justify-content:center;align-items:flex-start;min-width:0;gap:var(--fk-nav-brand-description-gap, .125rem)}.fk-nav-brand__text{font-family:var(--fk-nav-brand-font-family, var(--fk-font-family-display, var(--fk-font-family-base, sans-serif)));font-size:var(--fk-nav-brand-font-size, 1.125rem);font-weight:var(--fk-nav-brand-font-weight, 600);line-height:var(--fk-nav-brand-line-height, 1);white-space:nowrap;max-width:100%;overflow:hidden;text-overflow:ellipsis}.fk-nav-brand__description{font-family:var(--fk-nav-brand-description-font-family, var(--fk-font-family-base, sans-serif));font-size:var(--fk-nav-brand-description-font-size, .6875rem);font-weight:var(--fk-nav-brand-description-font-weight, 500);line-height:var(--fk-nav-brand-description-line-height, 1.2);letter-spacing:var(--fk-nav-brand-description-letter-spacing, .02em);color:var(--fk-nav-brand-description-color, var(--fk-color-muted, inherit));white-space:nowrap;max-width:100%;overflow:hidden;text-overflow:ellipsis}\n"] }]
37
+ args: [{ selector: 'fk-nav-brand', standalone: true, imports: [RouterModule], changeDetection: ChangeDetectionStrategy.OnPush, template: "<a\n class=\"fk-nav-brand__link\"\n [routerLink]=\"href()\"\n [attr.id]=\"id()\"\n [attr.aria-label]=\"ariaLabel()\"\n>\n <span class=\"fk-nav-brand__logo\">\n <ng-content select=\"[navLogo]\"></ng-content>\n </span>\n <span class=\"fk-nav-brand__body\">\n <span class=\"fk-nav-brand__text\">{{ brandText() }}</span>\n @if (description(); as brandDescription) {\n <span class=\"fk-nav-brand__description\">{{ brandDescription }}</span>\n }\n </span>\n</a>\n", styles: [":host{display:inline-block;max-width:100%;min-width:0}.fk-nav-brand__link{display:flex;width:100%;align-items:center;gap:var(--fk-nav-brand-gap, .5rem);text-decoration:none;color:var(--fk-nav-brand-color, inherit);min-width:0}.fk-nav-brand__link:hover{color:var(--fk-nav-brand-color-hover, inherit)}.fk-nav-brand__logo{display:inline-flex;flex:none;align-items:center}.fk-nav-brand__body{display:flex;flex:1 1 auto;flex-direction:column;justify-content:center;align-items:flex-start;min-width:0;gap:var(--fk-nav-brand-description-gap, .125rem)}.fk-nav-brand__text{font-family:var(--fk-nav-brand-font-family, var(--fk-font-family-display, var(--fk-font-family-base, sans-serif)));font-size:var(--fk-nav-brand-font-size, 1.125rem);font-weight:var(--fk-nav-brand-font-weight, 600);line-height:var(--fk-nav-brand-line-height, 1);white-space:nowrap;max-width:100%;overflow:hidden;text-overflow:ellipsis}.fk-nav-brand__description{font-family:var(--fk-nav-brand-description-font-family, var(--fk-font-family-base, sans-serif));font-size:var(--fk-nav-brand-description-font-size, .6875rem);font-weight:var(--fk-nav-brand-description-font-weight, 500);line-height:var(--fk-nav-brand-description-line-height, 1.2);letter-spacing:var(--fk-nav-brand-description-letter-spacing, .02em);color:var(--fk-nav-brand-description-color, var(--fk-color-muted, inherit));white-space:nowrap;max-width:100%;overflow:hidden;text-overflow:ellipsis}\n"] }]
38
38
  }], propDecorators: { brandText: [{ type: i0.Input, args: [{ isSignal: true, alias: "brandText", required: true }] }], href: [{ type: i0.Input, args: [{ isSignal: true, alias: "href", required: false }] }], description: [{ type: i0.Input, args: [{ isSignal: true, alias: "description", required: false }] }], id: [{ type: i0.Input, args: [{ isSignal: true, alias: "id", required: false }] }], className: [{ type: i0.Input, args: [{ isSignal: true, alias: "className", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }], hostClass: [{
39
39
  type: HostBinding,
40
40
  args: ['class']
@@ -1 +1 @@
1
- {"version":3,"file":"frame-kit-ui-ng-ui-nav-brand.mjs","sources":["../../../../packages/ui-ng/ui/nav-brand/nav-brand.component.ts","../../../../packages/ui-ng/ui/nav-brand/nav-brand.component.html","../../../../packages/ui-ng/ui/nav-brand/frame-kit-ui-ng-ui-nav-brand.ts"],"sourcesContent":["import {\n ChangeDetectionStrategy,\n Component,\n computed,\n HostBinding,\n input,\n} from '@angular/core';\nimport { RouterModule } from '@angular/router';\n\n@Component({\n selector: 'fk-nav-brand',\n standalone: true,\n imports: [RouterModule],\n changeDetection: ChangeDetectionStrategy.OnPush,\n templateUrl: './nav-brand.component.html',\n styleUrl: './nav-brand.component.scss',\n})\nexport class NavBrandComponent {\n // ===== REQUIRED =====\n /** Brand or application name rendered as the nav logo text. */\n readonly brandText = input.required<string>();\n\n // ===== OPTIONAL =====\n /** Router path the brand link navigates to when clicked. */\n readonly href = input<string>('/');\n\n /**\n * Secondary line under the brand text — typically the product or edition the\n * brand is qualifying (\"Developer Console\", \"Admin Workspace\"). Omit it and\n * the brand text stays a single line, centered against the logo exactly as\n * before; supply it and the two lines are centered against the logo as one\n * block, left-aligned with each other.\n */\n readonly description = input<string | null>(null);\n\n // ===== BASE PROPS =====\n readonly id = input<string | null>(null);\n readonly className = input<string>('');\n readonly ariaLabel = input<string | null>(null);\n\n // ===== COMPUTED =====\n readonly classes = computed(() => {\n return ['fk-nav-brand', this.className()].filter(Boolean).join(' ');\n });\n\n @HostBinding('class')\n get hostClass() {\n return this.classes();\n }\n}\n","<a\n class=\"fk-nav-brand__link\"\n [routerLink]=\"href()\"\n [attr.id]=\"id()\"\n [attr.aria-label]=\"ariaLabel()\"\n>\n <span class=\"fk-nav-brand__logo\">\n <ng-content select=\"[navLogo]\"></ng-content>\n </span>\n <span class=\"fk-nav-brand__body\">\n <span class=\"fk-nav-brand__text\">{{ brandText() }}</span>\n @if (description(); as brandDescription) {\n <span class=\"fk-nav-brand__description\">{{ brandDescription }}</span>\n }\n </span>\n</a>\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"frame-kit-ui-ng-ui-nav-brand.mjs","sources":["../../../../packages/ui-ng/ui/nav-brand/nav-brand.component.ts","../../../../packages/ui-ng/ui/nav-brand/nav-brand.component.html","../../../../packages/ui-ng/ui/nav-brand/frame-kit-ui-ng-ui-nav-brand.ts"],"sourcesContent":["import {\n ChangeDetectionStrategy,\n Component,\n computed,\n HostBinding,\n input,\n} from '@angular/core';\nimport { RouterModule } from '@angular/router';\n\n@Component({\n selector: 'fk-nav-brand',\n standalone: true,\n imports: [RouterModule],\n changeDetection: ChangeDetectionStrategy.OnPush,\n templateUrl: './nav-brand.component.html',\n styleUrl: './nav-brand.component.scss',\n})\nexport class NavBrandComponent {\n // ===== REQUIRED =====\n /** Brand or application name rendered as the nav logo text. */\n readonly brandText = input.required<string>();\n\n // ===== OPTIONAL =====\n /** Router path the brand link navigates to when clicked. */\n readonly href = input<string>('/');\n\n /**\n * Secondary line under the brand text — typically the product or edition the\n * brand is qualifying (\"Developer Console\", \"Admin Workspace\"). Omit it and\n * the brand text stays a single line, centered against the logo exactly as\n * before; supply it and the two lines are centered against the logo as one\n * block, left-aligned with each other.\n */\n readonly description = input<string | null>(null);\n\n // ===== BASE PROPS =====\n readonly id = input<string | null>(null);\n readonly className = input<string>('');\n readonly ariaLabel = input<string | null>(null);\n\n // ===== COMPUTED =====\n readonly classes = computed(() => {\n return ['fk-nav-brand', this.className()].filter(Boolean).join(' ');\n });\n\n @HostBinding('class')\n get hostClass() {\n return this.classes();\n }\n}\n","<a\n class=\"fk-nav-brand__link\"\n [routerLink]=\"href()\"\n [attr.id]=\"id()\"\n [attr.aria-label]=\"ariaLabel()\"\n>\n <span class=\"fk-nav-brand__logo\">\n <ng-content select=\"[navLogo]\"></ng-content>\n </span>\n <span class=\"fk-nav-brand__body\">\n <span class=\"fk-nav-brand__text\">{{ brandText() }}</span>\n @if (description(); as brandDescription) {\n <span class=\"fk-nav-brand__description\">{{ brandDescription }}</span>\n }\n </span>\n</a>\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@frame-kit/ui-ng",
3
- "version": "0.2.3",
3
+ "version": "0.2.5",
4
4
  "description": "Style-agnostic, token-driven Angular UI component library for FrameKit.",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -40,6 +40,19 @@ interface TableConfig<T = unknown> {
40
40
  emptyMessage: string;
41
41
  trackBy: (index: number, row: T) => unknown;
42
42
  rowStyleFn: ((row: T, index: number) => Record<string, string> | null) | null;
43
+ /**
44
+ * Groups consecutive rows under a heading. Returns the group key for a row,
45
+ * or `null` to leave it ungrouped; a heading is emitted wherever the key
46
+ * changes from the previous row.
47
+ *
48
+ * Grouping reads the rows in the order they are already sorted — it does not
49
+ * reorder them. Sort by the same field you group on, or the same group will
50
+ * open more than once.
51
+ *
52
+ * Mutually exclusive with virtualization, which assumes every row is
53
+ * `rowHeight` tall; a heading is not. Setting this turns virtualization off.
54
+ */
55
+ groupBy: ((row: T) => string | null) | null;
43
56
  virtualization: boolean | 'auto';
44
57
  virtualizationThreshold: number;
45
58
  rowHeight: number;
@@ -245,6 +258,15 @@ declare class DataTableComponent<T = unknown> {
245
258
  readonly expandableRow: _angular_core.InputSignal<((row: T) => boolean) | null>;
246
259
  /** Accessible label for each row's expansion chevron. */
247
260
  readonly expansionLabel: _angular_core.InputSignal<string>;
261
+ /**
262
+ * Template rendered inside a group heading. Receives the group key and the
263
+ * first row of the group, so a heading can show more than the key it was
264
+ * grouped by. Omit it and the key is rendered as text.
265
+ */
266
+ readonly groupHeadingRenderer: _angular_core.InputSignal<TemplateRef<{
267
+ $implicit: string;
268
+ row: T;
269
+ }> | null>;
248
270
  /** Fires when the user clicks a sortable column header, emitting the new sort state. */
249
271
  readonly sortChange: _angular_core.OutputEmitterRef<SortState>;
250
272
  /** Fires when a filter changes, emitting the updated filter state. */
@@ -269,6 +291,11 @@ declare class DataTableComponent<T = unknown> {
269
291
  readonly useVirtualization: _angular_core.Signal<boolean>;
270
292
  readonly virtualRows: _angular_core.Signal<T[]>;
271
293
  readonly virtualStartIndex: _angular_core.Signal<number>;
294
+ /**
295
+ * How many cells a heading has to span — the visible columns plus whichever
296
+ * of the selection and expansion cells the table is currently rendering.
297
+ */
298
+ readonly groupHeadingSpan: _angular_core.Signal<number>;
272
299
  readonly isEmpty: _angular_core.Signal<boolean>;
273
300
  readonly classes: _angular_core.Signal<string>;
274
301
  get hostClass(): string;
@@ -282,6 +309,11 @@ declare class DataTableComponent<T = unknown> {
282
309
  */
283
310
  private readonly groupCoordinator;
284
311
  constructor();
312
+ /**
313
+ * The group heading to emit before a row, or null when the row continues the
314
+ * group above it. The first row always opens its group.
315
+ */
316
+ groupHeadingFor(row: T, index: number): string | null;
285
317
  /** Resolves the display value for a cell given its column definition and row data. */
286
318
  getCellValue(column: ColumnDef<T>, row: T): unknown;
287
319
  /** Returns the trackBy function from the merged config for use in `*ngFor`. */
@@ -372,7 +404,7 @@ declare class DataTableComponent<T = unknown> {
372
404
  /** Returns the current sort direction for the given column id, or null if not sorted. */
373
405
  getSortDirection(columnId: string): 'asc' | 'desc' | null;
374
406
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<DataTableComponent<any>, never>;
375
- static ɵcmp: _angular_core.ɵɵComponentDeclaration<DataTableComponent<any>, "fk-data-table", never, { "className": { "alias": "className"; "required": false; "isSignal": true; }; "id": { "alias": "id"; "required": false; "isSignal": true; }; "ariaLabel": { "alias": "ariaLabel"; "required": false; "isSignal": true; }; "columns": { "alias": "columns"; "required": false; "isSignal": true; }; "rows": { "alias": "rows"; "required": false; "isSignal": true; }; "config": { "alias": "config"; "required": false; "isSignal": true; }; "loading": { "alias": "loading"; "required": false; "isSignal": true; }; "sortState": { "alias": "sortState"; "required": false; "isSignal": true; }; "filterState": { "alias": "filterState"; "required": false; "isSignal": true; }; "pageState": { "alias": "pageState"; "required": false; "isSignal": true; }; "rowSelection": { "alias": "rowSelection"; "required": false; "isSignal": true; }; "selectedKeys": { "alias": "selectedKeys"; "required": false; "isSignal": true; }; "getRowId": { "alias": "getRowId"; "required": false; "isSignal": true; }; "selectionDisabled": { "alias": "selectionDisabled"; "required": false; "isSignal": true; }; "selectionColumnPosition": { "alias": "selectionColumnPosition"; "required": false; "isSignal": true; }; "selectionLabel": { "alias": "selectionLabel"; "required": false; "isSignal": true; }; "rowDetailTemplate": { "alias": "rowDetailTemplate"; "required": false; "isSignal": true; }; "expansionColumnPosition": { "alias": "expansionColumnPosition"; "required": false; "isSignal": true; }; "expandedKeys": { "alias": "expandedKeys"; "required": false; "isSignal": true; }; "expandableRow": { "alias": "expandableRow"; "required": false; "isSignal": true; }; "expansionLabel": { "alias": "expansionLabel"; "required": false; "isSignal": true; }; }, { "sortChange": "sortChange"; "filterChange": "filterChange"; "pageChange": "pageChange"; "rowClick": "rowClick"; "selectionChange": "selectionChange"; "expansionChange": "expansionChange"; "columnResize": "columnResize"; }, never, never, true, never>;
407
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<DataTableComponent<any>, "fk-data-table", never, { "className": { "alias": "className"; "required": false; "isSignal": true; }; "id": { "alias": "id"; "required": false; "isSignal": true; }; "ariaLabel": { "alias": "ariaLabel"; "required": false; "isSignal": true; }; "columns": { "alias": "columns"; "required": false; "isSignal": true; }; "rows": { "alias": "rows"; "required": false; "isSignal": true; }; "config": { "alias": "config"; "required": false; "isSignal": true; }; "loading": { "alias": "loading"; "required": false; "isSignal": true; }; "sortState": { "alias": "sortState"; "required": false; "isSignal": true; }; "filterState": { "alias": "filterState"; "required": false; "isSignal": true; }; "pageState": { "alias": "pageState"; "required": false; "isSignal": true; }; "rowSelection": { "alias": "rowSelection"; "required": false; "isSignal": true; }; "selectedKeys": { "alias": "selectedKeys"; "required": false; "isSignal": true; }; "getRowId": { "alias": "getRowId"; "required": false; "isSignal": true; }; "selectionDisabled": { "alias": "selectionDisabled"; "required": false; "isSignal": true; }; "selectionColumnPosition": { "alias": "selectionColumnPosition"; "required": false; "isSignal": true; }; "selectionLabel": { "alias": "selectionLabel"; "required": false; "isSignal": true; }; "rowDetailTemplate": { "alias": "rowDetailTemplate"; "required": false; "isSignal": true; }; "expansionColumnPosition": { "alias": "expansionColumnPosition"; "required": false; "isSignal": true; }; "expandedKeys": { "alias": "expandedKeys"; "required": false; "isSignal": true; }; "expandableRow": { "alias": "expandableRow"; "required": false; "isSignal": true; }; "expansionLabel": { "alias": "expansionLabel"; "required": false; "isSignal": true; }; "groupHeadingRenderer": { "alias": "groupHeadingRenderer"; "required": false; "isSignal": true; }; }, { "sortChange": "sortChange"; "filterChange": "filterChange"; "pageChange": "pageChange"; "rowClick": "rowClick"; "selectionChange": "selectionChange"; "expansionChange": "expansionChange"; "columnResize": "columnResize"; }, never, never, true, never>;
376
408
  }
377
409
 
378
410
  /**
@@ -8,24 +8,25 @@ A token-driven, responsive data table component for Angular with sorting, virtua
8
8
 
9
9
  ### Inputs
10
10
 
11
- | Input | Type | Default | Description |
12
- | ------------------------- | ---------------------------------- | -------------- | ---------------------------------------------------------------------------------------------------- |
13
- | `columns` | `ColumnDef<T>[]` | `[]` | Column definitions describing headers, accessors, and behavior |
14
- | `rows` | `T[]` | `[]` | Data rows to render |
15
- | `config` | `Partial<TableConfig<T>>` | `{}` | Table configuration merged with defaults |
16
- | `loading` | `boolean` | `false` | Shows a loading overlay |
17
- | `sortState` | `SortState \| null` | `null` | Controlled sort state; `null` uses internal state |
18
- | `filterState` | `FilterState \| null` | `null` | Controlled filter state; `null` uses internal state |
19
- | `pageState` | `PageState \| null` | `null` | Controlled page state; `null` uses internal state |
20
- | `rowSelection` | `boolean` | `false` | Render a leading (or trailing) checkbox column with multi-row selection |
21
- | `selectedKeys` | `Set<unknown> \| null` | `null` | Controlled selection set; `null` uses internal state. Table never mutates the input — emits new Sets |
22
- | `getRowId` | `(row: T) => unknown` \| `null` | `null` | Required when `rowSelection` is `true`. Returns each row's stable identity key |
23
- | `selectionDisabled` | `boolean \| ((row: T) => boolean)` | `false` | Per-row opt-out. Disabled rows can't be selected and are skipped by the header "select all" |
24
- | `selectionColumnPosition` | `'start' \| 'end'` | `'start'` | Render the checkbox column at the leading or trailing edge |
25
- | `selectionLabel` | `string` | `'Select row'` | `aria-label` applied to each row checkbox |
26
- | `className` | `string` | `''` | Additional CSS classes for the host |
27
- | `id` | `string \| null` | `null` | Optional ID for the host element |
28
- | `ariaLabel` | `string \| null` | `null` | ARIA label for the table |
11
+ | Input | Type | Default | Description |
12
+ | ------------------------- | ---------------------------------------------------- | -------------- | ---------------------------------------------------------------------------------------------------- |
13
+ | `columns` | `ColumnDef<T>[]` | `[]` | Column definitions describing headers, accessors, and behavior |
14
+ | `rows` | `T[]` | `[]` | Data rows to render |
15
+ | `config` | `Partial<TableConfig<T>>` | `{}` | Table configuration merged with defaults |
16
+ | `loading` | `boolean` | `false` | Shows a loading overlay |
17
+ | `sortState` | `SortState \| null` | `null` | Controlled sort state; `null` uses internal state |
18
+ | `filterState` | `FilterState \| null` | `null` | Controlled filter state; `null` uses internal state |
19
+ | `pageState` | `PageState \| null` | `null` | Controlled page state; `null` uses internal state |
20
+ | `rowSelection` | `boolean` | `false` | Render a leading (or trailing) checkbox column with multi-row selection |
21
+ | `selectedKeys` | `Set<unknown> \| null` | `null` | Controlled selection set; `null` uses internal state. Table never mutates the input — emits new Sets |
22
+ | `getRowId` | `(row: T) => unknown` \| `null` | `null` | Required when `rowSelection` is `true`. Returns each row's stable identity key |
23
+ | `selectionDisabled` | `boolean \| ((row: T) => boolean)` | `false` | Per-row opt-out. Disabled rows can't be selected and are skipped by the header "select all" |
24
+ | `selectionColumnPosition` | `'start' \| 'end'` | `'start'` | Render the checkbox column at the leading or trailing edge |
25
+ | `selectionLabel` | `string` | `'Select row'` | `aria-label` applied to each row checkbox |
26
+ | `groupHeadingRenderer` | `TemplateRef<{ $implicit: string; row: T }> \| null` | `null` | Template for a group heading; omit to render the group key as text |
27
+ | `className` | `string` | `''` | Additional CSS classes for the host |
28
+ | `id` | `string \| null` | `null` | Optional ID for the host element |
29
+ | `ariaLabel` | `string \| null` | `null` | ARIA label for the table |
29
30
 
30
31
  ### Outputs
31
32
 
@@ -74,6 +75,7 @@ A token-driven, responsive data table component for Angular with sorting, virtua
74
75
  | `rowExpansion` | `boolean` | `false` | Enable row expansion |
75
76
  | `emptyMessage` | `string` | `'No data available'` | Message shown when no rows exist |
76
77
  | `trackBy` | `(index: number, row: T) => unknown` | `(index) => index` | Track-by function for row rendering |
78
+ | `groupBy` | `((row: T) => string \| null) \| null` | `null` | Group consecutive rows under a heading (see below) |
77
79
  | `virtualization` | `boolean \| 'auto'` | `'auto'` | Virtual scrolling mode |
78
80
  | `virtualizationThreshold` | `number` | `200` | Row count threshold for auto-activating virtualization |
79
81
  | `rowHeight` | `number` | `48` | Fixed row height in px (required for virtual scrolling) |
@@ -235,6 +237,36 @@ isLocked = (row: Order) => row.status === 'Closed';
235
237
  <fk-data-table [columns]="columns" [rows]="[]" [config]="{ emptyMessage: 'No records found' }" />
236
238
  ```
237
239
 
240
+ ### Row grouping
241
+
242
+ Rows sharing a group key render under a heading spanning the full table:
243
+
244
+ ```html
245
+ <fk-data-table [columns]="columns" [rows]="events" [config]="{ groupBy: byDay }" />
246
+ ```
247
+
248
+ ```ts
249
+ byDay = (row: Event) => this.formatDay(row.created_at);
250
+ ```
251
+
252
+ Return `null` from `groupBy` to leave a row ungrouped.
253
+
254
+ Grouping reads the rows in the order it is handed them — it does not reorder.
255
+ Sort by the same field you group on, or a group will open more than once.
256
+
257
+ For a heading richer than the key, pass a template:
258
+
259
+ ```html
260
+ <fk-data-table [columns]="columns" [rows]="events" [config]="{ groupBy: byDay }" [groupHeadingRenderer]="dayHeading" />
261
+
262
+ <ng-template #dayHeading let-key let-row="row"> {{ key }} <span class="muted">{{ row.created_at | date }}</span> </ng-template>
263
+ ```
264
+
265
+ > **Grouping and virtualization are mutually exclusive.** Virtual scrolling
266
+ > positions rows by multiplying an index by `rowHeight`, and a heading is not
267
+ > that tall. Setting `groupBy` turns virtualization off rather than misplacing
268
+ > every row after the first heading.
269
+
238
270
  ### Virtual scrolling
239
271
 
240
272
  Virtual scrolling activates automatically when row count exceeds `virtualizationThreshold` (default 200). Force it on or off:
@@ -341,6 +373,15 @@ Pinned columns stick to the left or right edge when scrolling horizontally.
341
373
  --fk-data-table-row-hover-bg
342
374
  --fk-data-table-viewport-height
343
375
 
376
+ // Group headings
377
+ --fk-data-table-group-heading-bg
378
+ --fk-data-table-group-heading-color
379
+ --fk-data-table-group-heading-font-size
380
+ --fk-data-table-group-heading-font-weight
381
+ --fk-data-table-group-heading-letter-spacing
382
+ --fk-data-table-group-heading-text-transform
383
+ --fk-data-table-group-heading-padding
384
+
344
385
  // Empty & Loading
345
386
  --fk-data-table-empty-padding
346
387
  --fk-data-table-empty-color