@mk-kit/ui 0.34.1 → 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.
@@ -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 };
@@ -1,7 +1,7 @@
1
1
  import * as _angular_core from '@angular/core';
2
2
  import { ElementRef, OnDestroy, TemplateRef } from '@angular/core';
3
3
  import * as _mk_kit_ui_core from '@mk-kit/ui/core';
4
- import { MkTone, MkSize } from '@mk-kit/ui/core';
4
+ import { MkPlacement, MkVariant, MkTone, MkSize } from '@mk-kit/ui/core';
5
5
 
6
6
  /**
7
7
  * A single tab + its panel. Declared inside `<mk-tabs>`; the projected content
@@ -231,6 +231,57 @@ declare class MkPagination {
231
231
  static ɵcmp: _angular_core.ɵɵComponentDeclaration<MkPagination, "mk-pagination", never, { "total": { "alias": "total"; "required": false; "isSignal": true; }; "pageSize": { "alias": "pageSize"; "required": false; "isSignal": true; }; "pageCount": { "alias": "pageCount"; "required": false; "isSignal": true; }; "page": { "alias": "page"; "required": false; "isSignal": true; }; "siblingCount": { "alias": "siblingCount"; "required": false; "isSignal": true; }; "boundaryCount": { "alias": "boundaryCount"; "required": false; "isSignal": true; }; "label": { "alias": "label"; "required": false; "isSignal": true; }; }, { "page": "pageChange"; }, never, never, true, never>;
232
232
  }
233
233
 
234
+ /**
235
+ * An item within an `<mk-menu>`. Renders as an ARIA `menuitem` with an optional
236
+ * icon slot (`[mkMenuItemIcon]`), disabled and danger states, and either emits
237
+ * `action` or navigates when `href` is set. Activating closes the menu.
238
+ *
239
+ * With `[mkSubmenuFor]` pointing at a nested `<mk-menu>` the item becomes a
240
+ * submenu trigger instead: it shows a chevron, exposes `aria-haspopup` /
241
+ * `aria-expanded`, opens the submenu beside itself on hover, ArrowRight,
242
+ * Enter, Space or click, and never emits `action`.
243
+ *
244
+ * ```html
245
+ * <mk-menu-item (action)="rename()">
246
+ * <svg mkMenuItemIcon>…</svg> Rename
247
+ * </mk-menu-item>
248
+ * <mk-menu-item danger (action)="remove()">Delete</mk-menu-item>
249
+ * <mk-menu-item href="/help">Help</mk-menu-item>
250
+ * <mk-menu-item [mkSubmenuFor]="more">More</mk-menu-item>
251
+ * ```
252
+ */
253
+ declare class MkMenuItem implements OnDestroy {
254
+ private readonly el;
255
+ private readonly document;
256
+ private readonly menu;
257
+ /** Prevent selection and focus. */
258
+ readonly disabled: _angular_core.InputSignalWithTransform<boolean, unknown>;
259
+ /** Destructive styling (e.g. Delete). */
260
+ readonly danger: _angular_core.InputSignalWithTransform<boolean, unknown>;
261
+ /** When set, activating the item navigates here. */
262
+ readonly href: _angular_core.InputSignal<string | undefined>;
263
+ /** A nested `mk-menu` this item opens as a submenu. */
264
+ readonly submenu: _angular_core.InputSignal<MkMenu | undefined>;
265
+ /** Emitted when the item is activated (not for disabled or submenu items). */
266
+ readonly action: _angular_core.OutputEmitterRef<void>;
267
+ private hoverTimer?;
268
+ activate(event?: Event): void;
269
+ /** Open the submenu beside this item; `focus` moves focus to its first item. */
270
+ openSubmenu(focus: boolean): void;
271
+ protected onMouseEnter(): void;
272
+ protected onMouseLeave(): void;
273
+ private cancelHover;
274
+ /** Move DOM focus to this item (roving focus). */
275
+ focusEl(): void;
276
+ /** Whether `node` lives inside this item. */
277
+ contains(node: Node | null): boolean;
278
+ /** Lowercased text content, used for typeahead matching. */
279
+ text(): string;
280
+ ngOnDestroy(): void;
281
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<MkMenuItem, never>;
282
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<MkMenuItem, "mk-menu-item", never, { "disabled": { "alias": "disabled"; "required": false; "isSignal": true; }; "danger": { "alias": "danger"; "required": false; "isSignal": true; }; "href": { "alias": "href"; "required": false; "isSignal": true; }; "submenu": { "alias": "mkSubmenuFor"; "required": false; "isSignal": true; }; }, { "action": "action"; }, never, ["[mkMenuItemIcon]", "*"], true, never>;
283
+ }
284
+
234
285
  /**
235
286
  * Dropdown menu implementing the ARIA menu pattern (roving focus, Arrow / Home
236
287
  * / End / typeahead, Enter/Space to activate). Attach it to a trigger with the
@@ -247,12 +298,33 @@ declare class MkPagination {
247
298
  * <mk-menu-item danger (action)="del()">Delete</mk-menu-item>
248
299
  * </mk-menu>
249
300
  * ```
301
+ *
302
+ * **Submenus.** Point an item at a nested menu with `[mkSubmenuFor]`; declare
303
+ * the nested `<mk-menu>` anywhere inside the parent menu. The submenu opens
304
+ * beside its item on hover (after a short delay), on ArrowRight / Enter /
305
+ * Space / click, and closes with ArrowLeft or Escape (returning focus to the
306
+ * item) — only that level, per the APG menu pattern. Activating any leaf item
307
+ * closes the whole chain. In RTL the submenu opens on the left and the arrow
308
+ * keys swap.
309
+ *
310
+ * ```html
311
+ * <mk-menu #menu>
312
+ * <mk-menu-item [mkSubmenuFor]="exportMenu">Export</mk-menu-item>
313
+ * <mk-menu #exportMenu>
314
+ * <mk-menu-item (action)="csv()">CSV</mk-menu-item>
315
+ * <mk-menu-item (action)="pdf()">PDF</mk-menu-item>
316
+ * </mk-menu>
317
+ * </mk-menu>
318
+ * ```
250
319
  */
