@mk-kit/ui 0.35.0 → 0.36.0

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.
@@ -14,6 +14,7 @@ export * from '@mk-kit/ui/feedback';
14
14
  export * from '@mk-kit/ui/rich-text';
15
15
  export * from '@mk-kit/ui/block-editor';
16
16
  export * from '@mk-kit/ui/context-menu';
17
+ export * from '@mk-kit/ui/layout';
17
18
  export * from '@mk-kit/ui/media';
18
19
 
19
20
  /*
@@ -1 +1 @@
1
- {"version":3,"file":"mk-kit-ui.mjs","sources":["../../../projects/mk-kit/src/public-api.ts","../../../projects/mk-kit/src/mk-kit-ui.ts"],"sourcesContent":["/*\n * Public API Surface of @mk-kit/ui.\n *\n * The library ships Material-style secondary entry points — import from the\n * group entries (`@mk-kit/ui/forms`, `@mk-kit/ui/table`, …) so a code-split\n * app only carries the groups each chunk uses. This root entry re-exports\n * everything for convenience; importing it eagerly pulls all groups into the\n * importing chunk.\n */\nexport * from '@mk-kit/ui/core';\nexport * from '@mk-kit/ui/icon';\nexport * from '@mk-kit/ui/chip';\nexport * from '@mk-kit/ui/checkbox';\nexport * from '@mk-kit/ui/button';\nexport * from '@mk-kit/ui/directives';\nexport * from '@mk-kit/ui/dnd';\nexport * from '@mk-kit/ui/navigation';\nexport * from '@mk-kit/ui/forms';\nexport * from '@mk-kit/ui/datetime';\nexport * from '@mk-kit/ui/table';\nexport * from '@mk-kit/ui/data';\nexport * from '@mk-kit/ui/feedback';\nexport * from '@mk-kit/ui/rich-text';\nexport * from '@mk-kit/ui/block-editor';\nexport * from '@mk-kit/ui/context-menu';\nexport * from '@mk-kit/ui/media';\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;AAAA;;;;;;;;AAQG;;ACRH;;AAEG"}
1
+ {"version":3,"file":"mk-kit-ui.mjs","sources":["../../../projects/mk-kit/src/public-api.ts","../../../projects/mk-kit/src/mk-kit-ui.ts"],"sourcesContent":["/*\n * Public API Surface of @mk-kit/ui.\n *\n * The library ships Material-style secondary entry points — import from the\n * group entries (`@mk-kit/ui/forms`, `@mk-kit/ui/table`, …) so a code-split\n * app only carries the groups each chunk uses. This root entry re-exports\n * everything for convenience; importing it eagerly pulls all groups into the\n * importing chunk.\n */\nexport * from '@mk-kit/ui/core';\nexport * from '@mk-kit/ui/icon';\nexport * from '@mk-kit/ui/chip';\nexport * from '@mk-kit/ui/checkbox';\nexport * from '@mk-kit/ui/button';\nexport * from '@mk-kit/ui/directives';\nexport * from '@mk-kit/ui/dnd';\nexport * from '@mk-kit/ui/navigation';\nexport * from '@mk-kit/ui/forms';\nexport * from '@mk-kit/ui/datetime';\nexport * from '@mk-kit/ui/table';\nexport * from '@mk-kit/ui/data';\nexport * from '@mk-kit/ui/feedback';\nexport * from '@mk-kit/ui/rich-text';\nexport * from '@mk-kit/ui/block-editor';\nexport * from '@mk-kit/ui/context-menu';\nexport * from '@mk-kit/ui/layout';\nexport * from '@mk-kit/ui/media';\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;AAAA;;;;;;;;AAQG;;ACRH;;AAEG"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mk-kit/ui",
3
- "version": "0.35.0",
3
+ "version": "0.36.0",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
@@ -107,6 +107,10 @@
107
107
  "types": "./types/mk-kit-ui-icon.d.ts",
108
108
  "default": "./fesm2022/mk-kit-ui-icon.mjs"
109
109
  },
110
+ "./layout": {
111
+ "types": "./types/mk-kit-ui-layout.d.ts",
112
+ "default": "./fesm2022/mk-kit-ui-layout.mjs"
113
+ },
110
114
  "./media": {
111
115
  "types": "./types/mk-kit-ui-media.d.ts",
112
116
  "default": "./fesm2022/mk-kit-ui-media.mjs"
package/styles/mk-kit.css CHANGED
@@ -381,6 +381,22 @@ body.mk-app {
381
381
  border: 0;
382
382
  }
383
383
 
384
+ /* Print helpers: `.mk-print-hidden` drops page chrome (toolbars, filters)
385
+ from paper; `.mk-print-only` shows something (a title, a date) only there.
386
+ Components ship their own `@media print` rules — mk-table prints as one
387
+ unbroken sheet with repeating headers and no controls. */
388
+ .mk-print-only {
389
+ display: none;
390
+ }
391
+
392
+ @media print {
393
+ .mk-print-hidden {
394
+ display: none !important;
395
+ }
396
+ .mk-print-only {
397
+ display: revert;
398
+ }
399
+ }
384
400
  /* mk-drag-handle — the grip a pointer drag must start on. MkDragHandle is a
385
401
  * DIRECTIVE (so it composes onto any element, including other components'
386
402
  * hosts like mk-icon), and directives cannot carry styles — hence the global
@@ -1,5 +1,5 @@
1
1
  import * as _angular_core from '@angular/core';
2
- import { InjectionToken, ComponentRef, Injector, OnDestroy, Type, AfterViewInit, ElementRef, Provider, Signal } from '@angular/core';
2
+ import { InjectionToken, OnDestroy, Signal, ComponentRef, Injector, Type, AfterViewInit, ElementRef, Provider } from '@angular/core';
3
3
  import { Observable } from 'rxjs';
4
4
  import { ValidationErrors } from '@angular/forms';
5
5
 
@@ -84,6 +84,67 @@ declare class MkThemeService {
84
84
  static ɵprov: _angular_core.ɵɵInjectableDeclaration<MkThemeService>;
85
85
  }
86
86
 
87
+ /** Named viewport ranges, mobile-first: each starts at its min-width. */
88
+ type MkBreakpoint = 'xs' | 'sm' | 'md' | 'lg' | 'xl' | '2xl';
89
+ /** Min-widths (px) at which each named breakpoint begins; `xs` is everything below `sm`. */
90
+ type MkBreakpoints = Record<Exclude<MkBreakpoint, 'xs'>, number>;
91
+ /** The default scale (same numbers as Tailwind, so mental models transfer). */
92
+ declare const MK_DEFAULT_BREAKPOINTS: MkBreakpoints;
93
+ /** Override the breakpoint scale app-wide: `{ provide: MK_BREAKPOINTS, useValue: { …, lg: 1100 } }`. */
94
+ declare const MK_BREAKPOINTS: InjectionToken<MkBreakpoints>;
95
+ /**
96
+ * A value that may differ per breakpoint: either the plain value or a
97
+ * mobile-first map, `{ xs: 1, md: 2, xl: 4 }`, where each key applies from
98
+ * that breakpoint up until the next key given.
99
+ */
100
+ type MkResponsive<T> = T | Partial<Record<MkBreakpoint, T>>;
101
+ /** True when `value` is a per-breakpoint map rather than a plain value. */
102
+ declare function mkIsResponsive<T>(value: MkResponsive<T>): value is Partial<Record<MkBreakpoint, T>>;
103
+ /**
104
+ * Reactive viewport breakpoints, one `matchMedia` listener per step.
105
+ *
106
+ * ```ts
107
+ * private readonly bp = inject(MkBreakpointService);
108
+ * readonly compact = this.bp.down('md'); // Signal<boolean>
109
+ * readonly columns = computed(() => this.bp.resolve({ xs: 1, md: 2, xl: 4 }));
110
+ * ```
111
+ *
112
+ * On the server (no `window`) everything reports `xs`.
113
+ */
114
+ declare class MkBreakpointService implements OnDestroy {
115
+ /** The breakpoint scale in effect (see {@link MK_BREAKPOINTS}). */
116
+ readonly breakpoints: MkBreakpoints;
117
+ private readonly document;
118
+ private readonly isBrowser;
119
+ /** `matches` per step, in scale order (`sm` first). */
120
+ private readonly steps;
121
+ private readonly cleanup;
122
+ private readonly queries;
123
+ private readonly ups;
124
+ private readonly downs;
125
+ /** The widest breakpoint the viewport currently satisfies. */
126
+ readonly current: Signal<MkBreakpoint>;
127
+ constructor();
128
+ /** True while the viewport is at least `bp` wide (`up('md')` = md, lg, xl, 2xl). */
129
+ up(bp: MkBreakpoint): Signal<boolean>;
130
+ /** True while the viewport is narrower than `bp` (`down('md')` = xs, sm). */
131
+ down(bp: MkBreakpoint): Signal<boolean>;
132
+ /** True while the viewport is at least `from` and narrower than `to`. */
133
+ between(from: MkBreakpoint, to: MkBreakpoint): Signal<boolean>;
134
+ /** Any media query as a signal, e.g. `observe('(orientation: portrait)')`. */
135
+ observe(query: string): Signal<boolean>;
136
+ /**
137
+ * Pick the entry of a responsive value for the current viewport —
138
+ * mobile-first, so `{ xs: 1, md: 2 }` gives 1 on `sm` and 2 on `xl`.
139
+ * A plain value is returned as is. Read inside a `computed` to track it.
140
+ */
141
+ resolve<T>(value: MkResponsive<T>): T | undefined;
142
+ ngOnDestroy(): void;
143
+ private watch;
144
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<MkBreakpointService, never>;
145
+ static ɵprov: _angular_core.ɵɵInjectableDeclaration<MkBreakpointService>;
146
+ }
147
+
87
148
  type MkAriaLivePoliteness = 'polite' | 'assertive';
88
149
  /**
89
150
  * Announces messages to assistive technology via a visually-hidden
@@ -780,6 +841,9 @@ interface MkI18nStrings {
780
841
  /** Row expander toggle labels. */
781
842
  expandRow: string;
782
843
  collapseRow: string;
844
+ /** Tree-row toggle labels (`childrenKey`). */
845
+ expandTreeRow: string;
846
+ collapseTreeRow: string;
783
847
  /** aria-label of a group header's toggle when the group is collapsed. */
784
848
  expandGroup: string;
785
849
  /** aria-label of a group header's toggle when the group is expanded. */
@@ -1117,5 +1181,5 @@ type MkErrorMessages = Readonly<Record<string, string | ((err: any) => string)>>
1117
1181
  */
1118
1182
  declare function mkFirstErrorMessage(errors: ValidationErrors | null | undefined, strings: MkValidationStrings, overrides?: MkErrorMessages): string | null;
1119
1183
 
1120
- export { MK_DEFAULT_DATE_NAMES, MK_DEFAULT_I18N, MK_DEFAULT_VALIDATION, MK_I18N, MK_OVERLAY_DATA, MkAnchoredPanel, MkFieldContext, MkFocusTrap, MkLiveAnnouncer, MkOverlayRef, MkOverlayService, MkThemeService, mkComputeAnchoredPosition, mkFirstErrorMessage, mkGetFocusable, mkHighlight, mkHighlightJson, mkUniqueId, mkValidatorChange, provideMkI18n };
1121
- export type { MkAnchoredPosition, MkAnchoredPositionOptions, MkAriaLivePoliteness, MkBlockEditorStrings, MkCodeLanguage, MkDateNames, MkDensity, MkErrorMessages, MkI18nStrings, MkOverlayConfig, MkPlacement, MkResolvedTheme, MkSize$1 as MkSize, MkSortAnnounceDirection, MkThemePreference, MkTone, MkValidationStrings, MkValidatorChangeRef, MkVariant };
1184
+ export { MK_BREAKPOINTS, MK_DEFAULT_BREAKPOINTS, MK_DEFAULT_DATE_NAMES, MK_DEFAULT_I18N, MK_DEFAULT_VALIDATION, MK_I18N, MK_OVERLAY_DATA, MkAnchoredPanel, MkBreakpointService, MkFieldContext, MkFocusTrap, MkLiveAnnouncer, MkOverlayRef, MkOverlayService, MkThemeService, mkComputeAnchoredPosition, mkFirstErrorMessage, mkGetFocusable, mkHighlight, mkHighlightJson, mkIsResponsive, mkUniqueId, mkValidatorChange, provideMkI18n };
1185
+ export type { MkAnchoredPosition, MkAnchoredPositionOptions, MkAriaLivePoliteness, MkBlockEditorStrings, MkBreakpoint, MkBreakpoints, MkCodeLanguage, MkDateNames, MkDensity, MkErrorMessages, MkI18nStrings, MkOverlayConfig, MkPlacement, MkResolvedTheme, MkResponsive, MkSize$1 as MkSize, MkSortAnnounceDirection, MkThemePreference, MkTone, MkValidationStrings, MkValidatorChangeRef, MkVariant };
@@ -37,7 +37,8 @@ declare class MkIcon {
37
37
 
38
38
  /**
39
39
  * IconRegistry — the name → SVG store behind `<mk-icon name="…">`. The built-in
40
- * {@link MK_DEFAULT_ICONS} set is registered on construction; add your own SVGs
40
+ * sets — the hand-made {@link MK_DEFAULT_ICONS} and the Lucide-derived
41
+ * {@link MK_EXTENDED_ICONS} — are registered on construction; add your own SVGs
41
42
  * (from a sprite, a design export, anything) with {@link register} /
42
43
  * {@link registerIcons}.
43
44
  *
@@ -105,4 +106,7 @@ declare const MK_MATERIAL_ICON_ALIASES: Readonly<Record<string, string>>;
105
106
  */
106
107
  declare function provideMkMaterialIcons(): EnvironmentProviders;
107
108
 
108
- export { MK_DEFAULT_ICONS, MK_MATERIAL_ICON_ALIASES, MkIcon, MkIconRegistry, provideMkMaterialIcons };
109
+ /** Name SVG markup map of the Lucide-derived icons registered by default. */
110
+ declare const MK_EXTENDED_ICONS: Readonly<Record<string, string>>;
111
+
112
+ export { MK_DEFAULT_ICONS, MK_EXTENDED_ICONS, MK_MATERIAL_ICON_ALIASES, MkIcon, MkIconRegistry, provideMkMaterialIcons };
@@ -0,0 +1,236 @@
1
+ import { MkResponsive, MkBreakpointService } from '@mk-kit/ui/core';
2
+ import * as _angular_core from '@angular/core';
3
+ import { InputSignal } from '@angular/core';
4
+
5
+ /**
6
+ * A spacing value: a step on the `--mk-space-*` scale (`0 1 2 3 4 5 6 8 10 12
7
+ * 16`, so `4` = `var(--mk-space-4)` = 16px and follows the density modes) or
8
+ * any CSS length such as `'1.5rem'` / `'clamp(1rem, 2vw, 2rem)'`.
9
+ */
10
+ type MkSpace = number | string;
11
+ /** Per-breakpoint spacing, e.g. `{ xs: 2, md: 4 }`. */
12
+ type MkResponsiveSpace = MkResponsive<MkSpace>;
13
+ /**
14
+ * A number, or a numeric string as it arrives from a plain attribute
15
+ * (`gap="4"`, `colSpan="2"`), as a number; anything else unchanged.
16
+ */
17
+ declare function mkNumeric<T>(value: T): T | number;
18
+ /** Translate a {@link MkSpace} into CSS (`null` when unset). */
19
+ declare function mkSpaceToCss(value: MkSpace | null | undefined): string | null;
20
+ /** Flex / grid main-axis distribution keywords, in plain words. */
21
+ type MkJustify = 'start' | 'center' | 'end' | 'between' | 'around' | 'evenly' | 'stretch';
22
+ /** Cross-axis alignment keywords. */
23
+ type MkAlign = 'start' | 'center' | 'end' | 'stretch' | 'baseline';
24
+ /** `justify-content` value for a {@link MkJustify} keyword (`null` when unset). */
25
+ declare function mkJustifyToCss(value: MkJustify | null | undefined): string | null;
26
+ /** `align-items` / `align-self` value for a {@link MkAlign} keyword (`null` when unset). */
27
+ declare function mkAlignToCss(value: MkAlign | null | undefined): string | null;
28
+
29
+ /** Flex direction keywords. */
30
+ type MkFlexDirection = 'row' | 'column' | 'row-reverse' | 'column-reverse';
31
+ /**
32
+ * Shared inputs and host style bindings of `mk-stack` and `mk-flex`.
33
+ * Responsive inputs re-resolve when the viewport crosses a breakpoint.
34
+ */
35
+ declare abstract class MkFlexBase {
36
+ protected readonly bp: MkBreakpointService;
37
+ /** Space between children — a `--mk-space-*` step, a CSS length, or a per-breakpoint map. */
38
+ abstract readonly gap: InputSignal<MkResponsiveSpace>;
39
+ /** Main-axis direction; may be responsive, e.g. `{ xs: 'column', md: 'row' }`. */
40
+ abstract readonly direction: InputSignal<MkResponsive<MkFlexDirection>>;
41
+ /** Cross-axis alignment of children. */
42
+ readonly align: InputSignal<MkResponsive<MkAlign> | null>;
43
+ /** Main-axis distribution of children. */
44
+ readonly justify: InputSignal<MkResponsive<MkJustify> | null>;
45
+ /** Let children wrap onto new lines. */
46
+ readonly wrap: _angular_core.InputSignalWithTransform<boolean, unknown>;
47
+ /** Render as `inline-flex` (sits in a line of text) instead of a block. */
48
+ readonly inline: _angular_core.InputSignalWithTransform<boolean, unknown>;
49
+ protected readonly directionCss: _angular_core.Signal<MkFlexDirection | null>;
50
+ protected readonly gapCss: _angular_core.Signal<string | null>;
51
+ protected readonly alignCss: _angular_core.Signal<string | null>;
52
+ protected readonly justifyCss: _angular_core.Signal<string | null>;
53
+ protected readonly wrapCss: _angular_core.Signal<"wrap" | null>;
54
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<MkFlexBase, never>;
55
+ static ɵdir: _angular_core.ɵɵDirectiveDeclaration<MkFlexBase, never, never, { "align": { "alias": "align"; "required": false; "isSignal": true; }; "justify": { "alias": "justify"; "required": false; "isSignal": true; }; "wrap": { "alias": "wrap"; "required": false; "isSignal": true; }; "inline": { "alias": "inline"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
56
+ }
57
+
58
+ /**
59
+ * Stack — children laid out one after another with a consistent gap, vertical
60
+ * by default. The everyday layout primitive: form sections, card bodies,
61
+ * sidebars, a row of buttons (`direction="row"`).
62
+ *
63
+ * ```html
64
+ * <mk-stack gap="4">
65
+ * <mk-input … />
66
+ * <mk-input … />
67
+ * <mk-stack direction="row" gap="2" justify="end">
68
+ * <button mkButton variant="ghost">Cancel</button>
69
+ * <button mkButton>Save</button>
70
+ * </mk-stack>
71
+ * </mk-stack>
72
+ *
73
+ * <!-- Stack on phones, side by side from md up -->
74
+ * <mk-stack [direction]="{ xs: 'column', md: 'row' }" [gap]="{ xs: 3, md: 6 }">…</mk-stack>
75
+ * ```
76
+ */
77
+ declare class MkStack extends MkFlexBase {
78
+ /** Space between children (default `4` = 16px). */
79
+ readonly gap: _angular_core.InputSignal<MkResponsiveSpace>;
80
+ /** Direction (default `column`). */
81
+ readonly direction: _angular_core.InputSignal<MkResponsive<MkFlexDirection>>;
82
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<MkStack, never>;
83
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<MkStack, "mk-stack", never, { "gap": { "alias": "gap"; "required": false; "isSignal": true; }; "direction": { "alias": "direction"; "required": false; "isSignal": true; }; }, {}, never, ["*"], true, never>;
84
+ }
85
+
86
+ /**
87
+ * Flex — a flexbox container with its options as inputs, horizontal by default
88
+ * and no gap unless asked. Reach for it when a stack's "one after another"
89
+ * isn't the point: space-between toolbars, centring, wrapping tag clouds.
90
+ * Children can fine-tune themselves with `mkFlexItem`.
91
+ *
92
+ * ```html
93
+ * <mk-flex align="center" justify="between" gap="3">
94
+ * <h2>Orders</h2>
95
+ * <button mkButton>New order</button>
96
+ * </mk-flex>
97
+ * <mk-flex wrap gap="2">@for (tag of tags; track tag) { <mk-chip>{{ tag }}</mk-chip> }</mk-flex>
98
+ * ```
99
+ */
100
+ declare class MkFlex extends MkFlexBase {
101
+ /** Space between children (default none). */
102
+ readonly gap: _angular_core.InputSignal<MkResponsiveSpace>;
103
+ /** Direction (default `row`). */
104
+ readonly direction: _angular_core.InputSignal<MkResponsive<MkFlexDirection>>;
105
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<MkFlex, never>;
106
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<MkFlex, "mk-flex", never, { "gap": { "alias": "gap"; "required": false; "isSignal": true; }; "direction": { "alias": "direction"; "required": false; "isSignal": true; }; }, {}, never, ["*"], true, never>;
107
+ }
108
+
109
+ /**
110
+ * Per-child flex options inside `mk-flex` / `mk-stack` (or any flex parent).
111
+ *
112
+ * ```html
113
+ * <mk-flex gap="3">
114
+ * <mk-input mkFlexItem grow /> <!-- takes the remaining width -->
115
+ * <button mkButton>Search</button>
116
+ * </mk-flex>
117
+ * ```
118
+ */
119
+ declare class MkFlexItem {
120
+ private readonly bp;
121
+ /** `flex-grow`; a bare `grow` attribute means `1`. */
122
+ readonly grow: _angular_core.InputSignalWithTransform<number | null, unknown>;
123
+ /** `flex-shrink`; a bare `shrink` attribute means `1`, `[shrink]="0"` pins the size. */
124
+ readonly shrink: _angular_core.InputSignalWithTransform<number | null, unknown>;
125
+ /** `flex-basis` — any CSS length or `auto`; may be responsive. */
126
+ readonly basis: _angular_core.InputSignal<MkResponsive<string> | null>;
127
+ /** Override the parent's `align` for this child. */
128
+ readonly alignSelf: _angular_core.InputSignal<MkResponsive<MkAlign> | null>;
129
+ /** Visual order; may be responsive (e.g. move a sidebar first on phones). */
130
+ readonly order: _angular_core.InputSignal<MkResponsive<string | number> | null>;
131
+ protected readonly growCss: _angular_core.Signal<number | null>;
132
+ protected readonly shrinkCss: _angular_core.Signal<number | null>;
133
+ protected readonly basisCss: _angular_core.Signal<string | null>;
134
+ protected readonly alignSelfCss: _angular_core.Signal<string | null>;
135
+ protected readonly orderCss: _angular_core.Signal<string | number | null>;
136
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<MkFlexItem, never>;
137
+ static ɵdir: _angular_core.ɵɵDirectiveDeclaration<MkFlexItem, "[mkFlexItem]", never, { "grow": { "alias": "grow"; "required": false; "isSignal": true; }; "shrink": { "alias": "shrink"; "required": false; "isSignal": true; }; "basis": { "alias": "basis"; "required": false; "isSignal": true; }; "alignSelf": { "alias": "alignSelf"; "required": false; "isSignal": true; }; "order": { "alias": "order"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
138
+ }
139
+
140
+ /** `justify-items` keywords. */
141
+ type MkGridJustify = 'start' | 'center' | 'end' | 'stretch';
142
+ /**
143
+ * Grid — a CSS grid with the common cases as inputs.
144
+ *
145
+ * - `columns` — a count (`3` → three equal tracks) or a raw
146
+ * `grid-template-columns` string (`'240px 1fr'`); responsive maps work for
147
+ * both: `[columns]="{ xs: 1, md: 2, xl: 4 }"`.
148
+ * - `minColumnWidth` — auto-responsive without breakpoints: as many
149
+ * `≥ 16rem` columns as fit (`auto-fill`; add `autoFit` to stretch the last
150
+ * row's items instead of leaving empty tracks). Wins over `columns`.
151
+ * - `gap`, `rowGap`, `columnGap` — `--mk-space-*` steps or CSS lengths.
152
+ *
153
+ * Children can span tracks with `mkGridItem`.
154
+ *
155
+ * ```html
156
+ * <mk-grid [columns]="{ xs: 1, md: 2, xl: 4 }" gap="4">
157
+ * @for (kpi of kpis; track kpi.id) { <mk-card>…</mk-card> }
158
+ * </mk-grid>
159
+ *
160
+ * <mk-grid minColumnWidth="16rem" gap="4">…cards…</mk-grid>
161
+ * ```
162
+ */
163
+ declare class MkGrid {
164
+ private readonly bp;
165
+ /** Column count or `grid-template-columns` value; responsive allowed (default `1`). */
166
+ readonly columns: _angular_core.InputSignal<MkResponsive<string | number>>;
167
+ /** Row count or `grid-template-rows` value; responsive allowed. */
168
+ readonly rows: _angular_core.InputSignal<MkResponsive<string | number> | null>;
169
+ /** Minimum column width for an auto-filling grid, e.g. `16rem`. Overrides `columns`. */
170
+ readonly minColumnWidth: _angular_core.InputSignal<MkResponsive<string> | null>;
171
+ /** With `minColumnWidth`: use `auto-fit` so items stretch when a row is short. */
172
+ readonly autoFit: _angular_core.InputSignalWithTransform<boolean, unknown>;
173
+ /** Gap between both rows and columns (default `4` = 16px). */
174
+ readonly gap: _angular_core.InputSignal<MkResponsiveSpace>;
175
+ /** Row gap override. */
176
+ readonly rowGap: _angular_core.InputSignal<MkResponsiveSpace | null>;
177
+ /** Column gap override. */
178
+ readonly columnGap: _angular_core.InputSignal<MkResponsiveSpace | null>;
179
+ /** `align-items` for every cell. */
180
+ readonly align: _angular_core.InputSignal<MkResponsive<MkAlign> | null>;
181
+ /** `justify-items` for every cell. */
182
+ readonly justify: _angular_core.InputSignal<MkResponsive<MkGridJustify> | null>;
183
+ /** `grid-auto-flow` (e.g. `dense` to backfill holes left by spanning items). */
184
+ readonly flow: _angular_core.InputSignal<"row" | "column" | "dense" | "row dense" | "column dense" | null>;
185
+ /** Render as `inline-grid`. */
186
+ readonly inline: _angular_core.InputSignalWithTransform<boolean, unknown>;
187
+ protected readonly columnsCss: _angular_core.Signal<string | null>;
188
+ protected readonly rowsCss: _angular_core.Signal<string | null>;
189
+ protected readonly rowGapCss: _angular_core.Signal<string | null>;
190
+ protected readonly columnGapCss: _angular_core.Signal<string | null>;
191
+ protected readonly alignCss: _angular_core.Signal<string | null>;
192
+ protected readonly justifyCss: _angular_core.Signal<MkGridJustify | null>;
193
+ protected readonly flowCss: _angular_core.Signal<"row" | "column" | "dense" | "row dense" | "column dense" | null>;
194
+ /** A track list: a count becomes `repeat(n, minmax(0, 1fr))` so long content cannot blow a column out. */
195
+ private static tracks;
196
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<MkGrid, never>;
197
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<MkGrid, "mk-grid", never, { "columns": { "alias": "columns"; "required": false; "isSignal": true; }; "rows": { "alias": "rows"; "required": false; "isSignal": true; }; "minColumnWidth": { "alias": "minColumnWidth"; "required": false; "isSignal": true; }; "autoFit": { "alias": "autoFit"; "required": false; "isSignal": true; }; "gap": { "alias": "gap"; "required": false; "isSignal": true; }; "rowGap": { "alias": "rowGap"; "required": false; "isSignal": true; }; "columnGap": { "alias": "columnGap"; "required": false; "isSignal": true; }; "align": { "alias": "align"; "required": false; "isSignal": true; }; "justify": { "alias": "justify"; "required": false; "isSignal": true; }; "flow": { "alias": "flow"; "required": false; "isSignal": true; }; "inline": { "alias": "inline"; "required": false; "isSignal": true; }; }, {}, never, ["*"], true, never>;
198
+ }
199
+
200
+ /**
201
+ * Per-cell placement inside `mk-grid`: span several tracks, or pin a start
202
+ * line. All inputs accept responsive maps, so a hero card can span the full
203
+ * row on phones and two of four columns on desktop.
204
+ *
205
+ * ```html
206
+ * <mk-grid [columns]="{ xs: 1, lg: 4 }" gap="4">
207
+ * <mk-card mkGridItem [colSpan]="{ xs: 1, lg: 2 }">Revenue</mk-card>
208
+ * …
209
+ * </mk-grid>
210
+ * ```
211
+ */
212
+ declare class MkGridItem {
213
+ private readonly bp;
214
+ /** Number of columns to span (`'all'` = the full row). */
215
+ readonly colSpan: _angular_core.InputSignal<MkResponsive<string | number> | null>;
216
+ /** Number of rows to span. */
217
+ readonly rowSpan: _angular_core.InputSignal<MkResponsive<string | number> | null>;
218
+ /** Column line to start at (1-based). */
219
+ readonly colStart: _angular_core.InputSignal<MkResponsive<string | number> | null>;
220
+ /** Row line to start at (1-based). */
221
+ readonly rowStart: _angular_core.InputSignal<MkResponsive<string | number> | null>;
222
+ /** `align-self` for this cell. */
223
+ readonly alignSelf: _angular_core.InputSignal<MkResponsive<"start" | "center" | "end" | "stretch"> | null>;
224
+ /** `justify-self` for this cell. */
225
+ readonly justifySelf: _angular_core.InputSignal<MkResponsive<"start" | "center" | "end" | "stretch"> | null>;
226
+ protected readonly columnCss: _angular_core.Signal<string | null>;
227
+ protected readonly rowCss: _angular_core.Signal<string | null>;
228
+ protected readonly alignSelfCss: _angular_core.Signal<"start" | "center" | "end" | "stretch" | null>;
229
+ protected readonly justifySelfCss: _angular_core.Signal<"start" | "center" | "end" | "stretch" | null>;
230
+ private static line;
231
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<MkGridItem, never>;
232
+ static ɵdir: _angular_core.ɵɵDirectiveDeclaration<MkGridItem, "[mkGridItem]", never, { "colSpan": { "alias": "colSpan"; "required": false; "isSignal": true; }; "rowSpan": { "alias": "rowSpan"; "required": false; "isSignal": true; }; "colStart": { "alias": "colStart"; "required": false; "isSignal": true; }; "rowStart": { "alias": "rowStart"; "required": false; "isSignal": true; }; "alignSelf": { "alias": "alignSelf"; "required": false; "isSignal": true; }; "justifySelf": { "alias": "justifySelf"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
233
+ }
234
+
235
+ export { MkFlex, MkFlexBase, MkFlexItem, MkGrid, MkGridItem, MkStack, mkAlignToCss, mkJustifyToCss, mkNumeric, mkSpaceToCss };
236
+ export type { MkAlign, MkFlexDirection, MkGridJustify, MkJustify, MkResponsiveSpace, MkSpace };
@@ -26,6 +26,65 @@ declare class MkTableRowDetail<T = unknown> {
26
26
  static ɵdir: _angular_core.ɵɵDirectiveDeclaration<MkTableRowDetail<any>, "[mkTableRowDetail]", never, {}, {}, never, never, true, never>;
27
27
  }
28
28
 
29
+ /**
30
+ * CSV export — turn rows into RFC 4180 text and hand it to the browser as a
31
+ * download. Framework-free so it also serves data that never touched a table.
32
+ */
33
+ /** A column to export: the row property, its header, and an optional formatter. */
34
+ interface MkCsvColumn<T = Record<string, unknown>> {
35
+ /** Property key on each row object supplying the cell value. */
36
+ key: string;
37
+ /** Header text; defaults to `key`. */
38
+ header?: string;
39
+ /** Formatter turning the raw value into cell text (same shape as `MkTableColumn.format`). */
40
+ format?: (value: unknown, row: T) => string;
41
+ }
42
+ interface MkCsvOptions {
43
+ /** Field separator (default `,`; use `;` for locales whose Excel expects it). */
44
+ delimiter?: string;
45
+ /** Emit the header row first (default `true`). */
46
+ header?: boolean;
47
+ /** Line terminator (default `\r\n`, per RFC 4180). */
48
+ newline?: string;
49
+ /**
50
+ * Prefix a UTF-8 byte-order mark (default `true`) so Excel reads accented
51
+ * characters correctly. Only affects the downloaded file / returned text.
52
+ */
53
+ bom?: boolean;
54
+ /**
55
+ * Neutralise spreadsheet formula injection (default `true`): a text cell
56
+ * starting with `=`, `+`, `-`, `@`, tab or CR is prefixed with `'` so a
57
+ * malicious value cannot execute when opened in Excel / Sheets. Numbers
58
+ * are never touched, so `-5` stays `-5`.
59
+ */
60
+ sanitize?: boolean;
61
+ /** Property holding child rows; when set, trees are flattened depth-first. */
62
+ childrenKey?: string;
63
+ }
64
+ /** Options for {@link mkExportCsv}. */
65
+ interface MkCsvExportOptions extends MkCsvOptions {
66
+ /** File name for the download (default `export.csv`; `.csv` is appended if missing). */
67
+ filename?: string;
68
+ }
69
+ /**
70
+ * Serialise `rows` as CSV text.
71
+ *
72
+ * Without `columns` every key of the first row is exported in its own order.
73
+ * Column formatters are applied, so what the user saw in the table is what
74
+ * lands in the file.
75
+ */
76
+ declare function mkToCsv<T>(rows: readonly T[], columns?: readonly MkCsvColumn<T>[], options?: MkCsvOptions): string;
77
+ /**
78
+ * Trigger a browser download of `text` as a file. No-op outside a DOM
79
+ * (server-side rendering); returns whether a download was started.
80
+ */
81
+ declare function mkDownloadText(text: string, filename: string, type?: string): boolean;
82
+ /**
83
+ * Serialise `rows` as CSV and download it. Returns the CSV text so callers
84
+ * can also keep it (tests, previews, uploads).
85
+ */
86
+ declare function mkExportCsv<T>(rows: readonly T[], columns?: readonly MkCsvColumn<T>[], options?: MkCsvExportOptions): string;
87
+
29
88
  /** Horizontal text alignment for a table column. */
30
89
  type MkTableAlign = 'start' | 'center' | 'end';
31
90
  /** Sort direction; `none` means unsorted. */
@@ -105,12 +164,28 @@ interface MkTableGroup<T = Record<string, unknown>> {
105
164
  rows: T[];
106
165
  }
107
166
  /** Payload emitted by {@link MkTable.groupToggle}. */
167
+ /** Payload of `(treeToggle)`: a parent row was expanded or collapsed. */
168
+ interface MkTreeToggle<T = Record<string, unknown>> {
169
+ /** The parent row. */
170
+ row: T;
171
+ /** Whether its child rows are now shown. */
172
+ expanded: boolean;
173
+ }
108
174
  interface MkGroupToggle {
109
175
  /** The toggled group's value. */
110
176
  key: unknown;
111
177
  /** Whether the group is now collapsed. */
112
178
  collapsed: boolean;
113
179
  }
180
+ /** Options for {@link MkTable.exportCsv}. */
181
+ interface MkTableExportOptions extends MkCsvExportOptions {
182
+ /** Export only the selected rows (default: every row). */
183
+ selectedOnly?: boolean;
184
+ /** Restrict to these column keys, in table order (default: every column). */
185
+ columns?: readonly string[];
186
+ /** Start the browser download (default `true`); `false` just returns the text. */
187
+ download?: boolean;
188
+ }
114
189
  /** One rendered tbody entry: either a group header or a data row. */
115
190
  type MkTableItem<T> = {
116
191
  kind: 'group';
@@ -118,6 +193,12 @@ type MkTableItem<T> = {
118
193
  } | {
119
194
  kind: 'row';
120
195
  row: T;
196
+ /** Nesting depth in tree mode (0 for roots and for flat tables). */
197
+ depth: number;
198
+ /** Whether the row has child rows (tree mode). */
199
+ hasChildren: boolean;
200
+ /** Whether its children are currently shown (tree mode). */
201
+ expanded: boolean;
121
202
  };
122
203
  /**
123
204
  * Table — a themed data table built on a native `<table>` for accessibility.
@@ -242,6 +323,15 @@ declare class MkTable<T = Record<string, unknown>> {
242
323
  readonly cellEdit: _angular_core.OutputEmitterRef<MkCellEdit<T>>;
243
324
  /** Emitted when a group header is expanded or collapsed. */
244
325
  readonly groupToggle: _angular_core.OutputEmitterRef<MkGroupToggle>;
326
+ /**
327
+ * Tree rows: the property on each row holding its child rows (`T[]`). When
328
+ * set, the table renders a tree grid — child rows are indented under their
329
+ * parent behind an expand toggle in the first column, sorting applies per
330
+ * sibling group, and ArrowRight / ArrowLeft on a row open / close it.
331
+ */
332
+ readonly childrenKey: _angular_core.InputSignal<string | null>;
333
+ /** Emitted when a parent row is expanded or collapsed (tree mode). */
334
+ readonly treeToggle: _angular_core.OutputEmitterRef<MkTreeToggle<T>>;
245
335
  /** User-set column widths (px), keyed by column key. */
246
336
  private readonly colWidths;
247
337
  /** User-set column order (keys); `null` = the input order. */
@@ -343,6 +433,8 @@ declare class MkTable<T = Record<string, unknown>> {
343
433
  private static readonly sortCollator;
344
434
  /** Data sorted by the active column, or the input order when unsorted. */
345
435
  protected readonly sortedData: _angular_core.Signal<T[]>;
436
+ /** Sort one sibling group by the active column (input order when unsorted). */
437
+ private sortRows;
346
438
  /** `aria-sort` value for a header cell. */
347
439
  protected ariaSort(col: MkTableColumn<T>): string | null;
348
440
  /** Glyph indicating a column's sort state. */
@@ -353,7 +445,7 @@ declare class MkTable<T = Record<string, unknown>> {
353
445
  protected cellText(row: T, col: MkTableColumn<T>): string;
354
446
  protected onSort(col: MkTableColumn<T>): void;
355
447
  protected onRowClick(row: T): void;
356
- /** Keyboard activation for clickable rows (Enter / Space). */
448
+ /** Keyboard activation for clickable rows (Enter / Space) and tree keys. */
357
449
  protected onRowKeydown(event: KeyboardEvent, row: T): void;
358
450
  /** Stable row identity for `@for` tracking (trackKey when set, else the row). */
359
451
  protected trackRow: (row: T) => unknown;
@@ -364,6 +456,12 @@ declare class MkTable<T = Record<string, unknown>> {
364
456
  private readonly selectedKeys;
365
457
  /** Whether `row` is currently selected. */
366
458
  protected isSelected(row: T): boolean;
459
+ /**
460
+ * Every data row, in display order, ignoring tree expansion — what
461
+ * "select all" and the header checkbox reason about. Equals `sortedData`
462
+ * for flat tables.
463
+ */
464
+ private readonly allRows;
367
465
  /** True when every visible row is selected. */
368
466
  protected readonly allSelected: _angular_core.Signal<boolean>;
369
467
  /** True when some — but not all — visible rows are selected. */
@@ -371,7 +469,7 @@ declare class MkTable<T = Record<string, unknown>> {
371
469
  private commitSelection;
372
470
  /** Toggle a single row's selection without triggering `rowClick`. */
373
471
  protected toggleRow(row: T): void;
374
- /** Select or deselect all visible rows. */
472
+ /** Select or deselect all rows (every tree row, expanded or not). */
375
473
  protected toggleAll(): void;
376
474
  /** Group values currently collapsed. */
377
475
  private readonly collapsedGroups;
@@ -382,6 +480,36 @@ declare class MkTable<T = Record<string, unknown>> {
382
480
  * rows when grouping is on, else just the sorted rows.
383
481
  */
384
482
  protected readonly displayItems: _angular_core.Signal<MkTableItem<T>[]>;
483
+ /**
484
+ * Append `rows` as render items. In tree mode each row is followed by its
485
+ * (sorted) children while it is expanded, one level deeper.
486
+ */
487
+ private pushRows;
488
+ /** The child rows of `row` (tree mode), or an empty list. */
489
+ private childrenOf;
490
+ /** Keys of parent rows whose children are shown. */
491
+ private readonly treeExpanded;
492
+ /** Whether a parent row's children are currently shown (tree mode). */
493
+ isTreeExpanded(row: T): boolean;
494
+ /** Show or hide a parent row's children (tree mode). */
495
+ toggleTreeRow(row: T, event?: Event): void;
496
+ /** Expand every parent row (tree mode). */
497
+ expandAllRows(): void;
498
+ /** Collapse every parent row (tree mode). */
499
+ collapseAllRows(): void;
500
+ /**
501
+ * The table's rows as CSV: current column order, column formatters applied,
502
+ * sorted the way they are shown, tree children flattened under their parent
503
+ * whether or not they are expanded. Downloads the file (default name
504
+ * `table.csv`) and returns the text.
505
+ */
506
+ exportCsv(options?: MkTableExportOptions): string;
507
+ private setTreeExpanded;
508
+ /**
509
+ * ArrowRight opens and ArrowLeft closes a parent row's children (swapped in
510
+ * RTL). Handled for keys pressed on the row itself or on its tree toggle.
511
+ */
512
+ protected onTreeKeydown(event: KeyboardEvent, row: T): boolean;
385
513
  /** `@for` identity: group headers by value, rows by {@link trackRow}. */
386
514
  protected trackItem: (item: MkTableItem<T>) => unknown;
387
515
  /** Whether a group is currently collapsed. */
@@ -400,7 +528,7 @@ declare class MkTable<T = Record<string, unknown>> {
400
528
  /** Toggle a row's detail panel, honouring `singleExpand`. */
401
529
  protected toggleExpand(row: T, event?: Event): void;
402
530
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<MkTable<any>, never>;
403
- static ɵcmp: _angular_core.ɵɵComponentDeclaration<MkTable<any>, "mk-table", never, { "columns": { "alias": "columns"; "required": true; "isSignal": true; }; "data": { "alias": "data"; "required": false; "isSignal": true; }; "stickyHeader": { "alias": "stickyHeader"; "required": false; "isSignal": true; }; "zebra": { "alias": "zebra"; "required": false; "isSignal": true; }; "hover": { "alias": "hover"; "required": false; "isSignal": true; }; "density": { "alias": "density"; "required": false; "isSignal": true; }; "stackAt": { "alias": "stackAt"; "required": false; "isSignal": true; }; "clickableRows": { "alias": "clickableRows"; "required": false; "isSignal": true; }; "emptyMessage": { "alias": "emptyMessage"; "required": false; "isSignal": true; }; "selectable": { "alias": "selectable"; "required": false; "isSignal": true; }; "selected": { "alias": "selected"; "required": false; "isSignal": true; }; "trackKey": { "alias": "trackKey"; "required": false; "isSignal": true; }; "rowClass": { "alias": "rowClass"; "required": false; "isSignal": true; }; "expandable": { "alias": "expandable"; "required": false; "isSignal": true; }; "singleExpand": { "alias": "singleExpand"; "required": false; "isSignal": true; }; "resizableColumns": { "alias": "resizableColumns"; "required": false; "isSignal": true; }; "reorderableColumns": { "alias": "reorderableColumns"; "required": false; "isSignal": true; }; "groupBy": { "alias": "groupBy"; "required": false; "isSignal": true; }; "groupLabel": { "alias": "groupLabel"; "required": false; "isSignal": true; }; }, { "selected": "selectedChange"; "sortChange": "sortChange"; "rowClick": "rowClick"; "selectionChange": "selectionChange"; "expandedChange": "expandedChange"; "columnResize": "columnResize"; "columnReorder": "columnReorder"; "cellEdit": "cellEdit"; "groupToggle": "groupToggle"; }, ["rowDetail", "cellTemplates"], ["[mkTableEmpty]"], true, never>;
531
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<MkTable<any>, "mk-table", never, { "columns": { "alias": "columns"; "required": true; "isSignal": true; }; "data": { "alias": "data"; "required": false; "isSignal": true; }; "stickyHeader": { "alias": "stickyHeader"; "required": false; "isSignal": true; }; "zebra": { "alias": "zebra"; "required": false; "isSignal": true; }; "hover": { "alias": "hover"; "required": false; "isSignal": true; }; "density": { "alias": "density"; "required": false; "isSignal": true; }; "stackAt": { "alias": "stackAt"; "required": false; "isSignal": true; }; "clickableRows": { "alias": "clickableRows"; "required": false; "isSignal": true; }; "emptyMessage": { "alias": "emptyMessage"; "required": false; "isSignal": true; }; "selectable": { "alias": "selectable"; "required": false; "isSignal": true; }; "selected": { "alias": "selected"; "required": false; "isSignal": true; }; "trackKey": { "alias": "trackKey"; "required": false; "isSignal": true; }; "rowClass": { "alias": "rowClass"; "required": false; "isSignal": true; }; "expandable": { "alias": "expandable"; "required": false; "isSignal": true; }; "singleExpand": { "alias": "singleExpand"; "required": false; "isSignal": true; }; "resizableColumns": { "alias": "resizableColumns"; "required": false; "isSignal": true; }; "reorderableColumns": { "alias": "reorderableColumns"; "required": false; "isSignal": true; }; "groupBy": { "alias": "groupBy"; "required": false; "isSignal": true; }; "groupLabel": { "alias": "groupLabel"; "required": false; "isSignal": true; }; "childrenKey": { "alias": "childrenKey"; "required": false; "isSignal": true; }; }, { "selected": "selectedChange"; "sortChange": "sortChange"; "rowClick": "rowClick"; "selectionChange": "selectionChange"; "expandedChange": "expandedChange"; "columnResize": "columnResize"; "columnReorder": "columnReorder"; "cellEdit": "cellEdit"; "groupToggle": "groupToggle"; "treeToggle": "treeToggle"; }, ["rowDetail", "cellTemplates"], ["[mkTableEmpty]"], true, never>;
404
532
  }
405
533
 
406
534
  /** Context handed to an `[mkTableCell]` template. */
@@ -735,5 +863,5 @@ declare class MkTableDataSource<T> {
735
863
  private clearSub;
736
864
  }
737
865
 
738
- export { MkSort, MkSortHeader, MkTable, MkTableCell, MkTableDataSource, MkTableRowDetail };
739
- export type { MkCellEdit, MkColumnResize, MkDataFetcher, MkDataPage, MkDataRequest, MkGroupToggle, MkSortChange, MkSortDirection, MkSortState, MkSortable, MkTableAlign, MkTableCellContext, MkTableColumn, MkTableDataSourceOptions, MkTableDensity, MkTableGroup };
866
+ export { MkSort, MkSortHeader, MkTable, MkTableCell, MkTableDataSource, MkTableRowDetail, mkDownloadText, mkExportCsv, mkToCsv };
867
+ export type { MkCellEdit, MkColumnResize, MkCsvColumn, MkCsvExportOptions, MkCsvOptions, MkDataFetcher, MkDataPage, MkDataRequest, MkGroupToggle, MkSortChange, MkSortDirection, MkSortState, MkSortable, MkTableAlign, MkTableCellContext, MkTableColumn, MkTableDataSourceOptions, MkTableDensity, MkTableExportOptions, MkTableGroup, MkTreeToggle };
@@ -14,4 +14,5 @@ export * from '@mk-kit/ui/feedback';
14
14
  export * from '@mk-kit/ui/rich-text';
15
15
  export * from '@mk-kit/ui/block-editor';
16
16
  export * from '@mk-kit/ui/context-menu';
17
+ export * from '@mk-kit/ui/layout';
17
18
  export * from '@mk-kit/ui/media';