@mk-kit/ui 0.36.0 → 0.38.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.
@@ -1,9 +1,9 @@
1
1
  import * as i0 from '@angular/core';
2
- import { inject, input, booleanAttribute, output, computed, ChangeDetectionStrategy, Component, ElementRef, ApplicationRef, EnvironmentInjector, DOCUMENT, PLATFORM_ID, createComponent, Directive, Injector, viewChild, signal, afterNextRender, numberAttribute, model, ViewEncapsulation, Injectable, InjectionToken, DestroyRef } from '@angular/core';
2
+ import { inject, input, booleanAttribute, output, computed, ChangeDetectionStrategy, Component, ElementRef, ApplicationRef, EnvironmentInjector, DOCUMENT, PLATFORM_ID, createComponent, Directive, Injector, viewChild, signal, afterNextRender, numberAttribute, model, DestroyRef, afterRenderEffect, ViewEncapsulation, Injectable, InjectionToken, effect } from '@angular/core';
3
3
  import { MK_I18N, mkUniqueId, mkComputeAnchoredPosition, mkGetFocusable, MkAnchoredPanel, MkOverlayRef, MK_OVERLAY_DATA, MkOverlayService } from '@mk-kit/ui/core';
4
4
  import { isPlatformBrowser } from '@angular/common';
5
5
  import { MkButton } from '@mk-kit/ui/button';
6
- import { MkBadge, MkEmptyState } from '@mk-kit/ui/data';
6
+ import { MkBadge, MkEmptyState, MkSpinner } from '@mk-kit/ui/data';
7
7
  import { MkIcon } from '@mk-kit/ui/icon';
8
8
  import { MkInput, MkFormField } from '@mk-kit/ui/forms';
9
9
 
@@ -1142,13 +1142,209 @@ class MkDialog {
1142
1142
  ...(ngDevMode ? [{ debugName: "titleId" }] : /* istanbul ignore next */ []));
1143
1143
  /** Hide the header close button. */