251
320
  declare class MkMenu implements OnDestroy {
252
321
  private readonly document;
253
322
  private readonly injector;
254
323
  private readonly isBrowser;
324
+ /** The enclosing menu when this one is a submenu. */
325
+ private readonly parent;
255
326
  private readonly items;
327
+ private readonly panelRef;
256
328
  /** Stable id for `aria-controls` on the trigger. */
257
329
  readonly panelId: string;
258
330
  private readonly _open;
@@ -267,16 +339,27 @@ declare class MkMenu implements OnDestroy {
267
339
  x: number;
268
340
  y: number;
269
341
  } | undefined>;
342
+ /** Where the panel sits relative to its anchor (submenus open sideways). */
343
+ protected readonly placement: _angular_core.WritableSignal<MkPlacement>;
270
344
  private triggerEl;
271
345
  private typeahead;
272
346
  private typeaheadTimer?;
347
+ /** Submenus that are currently open under this menu. */
348
+ private readonly openChildren;
349
+ /**
350
+ * Whether this menu is a submenu of another `mk-menu`. Submenus open
351
+ * sideways, close a single level on Escape, and swap ArrowLeft/ArrowRight.
352
+ */
353
+ get isSubmenu(): boolean;
273
354
  /**
274
355
  * Open the menu anchored to `trigger`. `focus` picks the item that receives
275
356
  * focus once the panel is painted: `true`/`'first'` for the first enabled
276
357
  * item, `'last'` for the last (ArrowUp on a menu button, per the APG
277
358
  * menu-button pattern), `false` to leave focus where it is (mouse open).
359
+ * `placement` overrides the default `bottom-start` (submenus pass
360
+ * `right-start` / `left-start`).
278
361
  */
279
- open(trigger: HTMLElement, focus?: boolean | 'first' | 'last'): void;
362
+ open(trigger: HTMLElement, focus?: boolean | 'first' | 'last', placement?: MkPlacement): void;
280
363
  /**
281
364
  * Open the menu at viewport coordinates — e.g. a right-click point from a
282
365
  * `contextmenu` event. Positioning (and flip/shift back on-screen) is handled
@@ -284,8 +367,19 @@ declare class MkMenu implements OnDestroy {
284
367
  * item; `restoreFocusEl` (when provided) regains focus on close.
285
368
  */
286
369
  openAt(x: number, y: number, restoreFocusEl?: HTMLElement): void;
287
- /** Close the menu; optionally restore focus to the trigger. */
370
+ /**
371
+ * Close this menu (and any submenu open under it); optionally restore focus
372
+ * to the trigger — for a submenu that is the item it hangs off.
373
+ */
288
374
  close(restoreFocus?: boolean): void;
375
+ /**
376
+ * Close the whole menu chain from the root down — what activating a leaf
377
+ * item does, wherever in the tree it sits. Focus returns to the root
378
+ * trigger when `restoreFocus` is set.
379
+ */
380
+ closeAll(restoreFocus?: boolean): void;
381
+ /** Close every submenu open under this menu, except `keep`. */
382
+ closeChildren(keep?: MkMenu): void;
289
383
  /** Toggle open/closed from a trigger. */
290
384
  toggle(trigger: HTMLElement, focusFirst?: boolean): void;
291
385
  /**
@@ -296,9 +390,35 @@ declare class MkMenu implements OnDestroy {
296
390
  focusFirstItem(): void;
297
391
  /** Move focus to the last enabled item (ArrowUp from the trigger). */
298
392
  focusLastItem(): void;
393
+ /**
394
+ * Whether `node` lives inside this menu's panel or any submenu open under
395
+ * it. Submenu panels are separate top-layer elements, so a plain
396
+ * `contains` on the parent panel would treat clicks in them as outside.
397
+ */
398
+ containsTarget(node: Node | null): boolean;
399
+ /** Bound for the anchored panel's `keepOpenWhen` (stable identity). */
400
+ protected readonly keepOpenWhen: (target: Node) => boolean;
401
+ /**
402
+ * An item was hovered: close sibling submenus so only the hovered branch
403
+ * stays open (the item opens its own submenu after a delay).
404
+ */
405
+ itemHovered(item: MkMenuItem): void;
406
+ /** @internal */
407
+ childOpened(child: MkMenu): void;
408
+ /** @internal */
409
+ childClosed(child: MkMenu): void;
410
+ private root;
299
411
  /** Focus an item once the panel is in the top layer and painted. */
300
412
  private focusAfterOpen;
413
+ /**
414
+ * Whether the menu is laid out right-to-left (submenu side + arrow keys).
415
+ * Read from the trigger's nearest `dir` attribute — the panel itself lives
416
+ * in the top layer, outside any `dir` container — then from computed style.
417
+ */
418
+ private isRtl;
301
419
  protected onPanelKeydown(event: KeyboardEvent): void;
420
+ /** Placement for a submenu hanging off `item`, honouring text direction. */
421
+ submenuPlacement(): MkPlacement;
302
422
  private enabled;
303
423
  private activeItem;
304
424
  private currentIndex;
@@ -311,42 +431,6 @@ declare class MkMenu implements OnDestroy {
311
431
  static ɵcmp: _angular_core.ɵɵComponentDeclaration<MkMenu, "mk-menu", ["mkMenu"], {}, {}, ["items"], ["*"], true, never>;
312
432
  }
313
433
 
314
- /**
315
- * An item within an `<mk-menu>`. Renders as an ARIA `menuitem` with an optional
316
- * icon slot (`[mkMenuItemIcon]`), disabled and danger states, and either emits
317
- * `action` or navigates when `href` is set. Activating closes the menu.
318
- *
319
- * ```html
320
- * <mk-menu-item (action)="rename()">
321
- * <svg mkMenuItemIcon>…</svg> Rename
322
- * </mk-menu-item>
323
- * <mk-menu-item danger (action)="remove()">Delete</mk-menu-item>
324
- * <mk-menu-item href="/help">Help</mk-menu-item>
325
- * ```
326
- */
327
- declare class MkMenuItem {
328
- private readonly el;
329
- private readonly document;
330
- private readonly menu;
331
- /** Prevent selection and focus. */
332
- readonly disabled: _angular_core.InputSignalWithTransform<boolean, unknown>;
333
- /** Destructive styling (e.g. Delete). */
334
- readonly danger: _angular_core.InputSignalWithTransform<boolean, unknown>;
335
- /** When set, activating the item navigates here. */
336
- readonly href: _angular_core.InputSignal<string | undefined>;
337
- /** Emitted when the item is activated (not for disabled items). */
338
- readonly action: _angular_core.OutputEmitterRef<void>;
339
- activate(event?: Event): void;
340
- /** Move DOM focus to this item (roving focus). */
341
- focusEl(): void;
342
- /** Whether `node` lives inside this item. */
343
- contains(node: Node | null): boolean;
344
- /** Lowercased text content, used for typeahead matching. */
345
- text(): string;
346
- static ɵfac: _angular_core.ɵɵFactoryDeclaration<MkMenuItem, never>;
347
- static ɵcmp: _angular_core.ɵɵComponentDeclaration<MkMenuItem, "mk-menu-item", never, { "disabled": { "alias": "disabled"; "required": false; "isSignal": true; }; "danger": { "alias": "danger"; "required": false; "isSignal": true; }; "href": { "alias": "href"; "required": false; "isSignal": true; }; }, { "action": "action"; }, never, ["[mkMenuItemIcon]", "*"], true, never>;
348
- }
349
-
350
434
  /**
351
435
  * Turns its host button into a trigger for an `<mk-menu>`. Wires
352
436
  * `aria-haspopup="menu"` / `aria-expanded` / `aria-controls`, toggles on click,
@@ -371,6 +455,54 @@ declare class MkMenuTrigger {
371
455
  static ɵdir: _angular_core.ɵɵDirectiveDeclaration<MkMenuTrigger, "[mkMenuTriggerFor]", ["mkMenuTrigger"], { "menu": { "alias": "mkMenuTriggerFor"; "required": true; "isSignal": true; }; }, {}, never, never, true, never>;
372
456
  }
373
457
 
458
+ /**
459
+ * SplitButton — a primary action with an attached menu of alternatives.
460
+ * The main segment emits `action`; the chevron segment is a menu button
461
+ * (`mkMenuTriggerFor`) for the `mk-menu` passed in `[menu]`, with the full
462
+ * keyboard model of the menu trigger (ArrowDown / Enter / Space open and focus
463
+ * the first item, ArrowUp the last, Escape closes).
464
+ *
465
+ * Both segments share `variant`, `tone` and `size`; `disabled` disables both,
466
+ * `loading` shows the main segment's spinner and disables the chevron.
467
+ *
468
+ * ```html
469
+ * <mk-split-button [menu]="saveMenu" tone="primary" (action)="save()">
470
+ * Save
471
+ * </mk-split-button>
472
+ * <mk-menu #saveMenu>
473
+ * <mk-menu-item (action)="saveAs()">Save as…</mk-menu-item>
474
+ * <mk-menu-item (action)="saveTemplate()">Save as template</mk-menu-item>
475
+ * </mk-menu>
476
+ * ```
477
+ */
478
+ declare class MkSplitButton {
479
+ protected readonly i18n: _mk_kit_ui_core.MkI18nStrings;
480
+ /** The menu the chevron segment opens. */
481
+ readonly menu: _angular_core.InputSignal<MkMenu>;
482
+ /** Visual treatment shared by both segments. */
483
+ readonly variant: _angular_core.InputSignal<MkVariant>;
484
+ /** Semantic color tone shared by both segments. */
485
+ readonly tone: _angular_core.InputSignal<MkTone>;
486
+ /** Control size shared by both segments. */
487
+ readonly size: _angular_core.InputSignal<MkSize>;
488
+ /** Disable both segments. */
489
+ readonly disabled: _angular_core.InputSignalWithTransform<boolean, unknown>;
490
+ /** Spinner on the main segment; the menu segment is disabled meanwhile. */
491
+ readonly loading: _angular_core.InputSignalWithTransform<boolean, unknown>;
492
+ /** Stretch to the container width (the main segment grows). */
493
+ readonly fullWidth: _angular_core.InputSignalWithTransform<boolean, unknown>;
494
+ /** `type` of the main segment — `submit` to submit the enclosing form. */
495
+ readonly type: _angular_core.InputSignal<"button" | "submit">;
496
+ /** Accessible name of the chevron segment. */
497
+ readonly menuLabel: _angular_core.InputSignal<string>;
498
+ /** Emitted when the main segment is activated (not while disabled/loading). */
499
+ readonly action: _angular_core.OutputEmitterRef<void>;
500
+ protected readonly menuDisabled: _angular_core.Signal<boolean>;
501
+ protected onMain(): void;
502
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<MkSplitButton, never>;
503
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<MkSplitButton, "mk-split-button", never, { "menu": { "alias": "menu"; "required": true; "isSignal": true; }; "variant": { "alias": "variant"; "required": false; "isSignal": true; }; "tone": { "alias": "tone"; "required": false; "isSignal": true; }; "size": { "alias": "size"; "required": false; "isSignal": true; }; "disabled": { "alias": "disabled"; "required": false; "isSignal": true; }; "loading": { "alias": "loading"; "required": false; "isSignal": true; }; "fullWidth": { "alias": "fullWidth"; "required": false; "isSignal": true; }; "type": { "alias": "type"; "required": false; "isSignal": true; }; "menuLabel": { "alias": "menuLabel"; "required": false; "isSignal": true; }; }, { "action": "action"; }, never, ["*"], true, never>;
504
+ }
505
+
374
506
  /**
375
507
  * Admin dashboard layout with a fixed header, a collapsible/responsive sidebar
376
508
  * and a main content area. On small screens the sidebar becomes a focus-trapped
@@ -1165,5 +1297,5 @@ declare class MkScrollArea {
1165
1297
  static ɵcmp: _angular_core.ɵɵComponentDeclaration<MkScrollArea, "mk-scroll-area", never, { "maxHeight": { "alias": "maxHeight"; "required": false; "isSignal": true; }; "ariaLabel": { "alias": "ariaLabel"; "required": false; "isSignal": true; }; "orientation": { "alias": "orientation"; "required": false; "isSignal": true; }; "hideDelay": { "alias": "hideDelay"; "required": false; "isSignal": true; }; }, {}, never, ["*"], true, never>;
1166
1298
  }
1167
1299
 
1168
- export { MkAccordion, MkAccordionHeader, MkAccordionItem, MkAppShell, MkBackToTop, MkBreadcrumb, MkBreadcrumbItem, MkCommandPalette, MkDrawer, MkFab, MkFabAction, MkMenu, MkMenuItem, MkMenuTrigger, MkNavGroup, MkNavItem, MkNavList, MkPageHeader, MkPagination, MkScrollArea, MkSplitter, MkStep, MkStepper, MkTab, MkTabs, MkToolbar, MkTree };
1300
+ export { MkAccordion, MkAccordionHeader, MkAccordionItem, MkAppShell, MkBackToTop, MkBreadcrumb, MkBreadcrumbItem, MkCommandPalette, MkDrawer, MkFab, MkFabAction, MkMenu, MkMenuItem, MkMenuTrigger, MkNavGroup, MkNavItem, MkNavList, MkPageHeader, MkPagination, MkScrollArea, MkSplitButton, MkSplitter, MkStep, MkStepper, MkTab, MkTabs, MkToolbar, MkTree };
1169
1301
  export type { MkCommand, MkDrawerSide, MkFabPosition, MkPageItem, MkScrollAreaOrientation, MkSplitterOrientation, MkStepperOrientation, MkTabsVariant, MkTreeNode };