1144
1144
  hideClose = input(false, { ...(ngDevMode ? { debugName: "hideClose" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
1145
+ /**
1146
+ * Let the user move the dialog by dragging its header (pointer / touch) or
1147
+ * with the arrow keys on the grip that appears in the header. Double-click
1148
+ * the header or press Home on the grip to snap back to the centre.
1149
+ */
1150
+ draggable = input(false, { ...(ngDevMode ? { debugName: "draggable" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
1151
+ /**
1152
+ * Let the user resize the dialog from the corner grip (pointer / touch, or
1153
+ * arrow keys on the grip; Home resets). The panel never grows past the
1154
+ * viewport or shrinks below a usable minimum.
1155
+ */
1156
+ resizable = input(false, { ...(ngDevMode ? { debugName: "resizable" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
1157
+ destroyRef = inject(DestroyRef);
1158
+ /** The body-level overlay panel this dialog lives in (`null` when inline). */
1159
+ panelEl;
1160
+ /** Current drag offset from the centred position, px. */
1161
+ offset = { x: 0, y: 0 };
1145
1162
  constructor() {
1146
1163
  afterNextRender(() => this.wireLabelledBy());
1164
+ afterRenderEffect(() => {
1165
+ const draggable = this.draggable();
1166
+ const resizable = this.resizable();
1167
+ const panel = this.panel();
1168
+ if (!panel)
1169
+ return;
1170
+ panel.classList.toggle('mk-dialog-panel--draggable', draggable);
1171
+ panel.classList.toggle('mk-dialog-panel--resizable', resizable);
1172
+ });
1173
+ const view = this.host.nativeElement.ownerDocument.defaultView;
1174
+ if (view) {
1175
+ const onResize = () => this.clampIntoView();
1176
+ view.addEventListener('resize', onResize);
1177
+ this.destroyRef.onDestroy(() => view.removeEventListener('resize', onResize));
1178
+ }
1147
1179
  }
1148
1180
  /** Close the surrounding overlay, if any. */
1149
1181
  close() {
1150
1182
  this.overlayRef?.close();
1151
1183
  }
1184
+ /** Snap back to the centred, preset-sized panel. */
1185
+ reset() {
1186
+ const panel = this.panel();
1187
+ if (!panel)
1188
+ return;
1189
+ this.offset = { x: 0, y: 0 };
1190
+ panel.style.translate = '';
1191
+ panel.style.width = '';
1192
+ panel.style.height = '';
1193
+ }
1194
+ // --- Drag / resize -----------------------------------------------------------
1195
+ onHeaderPointerDown(event) {
1196
+ if (!this.draggable())
1197
+ return;
1198
+ const target = event.target;
1199
+ // Controls in the header keep their own pointer behaviour.
1200
+ if (target.closest('button, a, input, textarea, select, [contenteditable="true"]') && !target.closest('.mk-dialog__grip'))
1201
+ return;
1202
+ this.startPointer(event, 'move');
1203
+ }
1204
+ onGripPointerDown(event) {
1205
+ this.startPointer(event, 'move');
1206
+ }
1207
+ onResizerPointerDown(event) {
1208
+ this.startPointer(event, 'resize');
1209
+ }
1210
+ onHeaderDoubleClick(event) {
1211
+ if (!this.draggable())
1212
+ return;
1213
+ if (event.target.closest('button, a, input, textarea, select'))
1214
+ return;
1215
+ this.reset();
1216
+ }
1217
+ onGripKeydown(event) {
1218
+ const step = event.shiftKey ? 32 : 8;
1219
+ const delta = MkDialog.arrowDelta(event.key, step);
1220
+ if (delta) {
1221
+ event.preventDefault();
1222
+ this.moveTo(this.offset.x + delta.x, this.offset.y + delta.y);
1223
+ }
1224
+ else if (event.key === 'Home') {
1225
+ event.preventDefault();
1226
+ this.reset();
1227
+ }
1228
+ }
1229
+ onResizerKeydown(event) {
1230
+ const step = event.shiftKey ? 64 : 16;
1231
+ const delta = MkDialog.arrowDelta(event.key, step);
1232
+ const panel = this.panel();
1233
+ if (delta && panel) {
1234
+ event.preventDefault();
1235
+ const rect = panel.getBoundingClientRect();
1236
+ const rtl = this.isRtl(panel);
1237
+ this.resizeTo(rect.width + (rtl ? -delta.x : delta.x), rect.height + delta.y);
1238
+ }
1239
+ else if (event.key === 'Home') {
1240
+ event.preventDefault();
1241
+ this.reset();
1242
+ }
1243
+ }
1244
+ static arrowDelta(key, step) {
1245
+ switch (key) {
1246
+ case 'ArrowLeft':
1247
+ return { x: -step, y: 0 };
1248
+ case 'ArrowRight':
1249
+ return { x: step, y: 0 };
1250
+ case 'ArrowUp':
1251
+ return { x: 0, y: -step };
1252
+ case 'ArrowDown':
1253
+ return { x: 0, y: step };
1254
+ default:
1255
+ return null;
1256
+ }
1257
+ }
1258
+ startPointer(event, mode) {
1259
+ const panel = this.panel();
1260
+ if (!panel || event.button !== 0)
1261
+ return;
1262
+ event.preventDefault();
1263
+ const handle = event.currentTarget;
1264
+ const startX = event.clientX;
1265
+ const startY = event.clientY;
1266
+ const startOffset = { ...this.offset };
1267
+ const rect = panel.getBoundingClientRect();
1268
+ const rtl = this.isRtl(panel);
1269
+ handle.setPointerCapture?.(event.pointerId);
1270
+ this.markMoved(panel);
1271
+ panel.classList.add('mk-dialog-panel--dragging');
1272
+ const onMove = (e) => {
1273
+ const dx = e.clientX - startX;
1274
+ const dy = e.clientY - startY;
1275
+ if (mode === 'move')
1276
+ this.moveTo(startOffset.x + dx, startOffset.y + dy);
1277
+ else
1278
+ this.resizeTo(rect.width + (rtl ? -dx : dx), rect.height + dy);
1279
+ };
1280
+ const onEnd = () => {
1281
+ handle.removeEventListener('pointermove', onMove);
1282
+ handle.removeEventListener('pointerup', onEnd);
1283
+ handle.removeEventListener('pointercancel', onEnd);
1284
+ handle.releasePointerCapture?.(event.pointerId);
1285
+ panel.classList.remove('mk-dialog-panel--dragging');
1286
+ };
1287
+ handle.addEventListener('pointermove', onMove);
1288
+ handle.addEventListener('pointerup', onEnd);
1289
+ handle.addEventListener('pointercancel', onEnd);
1290
+ }
1291
+ /** Move the panel to `(x, y)` px from its centred spot, kept inside the viewport. */
1292
+ moveTo(x, y) {
1293
+ const panel = this.panel();
1294
+ const view = panel?.ownerDocument.defaultView;
1295
+ if (!panel || !view)
1296
+ return;
1297
+ const rect = panel.getBoundingClientRect();
1298
+ // Where the panel sits with no offset at all.
1299
+ const baseLeft = rect.left - this.offset.x;
1300
+ const baseTop = rect.top - this.offset.y;
1301
+ const maxX = Math.max(-baseLeft, view.innerWidth - rect.width - baseLeft);
1302
+ const maxY = Math.max(-baseTop, view.innerHeight - rect.height - baseTop);
1303
+ this.offset = {
1304
+ x: Math.min(Math.max(x, -baseLeft), maxX),
1305
+ y: Math.min(Math.max(y, -baseTop), maxY),
1306
+ };
1307
+ this.markMoved(panel);
1308
+ panel.style.translate = `${Math.round(this.offset.x)}px ${Math.round(this.offset.y)}px`;
1309
+ }
1310
+ /** Resize the panel to `w × h` px within [minimum, viewport]. */
1311
+ resizeTo(w, h) {
1312
+ const panel = this.panel();
1313
+ const view = panel?.ownerDocument.defaultView;
1314
+ if (!panel || !view)
1315
+ return;
1316
+ const width = Math.min(Math.max(w, 240), view.innerWidth);
1317
+ const height = Math.min(Math.max(h, 160), view.innerHeight);
1318
+ this.markMoved(panel);
1319
+ panel.style.width = `${Math.round(width)}px`;
1320
+ panel.style.height = `${Math.round(height)}px`;
1321
+ panel.style.maxWidth = '100vw';
1322
+ panel.style.maxHeight = '100vh';
1323
+ this.clampIntoView();
1324
+ }
1325
+ /**
1326
+ * Once the user takes hold, the entrance animation must never run again —
1327
+ * toggling `animation` back on (as a transient class would) restarts it,
1328
+ * so the panel would blink after every drag. The class is never removed.
1329
+ */
1330
+ markMoved(panel) {
1331
+ panel.classList.add('mk-dialog-panel--moved');
1332
+ }
1333
+ clampIntoView() {
1334
+ if (this.offset.x === 0 && this.offset.y === 0)
1335
+ return;
1336
+ this.moveTo(this.offset.x, this.offset.y);
1337
+ }
1338
+ isRtl(panel) {
1339
+ const view = panel.ownerDocument.defaultView;
1340
+ return (view?.getComputedStyle(panel).direction ?? 'ltr') === 'rtl';
1341
+ }
1342
+ panel() {
1343
+ if (this.panelEl === undefined) {
1344
+ this.panelEl = this.host.nativeElement.closest('.mk-overlay-panel');
1345
+ }
1346
+ return this.panelEl;
1347
+ }
1152
1348
  /** Point the owning dialog panel's `aria-labelledby` at the title element. */
1153
1349
  wireLabelledBy() {
1154
1350
  const el = this.host.nativeElement;
@@ -1163,12 +1359,12 @@ class MkDialog {
1163
1359
  panel.setAttribute('aria-labelledby', titleEl.id);
1164
1360
  }
1165
1361
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.7", ngImport: i0, type: MkDialog, deps: [], target: i0.ɵɵFactoryTarget.Component });
1166
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.7", type: MkDialog, isStandalone: true, selector: "mk-dialog", inputs: { dialogTitle: { classPropertyName: "dialogTitle", publicName: "dialogTitle", isSignal: true, isRequired: false, transformFunction: null }, titleId: { classPropertyName: "titleId", publicName: "titleId", isSignal: true, isRequired: false, transformFunction: null }, hideClose: { classPropertyName: "hideClose", publicName: "hideClose", isSignal: true, isRequired: false, transformFunction: null } }, host: { classAttribute: "mk-dialog" }, ngImport: i0, template: "<header class=\"mk-dialog__header\">\n <div class=\"mk-dialog__heading\">\n <ng-content select=\"[mkDialogHeader], mk-dialog-title\" />\n @if (dialogTitle(); as heading) {\n <h2 class=\"mk-dialog__title\" [id]=\"titleId()\">{{ heading }}</h2>\n }\n </div>\n @if (!hideClose()) {\n <button\n type=\"button\"\n class=\"mk-dialog__close\"\n [attr.aria-label]=\"i18n.close\"\n (click)=\"close()\"\n >\n <svg viewBox=\"0 0 16 16\" width=\"16\" height=\"16\" aria-hidden=\"true\" focusable=\"false\">\n <path\n d=\"M4 4l8 8M12 4l-8 8\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"1.5\"\n stroke-linecap=\"round\"\n />\n </svg>\n </button>\n }\n</header>\n\n<div class=\"mk-dialog__body\">\n <ng-content />\n</div>\n\n<footer class=\"mk-dialog__footer\">\n <ng-content select=\"[mkDialogFooter]\" />\n</footer>\n", styles: ["@charset \"UTF-8\";.mk-dialog{display:flex;flex-direction:column;min-height:0;max-height:inherit;flex:1 1 auto;font-family:var(--mk-font-sans)}.mk-dialog__header{position:sticky;top:0;z-index:1;display:flex;align-items:flex-start;gap:var(--mk-space-3);flex-shrink:0;padding:var(--mk-space-4) var(--mk-space-5);border-bottom:var(--mk-border-width) solid var(--mk-border-subtle);background-color:var(--mk-surface)}.mk-dialog__heading{flex:1;min-width:0;display:flex;flex-direction:column;gap:var(--mk-space-1)}.mk-dialog__title{margin:0;min-width:0;overflow-wrap:anywhere;font-size:var(--mk-font-size-lg);font-weight:var(--mk-font-weight-semibold);line-height:var(--mk-line-height-snug);color:var(--mk-text)}.mk-dialog__body{flex:1 1 auto;min-height:0;overflow-y:auto;overscroll-behavior:contain;padding:var(--mk-space-5);font-size:var(--mk-font-size-md);line-height:var(--mk-line-height-normal);color:var(--mk-text)}.mk-dialog__footer{position:sticky;bottom:0;flex-shrink:0;display:flex;align-items:center;justify-content:flex-end;gap:var(--mk-space-2);padding:var(--mk-space-4) var(--mk-space-5);border-top:var(--mk-border-width) solid var(--mk-border-subtle);background-color:var(--mk-surface)}.mk-dialog__footer:empty{display:none}.mk-dialog__footer>*{display:flex;flex:1 1 auto;align-items:center;justify-content:flex-end;gap:var(--mk-space-2);flex-wrap:wrap}.mk-dialog__footer>[data-align=start]{justify-content:flex-start}.mk-dialog__footer>[data-align=center]{justify-content:center}.mk-dialog__footer>[data-align=between]{justify-content:space-between}.mk-dialog__close{flex-shrink:0;display:inline-flex;align-items:center;justify-content:center;width:2rem;height:2rem;margin:-4px -4px 0 0;padding:0;background:transparent;border:0;border-radius:var(--mk-radius-sm);color:var(--mk-text-muted);cursor:pointer;transition:color var(--mk-duration-fast) var(--mk-ease-standard),background-color var(--mk-duration-fast) var(--mk-ease-standard)}.mk-dialog__close:hover{color:var(--mk-text);background-color:var(--mk-hover-overlay)}.mk-dialog__close:focus-visible{color:var(--mk-text);outline:var(--mk-focus-ring-width) solid var(--mk-focus-ring);outline-offset:var(--mk-focus-ring-offset)}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
1362
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.7", type: MkDialog, isStandalone: true, selector: "mk-dialog", inputs: { dialogTitle: { classPropertyName: "dialogTitle", publicName: "dialogTitle", isSignal: true, isRequired: false, transformFunction: null }, titleId: { classPropertyName: "titleId", publicName: "titleId", isSignal: true, isRequired: false, transformFunction: null }, hideClose: { classPropertyName: "hideClose", publicName: "hideClose", isSignal: true, isRequired: false, transformFunction: null }, draggable: { classPropertyName: "draggable", publicName: "draggable", isSignal: true, isRequired: false, transformFunction: null }, resizable: { classPropertyName: "resizable", publicName: "resizable", isSignal: true, isRequired: false, transformFunction: null } }, host: { classAttribute: "mk-dialog" }, ngImport: i0, template: "<header\n class=\"mk-dialog__header\"\n [class.mk-dialog__header--draggable]=\"draggable()\"\n (pointerdown)=\"onHeaderPointerDown($event)\"\n (dblclick)=\"onHeaderDoubleClick($event)\"\n>\n @if (draggable()) {\n <button\n type=\"button\"\n class=\"mk-dialog__grip\"\n [attr.aria-label]=\"i18n.dialogMove\"\n [title]=\"i18n.dialogMove\"\n (pointerdown)=\"onGripPointerDown($event)\"\n (keydown)=\"onGripKeydown($event)\"\n >\n <svg viewBox=\"0 0 16 16\" width=\"16\" height=\"16\" aria-hidden=\"true\" focusable=\"false\">\n <circle cx=\"5.5\" cy=\"3.5\" r=\"1.3\" fill=\"currentColor\" />\n <circle cx=\"10.5\" cy=\"3.5\" r=\"1.3\" fill=\"currentColor\" />\n <circle cx=\"5.5\" cy=\"8\" r=\"1.3\" fill=\"currentColor\" />\n <circle cx=\"10.5\" cy=\"8\" r=\"1.3\" fill=\"currentColor\" />\n <circle cx=\"5.5\" cy=\"12.5\" r=\"1.3\" fill=\"currentColor\" />\n <circle cx=\"10.5\" cy=\"12.5\" r=\"1.3\" fill=\"currentColor\" />\n </svg>\n </button>\n }\n <div class=\"mk-dialog__heading\">\n <ng-content select=\"[mkDialogHeader], mk-dialog-title\" />\n @if (dialogTitle(); as heading) {\n <h2 class=\"mk-dialog__title\" [id]=\"titleId()\">{{ heading }}</h2>\n }\n </div>\n @if (!hideClose()) {\n <button\n type=\"button\"\n class=\"mk-dialog__close\"\n [attr.aria-label]=\"i18n.close\"\n (click)=\"close()\"\n >\n <svg viewBox=\"0 0 16 16\" width=\"16\" height=\"16\" aria-hidden=\"true\" focusable=\"false\">\n <path\n d=\"M4 4l8 8M12 4l-8 8\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"1.5\"\n stroke-linecap=\"round\"\n />\n </svg>\n </button>\n }\n</header>\n\n<div class=\"mk-dialog__body\">\n <ng-content />\n</div>\n\n<footer class=\"mk-dialog__footer\">\n <ng-content select=\"[mkDialogFooter]\" />\n</footer>\n\n@if (resizable()) {\n <button\n type=\"button\"\n class=\"mk-dialog__resizer\"\n [attr.aria-label]=\"i18n.dialogResize\"\n [title]=\"i18n.dialogResize\"\n (pointerdown)=\"onResizerPointerDown($event)\"\n (keydown)=\"onResizerKeydown($event)\"\n >\n <svg viewBox=\"0 0 16 16\" width=\"16\" height=\"16\" aria-hidden=\"true\" focusable=\"false\">\n <path d=\"M14 8L8 14M14 12l-2 2\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" />\n </svg>\n </button>\n}\n", styles: ["@charset \"UTF-8\";.mk-dialog{display:flex;flex-direction:column;min-height:0;max-height:inherit;flex:1 1 auto;font-family:var(--mk-font-sans)}.mk-dialog__header{position:sticky;top:0;z-index:1;display:flex;align-items:flex-start;gap:var(--mk-space-3);flex-shrink:0;padding:var(--mk-space-4) var(--mk-space-5);border-bottom:var(--mk-border-width) solid var(--mk-border-subtle);background-color:var(--mk-surface)}.mk-dialog__heading{flex:1;min-width:0;display:flex;flex-direction:column;gap:var(--mk-space-1)}.mk-dialog__title{margin:0;min-width:0;overflow-wrap:anywhere;font-size:var(--mk-font-size-lg);font-weight:var(--mk-font-weight-semibold);line-height:var(--mk-line-height-snug);color:var(--mk-text)}.mk-dialog__body{flex:1 1 auto;min-height:0;overflow-y:auto;overscroll-behavior:contain;padding:var(--mk-space-5);font-size:var(--mk-font-size-md);line-height:var(--mk-line-height-normal);color:var(--mk-text)}.mk-dialog__footer{position:sticky;bottom:0;flex-shrink:0;display:flex;align-items:center;justify-content:flex-end;gap:var(--mk-space-2);padding:var(--mk-space-4) var(--mk-space-5);border-top:var(--mk-border-width) solid var(--mk-border-subtle);background-color:var(--mk-surface)}.mk-dialog__footer:empty{display:none}.mk-dialog__footer>*{display:flex;flex:1 1 auto;align-items:center;justify-content:flex-end;gap:var(--mk-space-2);flex-wrap:wrap}.mk-dialog__footer>[data-align=start]{justify-content:flex-start}.mk-dialog__footer>[data-align=center]{justify-content:center}.mk-dialog__footer>[data-align=between]{justify-content:space-between}.mk-dialog__close{flex-shrink:0;display:inline-flex;align-items:center;justify-content:center;width:2rem;height:2rem;margin:-4px -4px 0 0;padding:0;background:transparent;border:0;border-radius:var(--mk-radius-sm);color:var(--mk-text-muted);cursor:pointer;transition:color var(--mk-duration-fast) var(--mk-ease-standard),background-color var(--mk-duration-fast) var(--mk-ease-standard)}.mk-dialog__close:hover{color:var(--mk-text);background-color:var(--mk-hover-overlay)}.mk-dialog__close:focus-visible{color:var(--mk-text);outline:var(--mk-focus-ring-width) solid var(--mk-focus-ring);outline-offset:var(--mk-focus-ring-offset)}.mk-dialog__header--draggable{cursor:move;touch-action:none;-webkit-user-select:none;user-select:none}.mk-dialog__grip,.mk-dialog__resizer{display:inline-flex;align-items:center;justify-content:center;padding:0;border:0;background:transparent;color:var(--mk-text-muted);cursor:move;touch-action:none}.mk-dialog__grip:hover,.mk-dialog__resizer:hover{color:var(--mk-text)}.mk-dialog__grip:focus-visible,.mk-dialog__resizer:focus-visible{outline:var(--mk-focus-ring-width) solid var(--mk-focus-ring);outline-offset:var(--mk-focus-ring-offset)}.mk-dialog__grip{flex:none;width:1.5rem;height:1.5rem;margin-inline-start:calc(-1 * var(--mk-space-2));border-radius:var(--mk-radius-sm)}.mk-dialog__resizer{position:absolute;inset-inline-end:0;bottom:0;width:1.25rem;height:1.25rem;cursor:nwse-resize;border-start-start-radius:var(--mk-radius-sm)}[dir=rtl] .mk-dialog__resizer,.mk-dialog-panel:dir(rtl) .mk-dialog__resizer{cursor:nesw-resize;transform:scaleX(-1)}.mk-dialog-panel--moved{animation:none}.mk-dialog-panel--dragging{transition:none;-webkit-user-select:none;user-select:none}.mk-dialog-panel--resizable .mk-dialog{height:100%}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
1167
1363
  }
1168
1364
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImport: i0, type: MkDialog, decorators: [{
1169
1365
  type: Component,
1170
- args: [{ selector: 'mk-dialog', changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, host: { class: 'mk-dialog' }, template: "<header class=\"mk-dialog__header\">\n <div class=\"mk-dialog__heading\">\n <ng-content select=\"[mkDialogHeader], mk-dialog-title\" />\n @if (dialogTitle(); as heading) {\n <h2 class=\"mk-dialog__title\" [id]=\"titleId()\">{{ heading }}</h2>\n }\n </div>\n @if (!hideClose()) {\n <button\n type=\"button\"\n class=\"mk-dialog__close\"\n [attr.aria-label]=\"i18n.close\"\n (click)=\"close()\"\n >\n <svg viewBox=\"0 0 16 16\" width=\"16\" height=\"16\" aria-hidden=\"true\" focusable=\"false\">\n <path\n d=\"M4 4l8 8M12 4l-8 8\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"1.5\"\n stroke-linecap=\"round\"\n />\n </svg>\n </button>\n }\n</header>\n\n<div class=\"mk-dialog__body\">\n <ng-content />\n</div>\n\n<footer class=\"mk-dialog__footer\">\n <ng-content select=\"[mkDialogFooter]\" />\n</footer>\n", styles: ["@charset \"UTF-8\";.mk-dialog{display:flex;flex-direction:column;min-height:0;max-height:inherit;flex:1 1 auto;font-family:var(--mk-font-sans)}.mk-dialog__header{position:sticky;top:0;z-index:1;display:flex;align-items:flex-start;gap:var(--mk-space-3);flex-shrink:0;padding:var(--mk-space-4) var(--mk-space-5);border-bottom:var(--mk-border-width) solid var(--mk-border-subtle);background-color:var(--mk-surface)}.mk-dialog__heading{flex:1;min-width:0;display:flex;flex-direction:column;gap:var(--mk-space-1)}.mk-dialog__title{margin:0;min-width:0;overflow-wrap:anywhere;font-size:var(--mk-font-size-lg);font-weight:var(--mk-font-weight-semibold);line-height:var(--mk-line-height-snug);color:var(--mk-text)}.mk-dialog__body{flex:1 1 auto;min-height:0;overflow-y:auto;overscroll-behavior:contain;padding:var(--mk-space-5);font-size:var(--mk-font-size-md);line-height:var(--mk-line-height-normal);color:var(--mk-text)}.mk-dialog__footer{position:sticky;bottom:0;flex-shrink:0;display:flex;align-items:center;justify-content:flex-end;gap:var(--mk-space-2);padding:var(--mk-space-4) var(--mk-space-5);border-top:var(--mk-border-width) solid var(--mk-border-subtle);background-color:var(--mk-surface)}.mk-dialog__footer:empty{display:none}.mk-dialog__footer>*{display:flex;flex:1 1 auto;align-items:center;justify-content:flex-end;gap:var(--mk-space-2);flex-wrap:wrap}.mk-dialog__footer>[data-align=start]{justify-content:flex-start}.mk-dialog__footer>[data-align=center]{justify-content:center}.mk-dialog__footer>[data-align=between]{justify-content:space-between}.mk-dialog__close{flex-shrink:0;display:inline-flex;align-items:center;justify-content:center;width:2rem;height:2rem;margin:-4px -4px 0 0;padding:0;background:transparent;border:0;border-radius:var(--mk-radius-sm);color:var(--mk-text-muted);cursor:pointer;transition:color var(--mk-duration-fast) var(--mk-ease-standard),background-color var(--mk-duration-fast) var(--mk-ease-standard)}.mk-dialog__close:hover{color:var(--mk-text);background-color:var(--mk-hover-overlay)}.mk-dialog__close:focus-visible{color:var(--mk-text);outline:var(--mk-focus-ring-width) solid var(--mk-focus-ring);outline-offset:var(--mk-focus-ring-offset)}\n"] }]
1171
- }], ctorParameters: () => [], propDecorators: { dialogTitle: [{ type: i0.Input, args: [{ isSignal: true, alias: "dialogTitle", required: false }] }], titleId: [{ type: i0.Input, args: [{ isSignal: true, alias: "titleId", required: false }] }], hideClose: [{ type: i0.Input, args: [{ isSignal: true, alias: "hideClose", required: false }] }] } });
1366
+ args: [{ selector: 'mk-dialog', changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, host: { class: 'mk-dialog' }, template: "<header\n class=\"mk-dialog__header\"\n [class.mk-dialog__header--draggable]=\"draggable()\"\n (pointerdown)=\"onHeaderPointerDown($event)\"\n (dblclick)=\"onHeaderDoubleClick($event)\"\n>\n @if (draggable()) {\n <button\n type=\"button\"\n class=\"mk-dialog__grip\"\n [attr.aria-label]=\"i18n.dialogMove\"\n [title]=\"i18n.dialogMove\"\n (pointerdown)=\"onGripPointerDown($event)\"\n (keydown)=\"onGripKeydown($event)\"\n >\n <svg viewBox=\"0 0 16 16\" width=\"16\" height=\"16\" aria-hidden=\"true\" focusable=\"false\">\n <circle cx=\"5.5\" cy=\"3.5\" r=\"1.3\" fill=\"currentColor\" />\n <circle cx=\"10.5\" cy=\"3.5\" r=\"1.3\" fill=\"currentColor\" />\n <circle cx=\"5.5\" cy=\"8\" r=\"1.3\" fill=\"currentColor\" />\n <circle cx=\"10.5\" cy=\"8\" r=\"1.3\" fill=\"currentColor\" />\n <circle cx=\"5.5\" cy=\"12.5\" r=\"1.3\" fill=\"currentColor\" />\n <circle cx=\"10.5\" cy=\"12.5\" r=\"1.3\" fill=\"currentColor\" />\n </svg>\n </button>\n }\n <div class=\"mk-dialog__heading\">\n <ng-content select=\"[mkDialogHeader], mk-dialog-title\" />\n @if (dialogTitle(); as heading) {\n <h2 class=\"mk-dialog__title\" [id]=\"titleId()\">{{ heading }}</h2>\n }\n </div>\n @if (!hideClose()) {\n <button\n type=\"button\"\n class=\"mk-dialog__close\"\n [attr.aria-label]=\"i18n.close\"\n (click)=\"close()\"\n >\n <svg viewBox=\"0 0 16 16\" width=\"16\" height=\"16\" aria-hidden=\"true\" focusable=\"false\">\n <path\n d=\"M4 4l8 8M12 4l-8 8\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"1.5\"\n stroke-linecap=\"round\"\n />\n </svg>\n </button>\n }\n</header>\n\n<div class=\"mk-dialog__body\">\n <ng-content />\n</div>\n\n<footer class=\"mk-dialog__footer\">\n <ng-content select=\"[mkDialogFooter]\" />\n</footer>\n\n@if (resizable()) {\n <button\n type=\"button\"\n class=\"mk-dialog__resizer\"\n [attr.aria-label]=\"i18n.dialogResize\"\n [title]=\"i18n.dialogResize\"\n (pointerdown)=\"onResizerPointerDown($event)\"\n (keydown)=\"onResizerKeydown($event)\"\n >\n <svg viewBox=\"0 0 16 16\" width=\"16\" height=\"16\" aria-hidden=\"true\" focusable=\"false\">\n <path d=\"M14 8L8 14M14 12l-2 2\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" />\n </svg>\n </button>\n}\n", styles: ["@charset \"UTF-8\";.mk-dialog{display:flex;flex-direction:column;min-height:0;max-height:inherit;flex:1 1 auto;font-family:var(--mk-font-sans)}.mk-dialog__header{position:sticky;top:0;z-index:1;display:flex;align-items:flex-start;gap:var(--mk-space-3);flex-shrink:0;padding:var(--mk-space-4) var(--mk-space-5);border-bottom:var(--mk-border-width) solid var(--mk-border-subtle);background-color:var(--mk-surface)}.mk-dialog__heading{flex:1;min-width:0;display:flex;flex-direction:column;gap:var(--mk-space-1)}.mk-dialog__title{margin:0;min-width:0;overflow-wrap:anywhere;font-size:var(--mk-font-size-lg);font-weight:var(--mk-font-weight-semibold);line-height:var(--mk-line-height-snug);color:var(--mk-text)}.mk-dialog__body{flex:1 1 auto;min-height:0;overflow-y:auto;overscroll-behavior:contain;padding:var(--mk-space-5);font-size:var(--mk-font-size-md);line-height:var(--mk-line-height-normal);color:var(--mk-text)}.mk-dialog__footer{position:sticky;bottom:0;flex-shrink:0;display:flex;align-items:center;justify-content:flex-end;gap:var(--mk-space-2);padding:var(--mk-space-4) var(--mk-space-5);border-top:var(--mk-border-width) solid var(--mk-border-subtle);background-color:var(--mk-surface)}.mk-dialog__footer:empty{display:none}.mk-dialog__footer>*{display:flex;flex:1 1 auto;align-items:center;justify-content:flex-end;gap:var(--mk-space-2);flex-wrap:wrap}.mk-dialog__footer>[data-align=start]{justify-content:flex-start}.mk-dialog__footer>[data-align=center]{justify-content:center}.mk-dialog__footer>[data-align=between]{justify-content:space-between}.mk-dialog__close{flex-shrink:0;display:inline-flex;align-items:center;justify-content:center;width:2rem;height:2rem;margin:-4px -4px 0 0;padding:0;background:transparent;border:0;border-radius:var(--mk-radius-sm);color:var(--mk-text-muted);cursor:pointer;transition:color var(--mk-duration-fast) var(--mk-ease-standard),background-color var(--mk-duration-fast) var(--mk-ease-standard)}.mk-dialog__close:hover{color:var(--mk-text);background-color:var(--mk-hover-overlay)}.mk-dialog__close:focus-visible{color:var(--mk-text);outline:var(--mk-focus-ring-width) solid var(--mk-focus-ring);outline-offset:var(--mk-focus-ring-offset)}.mk-dialog__header--draggable{cursor:move;touch-action:none;-webkit-user-select:none;user-select:none}.mk-dialog__grip,.mk-dialog__resizer{display:inline-flex;align-items:center;justify-content:center;padding:0;border:0;background:transparent;color:var(--mk-text-muted);cursor:move;touch-action:none}.mk-dialog__grip:hover,.mk-dialog__resizer:hover{color:var(--mk-text)}.mk-dialog__grip:focus-visible,.mk-dialog__resizer:focus-visible{outline:var(--mk-focus-ring-width) solid var(--mk-focus-ring);outline-offset:var(--mk-focus-ring-offset)}.mk-dialog__grip{flex:none;width:1.5rem;height:1.5rem;margin-inline-start:calc(-1 * var(--mk-space-2));border-radius:var(--mk-radius-sm)}.mk-dialog__resizer{position:absolute;inset-inline-end:0;bottom:0;width:1.25rem;height:1.25rem;cursor:nwse-resize;border-start-start-radius:var(--mk-radius-sm)}[dir=rtl] .mk-dialog__resizer,.mk-dialog-panel:dir(rtl) .mk-dialog__resizer{cursor:nesw-resize;transform:scaleX(-1)}.mk-dialog-panel--moved{animation:none}.mk-dialog-panel--dragging{transition:none;-webkit-user-select:none;user-select:none}.mk-dialog-panel--resizable .mk-dialog{height:100%}\n"] }]
1367
+ }], ctorParameters: () => [], propDecorators: { dialogTitle: [{ type: i0.Input, args: [{ isSignal: true, alias: "dialogTitle", required: false }] }], titleId: [{ type: i0.Input, args: [{ isSignal: true, alias: "titleId", required: false }] }], hideClose: [{ type: i0.Input, args: [{ isSignal: true, alias: "hideClose", required: false }] }], draggable: [{ type: i0.Input, args: [{ isSignal: true, alias: "draggable", required: false }] }], resizable: [{ type: i0.Input, args: [{ isSignal: true, alias: "resizable", required: false }] }] } });
1172
1368
 
1173
1369
  /**
1174
1370
  * Internal component backing `MkDialogService.confirm`. Renders a title,
@@ -1193,7 +1389,7 @@ class MkConfirmDialog {
1193
1389
  this.ref.close(false);
1194
1390
  }
1195
1391
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.7", ngImport: i0, type: MkConfirmDialog, deps: [], target: i0.ɵɵFactoryTarget.Component });
1196
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.7", type: MkConfirmDialog, isStandalone: true, selector: "mk-confirm-dialog", ngImport: i0, template: "<mk-dialog [dialogTitle]=\"data.title\" hideClose>\n <div class=\"mk-confirm-dialog__body\">\n @if (data.icon; as icon) {\n <mk-icon\n class=\"mk-confirm-dialog__icon\"\n [class.mk-confirm-dialog__icon--danger]=\"data.tone === 'danger'\"\n [name]=\"icon\"\n [size]=\"22\"\n />\n }\n <p class=\"mk-confirm-dialog__message\">{{ data.message }}</p>\n </div>\n\n <div mkDialogFooter>\n @if (!data.hideCancel) {\n <button mkButton variant=\"ghost\" tone=\"neutral\" (click)=\"cancel()\">\n {{ cancelText() }}\n </button>\n }\n <button mkButton [tone]=\"confirmTone()\" (click)=\"confirm()\">\n {{ confirmText() }}\n </button>\n </div>\n</mk-dialog>\n", styles: [".mk-confirm-dialog__message{margin:0;color:var(--mk-text-muted);font-size:var(--mk-font-size-md);line-height:var(--mk-line-height-relaxed)}.mk-confirm-dialog__body{display:flex;align-items:flex-start;gap:var(--mk-space-3)}.mk-confirm-dialog__icon{flex-shrink:0;margin-top:.125rem;color:var(--mk-text-muted)}.mk-confirm-dialog__icon--danger{color:var(--mk-danger-text)}\n"], dependencies: [{ kind: "component", type: MkDialog, selector: "mk-dialog", inputs: ["dialogTitle", "titleId", "hideClose"] }, { kind: "component", type: MkButton, selector: "button[mkButton], a[mkButton]", inputs: ["variant", "tone", "size", "loading", "fullWidth", "iconOnly", "disabled"] }, { kind: "component", type: MkIcon, selector: "mk-icon", inputs: ["name", "size", "label"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
1392
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.7", type: MkConfirmDialog, isStandalone: true, selector: "mk-confirm-dialog", ngImport: i0, template: "<mk-dialog [dialogTitle]=\"data.title\" hideClose>\n <div class=\"mk-confirm-dialog__body\">\n @if (data.icon; as icon) {\n <mk-icon\n class=\"mk-confirm-dialog__icon\"\n [class.mk-confirm-dialog__icon--danger]=\"data.tone === 'danger'\"\n [name]=\"icon\"\n [size]=\"22\"\n />\n }\n <p class=\"mk-confirm-dialog__message\">{{ data.message }}</p>\n </div>\n\n <div mkDialogFooter>\n @if (!data.hideCancel) {\n <button mkButton variant=\"ghost\" tone=\"neutral\" (click)=\"cancel()\">\n {{ cancelText() }}\n </button>\n }\n <button mkButton [tone]=\"confirmTone()\" (click)=\"confirm()\">\n {{ confirmText() }}\n </button>\n </div>\n</mk-dialog>\n", styles: [".mk-confirm-dialog__message{margin:0;color:var(--mk-text-muted);font-size:var(--mk-font-size-md);line-height:var(--mk-line-height-relaxed)}.mk-confirm-dialog__body{display:flex;align-items:flex-start;gap:var(--mk-space-3)}.mk-confirm-dialog__icon{flex-shrink:0;margin-top:.125rem;color:var(--mk-text-muted)}.mk-confirm-dialog__icon--danger{color:var(--mk-danger-text)}\n"], dependencies: [{ kind: "component", type: MkDialog, selector: "mk-dialog", inputs: ["dialogTitle", "titleId", "hideClose", "draggable", "resizable"] }, { kind: "component", type: MkButton, selector: "button[mkButton], a[mkButton]", inputs: ["variant", "tone", "size", "loading", "fullWidth", "iconOnly", "disabled"] }, { kind: "component", type: MkIcon, selector: "mk-icon", inputs: ["name", "size", "label"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
1197
1393
  }
1198
1394
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImport: i0, type: MkConfirmDialog, decorators: [{
1199
1395
  type: Component,
@@ -1233,7 +1429,7 @@ class MkPromptDialog {
1233
1429
  this.ref.close(null);
1234
1430
  }
1235
1431
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.7", ngImport: i0, type: MkPromptDialog, deps: [], target: i0.ɵɵFactoryTarget.Component });
1236
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.7", type: MkPromptDialog, isStandalone: true, selector: "mk-prompt-dialog", viewQueries: [{ propertyName: "inputRef", first: true, predicate: ["input"], descendants: true, isSignal: true }], ngImport: i0, template: "<mk-dialog [dialogTitle]=\"data.title\" hideClose>\n @if (data.message) {\n <p class=\"mk-confirm-dialog__message\">{{ data.message }}</p>\n }\n\n <mk-form-field [label]=\"data.label ?? ''\">\n <input\n #input\n mkInput\n [attr.type]=\"data.inputType ?? 'text'\"\n [value]=\"value()\"\n [attr.placeholder]=\"data.placeholder ?? null\"\n [attr.required]=\"data.required ? '' : null\"\n (input)=\"onInput($event)\"\n (keydown.enter)=\"submit()\"\n />\n </mk-form-field>\n\n <div mkDialogFooter>\n <button mkButton variant=\"ghost\" tone=\"neutral\" (click)=\"cancel()\">\n {{ cancelText() }}\n </button>\n <button mkButton (click)=\"submit()\" [disabled]=\"!canConfirm()\">\n {{ confirmText() }}\n </button>\n </div>\n</mk-dialog>\n", styles: [".mk-confirm-dialog__message{margin:0;color:var(--mk-text-muted);font-size:var(--mk-font-size-md);line-height:var(--mk-line-height-relaxed)}.mk-confirm-dialog__body{display:flex;align-items:flex-start;gap:var(--mk-space-3)}.mk-confirm-dialog__icon{flex-shrink:0;margin-top:.125rem;color:var(--mk-text-muted)}.mk-confirm-dialog__icon--danger{color:var(--mk-danger-text)}\n"], dependencies: [{ kind: "component", type: MkDialog, selector: "mk-dialog", inputs: ["dialogTitle", "titleId", "hideClose"] }, { kind: "component", type: MkButton, selector: "button[mkButton], a[mkButton]", inputs: ["variant", "tone", "size", "loading", "fullWidth", "iconOnly", "disabled"] }, { kind: "component", type: MkInput, selector: "input[mkInput], textarea[mkInput]", inputs: ["size", "invalid"] }, { kind: "component", type: MkFormField, selector: "mk-form-field", inputs: ["label", "hint", "error", "errorMessages", "errorOn", "required", "disabled", "size"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
1432
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.7", type: MkPromptDialog, isStandalone: true, selector: "mk-prompt-dialog", viewQueries: [{ propertyName: "inputRef", first: true, predicate: ["input"], descendants: true, isSignal: true }], ngImport: i0, template: "<mk-dialog [dialogTitle]=\"data.title\" hideClose>\n @if (data.message) {\n <p class=\"mk-confirm-dialog__message\">{{ data.message }}</p>\n }\n\n <mk-form-field [label]=\"data.label ?? ''\">\n <input\n #input\n mkInput\n [attr.type]=\"data.inputType ?? 'text'\"\n [value]=\"value()\"\n [attr.placeholder]=\"data.placeholder ?? null\"\n [attr.required]=\"data.required ? '' : null\"\n (input)=\"onInput($event)\"\n (keydown.enter)=\"submit()\"\n />\n </mk-form-field>\n\n <div mkDialogFooter>\n <button mkButton variant=\"ghost\" tone=\"neutral\" (click)=\"cancel()\">\n {{ cancelText() }}\n </button>\n <button mkButton (click)=\"submit()\" [disabled]=\"!canConfirm()\">\n {{ confirmText() }}\n </button>\n </div>\n</mk-dialog>\n", styles: [".mk-confirm-dialog__message{margin:0;color:var(--mk-text-muted);font-size:var(--mk-font-size-md);line-height:var(--mk-line-height-relaxed)}.mk-confirm-dialog__body{display:flex;align-items:flex-start;gap:var(--mk-space-3)}.mk-confirm-dialog__icon{flex-shrink:0;margin-top:.125rem;color:var(--mk-text-muted)}.mk-confirm-dialog__icon--danger{color:var(--mk-danger-text)}\n"], dependencies: [{ kind: "component", type: MkDialog, selector: "mk-dialog", inputs: ["dialogTitle", "titleId", "hideClose", "draggable", "resizable"] }, { kind: "component", type: MkButton, selector: "button[mkButton], a[mkButton]", inputs: ["variant", "tone", "size", "loading", "fullWidth", "iconOnly", "disabled"] }, { kind: "component", type: MkInput, selector: "input[mkInput], textarea[mkInput]", inputs: ["size", "invalid"] }, { kind: "component", type: MkFormField, selector: "mk-form-field", inputs: ["label", "hint", "error", "errorMessages", "errorOn", "required", "disabled", "size", "labelPosition"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
1237
1433
  }
1238
1434
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImport: i0, type: MkPromptDialog, decorators: [{
1239
1435
  type: Component,
@@ -2413,6 +2609,202 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImpor
2413
2609
  args: [{ providedIn: 'root' }]
2414
2610
  }], ctorParameters: () => [] });
2415
2611
 
2612
+ /**
2613
+ * The translucent panel with a spinner that `mkBlockUi` and
2614
+ * `MkBlockUiService` drop over a region or the page.
2615
+ */
2616
+ class MkBlockUiOverlay {
2617
+ i18n = inject(MK_I18N);
2618
+ message = signal('', /* @ts-ignore */
2619
+ ...(ngDevMode ? [{ debugName: "message" }] : /* istanbul ignore next */ []));
2620
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.7", ngImport: i0, type: MkBlockUiOverlay, deps: [], target: i0.ɵɵFactoryTarget.Component });
2621
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.7", type: MkBlockUiOverlay, isStandalone: true, selector: "mk-block-ui-overlay", host: { attributes: { "role": "status", "aria-live": "polite" }, classAttribute: "mk-block-ui" }, ngImport: i0, template: `
2622
+ <div class="mk-block-ui__panel">
2623
+ <mk-spinner [label]="message() || i18n.loading" />
2624
+ @if (message()) {
2625
+ <span class="mk-block-ui__message">{{ message() }}</span>
2626
+ }
2627
+ </div>
2628
+ `, isInline: true, styles: [":host{position:absolute;inset:0;z-index:var(--mk-z-sticky, 10);display:flex;align-items:center;justify-content:center;background:var(--mk-block-ui-scrim, color-mix(in srgb, var(--mk-surface) 70%, transparent));-webkit-backdrop-filter:blur(1px);backdrop-filter:blur(1px);border-radius:inherit;cursor:progress;animation:mk-block-ui-in var(--mk-duration-fast) var(--mk-ease-standard)}:host(.mk-block-ui--page){position:fixed;z-index:var(--mk-z-overlay, 1000);border-radius:0}.mk-block-ui__panel{display:flex;flex-direction:column;align-items:center;gap:var(--mk-space-3);padding:var(--mk-space-4) var(--mk-space-5);border-radius:var(--mk-radius-lg);background:var(--mk-surface);box-shadow:var(--mk-shadow-md);color:var(--mk-text);font-size:var(--mk-font-size-sm)}@keyframes mk-block-ui-in{0%{opacity:0}to{opacity:1}}@media(prefers-reduced-motion:reduce){:host{animation:none;-webkit-backdrop-filter:none;backdrop-filter:none}}\n"], dependencies: [{ kind: "component", type: MkSpinner, selector: "mk-spinner", inputs: ["size", "tone", "label"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
2629
+ }
2630
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImport: i0, type: MkBlockUiOverlay, decorators: [{
2631
+ type: Component,
2632
+ args: [{ selector: 'mk-block-ui-overlay', template: `
2633
+ <div class="mk-block-ui__panel">
2634
+ <mk-spinner [label]="message() || i18n.loading" />
2635
+ @if (message()) {
2636
+ <span class="mk-block-ui__message">{{ message() }}</span>
2637
+ }
2638
+ </div>
2639
+ `, changeDetection: ChangeDetectionStrategy.OnPush, imports: [MkSpinner], host: { class: 'mk-block-ui', role: 'status', 'aria-live': 'polite' }, styles: [":host{position:absolute;inset:0;z-index:var(--mk-z-sticky, 10);display:flex;align-items:center;justify-content:center;background:var(--mk-block-ui-scrim, color-mix(in srgb, var(--mk-surface) 70%, transparent));-webkit-backdrop-filter:blur(1px);backdrop-filter:blur(1px);border-radius:inherit;cursor:progress;animation:mk-block-ui-in var(--mk-duration-fast) var(--mk-ease-standard)}:host(.mk-block-ui--page){position:fixed;z-index:var(--mk-z-overlay, 1000);border-radius:0}.mk-block-ui__panel{display:flex;flex-direction:column;align-items:center;gap:var(--mk-space-3);padding:var(--mk-space-4) var(--mk-space-5);border-radius:var(--mk-radius-lg);background:var(--mk-surface);box-shadow:var(--mk-shadow-md);color:var(--mk-text);font-size:var(--mk-font-size-sm)}@keyframes mk-block-ui-in{0%{opacity:0}to{opacity:1}}@media(prefers-reduced-motion:reduce){:host{animation:none;-webkit-backdrop-filter:none;backdrop-filter:none}}\n"] }]
2640
+ }] });
2641
+ /** Mount an overlay into `host`, making the host a positioning context if it has none. */
2642
+ function mountOverlay(document, injector, appRef, host, message, page) {
2643
+ const ref = createComponent(MkBlockUiOverlay, { environmentInjector: injector });
2644
+ const el = ref.location.nativeElement;
2645
+ if (page)
2646
+ el.classList.add('mk-block-ui--page');
2647
+ ref.instance.message.set(message);
2648
+ const view = document.defaultView;
2649
+ const current = view?.getComputedStyle(host).position ?? '';
2650
+ const positioned = !page && current !== '' && current !== 'static';
2651
+ const addedPosition = !page && !positioned;
2652
+ if (addedPosition)
2653
+ host.style.position = 'relative';
2654
+ // Everything already inside becomes inert: no clicks, no Tab stops, no
2655
+ // screen-reader traversal — only the status overlay is reachable.
2656
+ const inerted = [];
2657
+ for (const child of Array.from(host.children)) {
2658
+ if (!child.hasAttribute('inert')) {
2659
+ child.setAttribute('inert', '');
2660
+ inerted.push(child);
2661
+ }
2662
+ }
2663
+ host.appendChild(el);
2664
+ host.setAttribute('aria-busy', 'true');
2665
+ // Attach so later `message` updates render with the app's change detection.
2666
+ appRef.attachView(ref.hostView);
2667
+ ref.changeDetectorRef.detectChanges();
2668
+ return {
2669
+ ref,
2670
+ dispose: () => {
2671
+ appRef.detachView(ref.hostView);
2672
+ ref.destroy();
2673
+ el.remove();
2674
+ for (const child of inerted)
2675
+ child.removeAttribute('inert');
2676
+ host.removeAttribute('aria-busy');
2677
+ if (addedPosition)
2678
+ host.style.position = '';
2679
+ },
2680
+ };
2681
+ }
2682
+ /**
2683
+ * Block UI — cover the host element with a translucent panel and spinner
2684
+ * while something loads, so its contents can neither be read nor operated:
2685
+ * a card refreshing, a form submitting, a table re-fetching.
2686
+ *
2687
+ * Children get `inert` (no clicks, no Tab stops) and the host `aria-busy`.
2688
+ * `mkBlockUiDelay` (ms) holds the overlay back for quick operations so the
2689
+ * screen does not flash.
2690
+ *
2691
+ * ```html
2692
+ * <mk-card [mkBlockUi]="saving()" mkBlockUiMessage="Saving…">…</mk-card>
2693
+ * <form [mkBlockUi]="submitting()" [mkBlockUiDelay]="300">…</form>
2694
+ * ```
2695
+ */
2696
+ class MkBlockUi {
2697
+ host = inject(ElementRef);
2698
+ document = inject(DOCUMENT);
2699
+ injector = inject(EnvironmentInjector);
2700
+ appRef = inject(ApplicationRef);
2701
+ /** Block while `true`. */
2702
+ mkBlockUi = input(false, { ...(ngDevMode ? { debugName: "mkBlockUi" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
2703
+ /** Text under the spinner (also its accessible label). */
2704
+ mkBlockUiMessage = input('', /* @ts-ignore */
2705
+ ...(ngDevMode ? [{ debugName: "mkBlockUiMessage" }] : /* istanbul ignore next */ []));
2706
+ /** Wait this many ms before showing, to avoid a flash on fast operations. */
2707
+ mkBlockUiDelay = input(0, { ...(ngDevMode ? { debugName: "mkBlockUiDelay" } : /* istanbul ignore next */ {}), transform: numberAttribute });
2708
+ mounted = null;
2709
+ timer = null;
2710
+ constructor() {
2711
+ effect(() => {
2712
+ const on = this.mkBlockUi();
2713
+ const message = this.mkBlockUiMessage();
2714
+ const delay = this.mkBlockUiDelay();
2715
+ this.cancelTimer();
2716
+ if (!on) {
2717
+ this.unmount();
2718
+ return;
2719
+ }
2720
+ if (this.mounted) {
2721
+ this.mounted.ref.instance.message.set(message);
2722
+ return;
2723
+ }
2724
+ if (delay > 0)
2725
+ this.timer = setTimeout(() => this.mount(message), delay);
2726
+ else
2727
+ this.mount(message);
2728
+ });
2729
+ inject(DestroyRef).onDestroy(() => {
2730
+ this.cancelTimer();
2731
+ this.unmount();
2732
+ });
2733
+ }
2734
+ mount(message) {
2735
+ this.timer = null;
2736
+ this.mounted = mountOverlay(this.document, this.injector, this.appRef, this.host.nativeElement, message, false);
2737
+ }
2738
+ unmount() {
2739
+ this.mounted?.dispose();
2740
+ this.mounted = null;
2741
+ }
2742
+ cancelTimer() {
2743
+ if (this.timer)
2744
+ clearTimeout(this.timer);
2745
+ this.timer = null;
2746
+ }
2747
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.7", ngImport: i0, type: MkBlockUi, deps: [], target: i0.ɵɵFactoryTarget.Directive });
2748
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.0.7", type: MkBlockUi, isStandalone: true, selector: "[mkBlockUi]", inputs: { mkBlockUi: { classPropertyName: "mkBlockUi", publicName: "mkBlockUi", isSignal: true, isRequired: false, transformFunction: null }, mkBlockUiMessage: { classPropertyName: "mkBlockUiMessage", publicName: "mkBlockUiMessage", isSignal: true, isRequired: false, transformFunction: null }, mkBlockUiDelay: { classPropertyName: "mkBlockUiDelay", publicName: "mkBlockUiDelay", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class.mk-block-ui-host": "mkBlockUi()" } }, ngImport: i0 });
2749
+ }
2750
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImport: i0, type: MkBlockUi, decorators: [{
2751
+ type: Directive,
2752
+ args: [{
2753
+ selector: '[mkBlockUi]',
2754
+ host: { '[class.mk-block-ui-host]': 'mkBlockUi()' },
2755
+ }]
2756
+ }], ctorParameters: () => [], propDecorators: { mkBlockUi: [{ type: i0.Input, args: [{ isSignal: true, alias: "mkBlockUi", required: false }] }], mkBlockUiMessage: [{ type: i0.Input, args: [{ isSignal: true, alias: "mkBlockUiMessage", required: false }] }], mkBlockUiDelay: [{ type: i0.Input, args: [{ isSignal: true, alias: "mkBlockUiDelay", required: false }] }] } });
2757
+ /**
2758
+ * Block the whole page. Reference-counted: every `block()` returns a release
2759
+ * function, and the overlay disappears once all of them have been called.
2760
+ *
2761
+ * ```ts
2762
+ * const release = this.blockUi.block('Exporting…');
2763
+ * try { await this.api.export(); } finally { release(); }
2764
+ * ```
2765
+ */
2766
+ class MkBlockUiService {
2767
+ document = inject(DOCUMENT);
2768
+ injector = inject(EnvironmentInjector);
2769
+ appRef = inject(ApplicationRef);
2770
+ mounted = null;
2771
+ _count = signal(0, /* @ts-ignore */
2772
+ ...(ngDevMode ? [{ debugName: "_count" }] : /* istanbul ignore next */ []));
2773
+ /** Number of active blocks. */
2774
+ count = this._count.asReadonly();
2775
+ /** Show the page overlay (or update its message); call the returned function to release. */
2776
+ block(message = '') {
2777
+ if (typeof this.document.body === 'undefined')
2778
+ return () => { };
2779
+ this._count.update((n) => n + 1);
2780
+ if (this.mounted)
2781
+ this.mounted.ref.instance.message.set(message);
2782
+ else
2783
+ this.mounted = mountOverlay(this.document, this.injector, this.appRef, this.document.body, message, true);
2784
+ let released = false;
2785
+ return () => {
2786
+ if (released)
2787
+ return;
2788
+ released = true;
2789
+ this._count.update((n) => Math.max(0, n - 1));
2790
+ if (this._count() === 0) {
2791
+ this.mounted?.dispose();
2792
+ this.mounted = null;
2793
+ }
2794
+ };
2795
+ }
2796
+ /** True while the page is blocked. */
2797
+ isBlocked() {
2798
+ return this._count() > 0;
2799
+ }
2800
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.7", ngImport: i0, type: MkBlockUiService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
2801
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.7", ngImport: i0, type: MkBlockUiService, providedIn: 'root' });
2802
+ }
2803
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImport: i0, type: MkBlockUiService, decorators: [{
2804
+ type: Injectable,
2805
+ args: [{ providedIn: 'root' }]
2806
+ }] });
2807
+
2416
2808
  /**
2417
2809
  * Feedback & Overlay component group barrel.
2418
2810
  * Re-exports every public symbol (components, directives, services, types).
@@ -2422,5 +2814,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImpor
2422
2814
  * Generated bundle index. Do not edit.
2423
2815
  */
2424
2816
 
2425
- export { MK_TOUR_DATA, MkAlert, MkBanner, MkBottomSheet, MkBottomSheetService, MkConfirmDialog, MkDialog, MkDialogService, MkDialogTitle, MkHovercard, MkHovercardTrigger, MkLoadingBar, MkLoadingBarService, MkNotificationCenter, MkPopconfirm, MkPopconfirmTrigger, MkPopover, MkPopoverTrigger, MkPromptDialog, MkResult, MkSnackbar, MkSnackbarContainer, MkSnackbarRef, MkSnackbarService, MkToast, MkToastContainer, MkToastService, MkTooltip, MkTooltipPanel, MkTourPopup, MkTourService };
2817
+ export { MK_TOUR_DATA, MkAlert, MkBanner, MkBlockUi, MkBlockUiOverlay, MkBlockUiService, MkBottomSheet, MkBottomSheetService, MkConfirmDialog, MkDialog, MkDialogService, MkDialogTitle, MkHovercard, MkHovercardTrigger, MkLoadingBar, MkLoadingBarService, MkNotificationCenter, MkPopconfirm, MkPopconfirmTrigger, MkPopover, MkPopoverTrigger, MkPromptDialog, MkResult, MkSnackbar, MkSnackbarContainer, MkSnackbarRef, MkSnackbarService, MkToast, MkToastContainer, MkToastService, MkTooltip, MkTooltipPanel, MkTourPopup, MkTourService };
2426
2818
  //# sourceMappingURL=mk-kit-ui-feedback.mjs.map