mn-angular-lib 1.0.99 → 1.0.101
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.
|
@@ -253,6 +253,11 @@ const mnButtonVariants = tv({
|
|
|
253
253
|
disabled: {
|
|
254
254
|
true: 'opacity-50 pointer-events-none',
|
|
255
255
|
},
|
|
256
|
+
// Busy state: dim and block interaction like `disabled`, and hint the wait cursor.
|
|
257
|
+
// The spinner itself is rendered in the template (see mn-button.html).
|
|
258
|
+
loading: {
|
|
259
|
+
true: 'opacity-50 pointer-events-none cursor-wait',
|
|
260
|
+
},
|
|
256
261
|
wrap: {
|
|
257
262
|
true: 'whitespace-normal',
|
|
258
263
|
false: 'whitespace-nowrap',
|
|
@@ -333,6 +338,7 @@ const mnButtonVariants = tv({
|
|
|
333
338
|
color: 'primary',
|
|
334
339
|
borderRadius: 'lg',
|
|
335
340
|
disabled: false,
|
|
341
|
+
loading: false,
|
|
336
342
|
wrap: false,
|
|
337
343
|
hover: true,
|
|
338
344
|
},
|
|
@@ -340,6 +346,7 @@ const mnButtonVariants = tv({
|
|
|
340
346
|
|
|
341
347
|
class MnButton {
|
|
342
348
|
data = {};
|
|
349
|
+
// A loading button is treated as non-interactive: it blocks clicks and is unfocusable,
|
|
343
350
|
// Bind the computed classes to the host element
|
|
344
351
|
get hostClasses() {
|
|
345
352
|
return mnButtonVariants({
|
|
@@ -348,33 +355,45 @@ class MnButton {
|
|
|
348
355
|
color: this.data.color,
|
|
349
356
|
borderRadius: this.data.borderRadius,
|
|
350
357
|
disabled: this.data.disabled,
|
|
358
|
+
loading: this.data.loading,
|
|
351
359
|
wrap: this.data.wrap,
|
|
352
360
|
hover: this.data.hover,
|
|
353
361
|
});
|
|
354
362
|
}
|
|
363
|
+
// Signals assistive tech that the control is busy while a loading action runs.
|
|
364
|
+
get ariaBusy() {
|
|
365
|
+
return this.data.loading ? 'true' : null;
|
|
366
|
+
}
|
|
355
367
|
// For accessibility (works for both <button> and <a>)
|
|
356
368
|
get ariaDisabled() {
|
|
357
369
|
return this.data.disabled ? 'true' : null;
|
|
358
370
|
}
|
|
359
371
|
// Only meaningful for <button>. For <a> it does nothing semantically.
|
|
360
372
|
get disabledAttr() {
|
|
361
|
-
return this.
|
|
373
|
+
return this.isBlocked ? '' : null;
|
|
362
374
|
}
|
|
363
|
-
// Make disabled anchors unfocusable + prevent activation
|
|
375
|
+
// Make disabled/loading anchors unfocusable + prevent activation
|
|
364
376
|
get tabIndex() {
|
|
365
|
-
return this.
|
|
377
|
+
return this.isBlocked ? '-1' : null;
|
|
378
|
+
}
|
|
379
|
+
// so an in-flight action cannot be triggered a second time.
|
|
380
|
+
get isBlocked() {
|
|
381
|
+
return !!(this.data.disabled || this.data.loading);
|
|
366
382
|
}
|
|
367
383
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: MnButton, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
368
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "
|
|
384
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.3", type: MnButton, isStandalone: true, selector: "button[mnButton], a[mnButton]", inputs: { data: "data" }, host: { properties: { "class": "this.hostClasses", "attr.aria-busy": "this.ariaBusy", "attr.aria-disabled": "this.ariaDisabled", "attr.disabled": "this.disabledAttr", "attr.tabindex": "this.tabIndex" } }, ngImport: i0, template: "@if (data.loading) {\n <span\n aria-hidden=\"true\"\n class=\"inline-block h-4 w-4 shrink-0 mr-2 animate-spin rounded-full border-2 border-current border-r-transparent\"\n ></span>\n}\n<ng-content></ng-content>\n" });
|
|
369
385
|
}
|
|
370
386
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: MnButton, decorators: [{
|
|
371
387
|
type: Component,
|
|
372
|
-
args: [{ selector: 'button[mnButton], a[mnButton]', standalone: true, template: "<ng-content></ng-content>\n" }]
|
|
388
|
+
args: [{ selector: 'button[mnButton], a[mnButton]', standalone: true, template: "@if (data.loading) {\n <span\n aria-hidden=\"true\"\n class=\"inline-block h-4 w-4 shrink-0 mr-2 animate-spin rounded-full border-2 border-current border-r-transparent\"\n ></span>\n}\n<ng-content></ng-content>\n" }]
|
|
373
389
|
}], propDecorators: { data: [{
|
|
374
390
|
type: Input
|
|
375
391
|
}], hostClasses: [{
|
|
376
392
|
type: HostBinding,
|
|
377
393
|
args: ['class']
|
|
394
|
+
}], ariaBusy: [{
|
|
395
|
+
type: HostBinding,
|
|
396
|
+
args: ['attr.aria-busy']
|
|
378
397
|
}], ariaDisabled: [{
|
|
379
398
|
type: HostBinding,
|
|
380
399
|
args: ['attr.aria-disabled']
|
|
@@ -6621,7 +6640,14 @@ class MnModalShellComponent {
|
|
|
6621
6640
|
config;
|
|
6622
6641
|
modalRef;
|
|
6623
6642
|
isClosing = false;
|
|
6624
|
-
|
|
6643
|
+
/**
|
|
6644
|
+
* Whether another modal is stacked on top of this one. Set imperatively by
|
|
6645
|
+
* `MnModalService` on the already-rendered shell below the newly opened one, so it must
|
|
6646
|
+
* be a signal: mutating a plain field there changes the host `[class]` after the view was
|
|
6647
|
+
* checked (NG0100 ExpressionChangedAfterItHasBeenChecked) and does not schedule change
|
|
6648
|
+
* detection in a zoneless app. A signal write both notifies the host binding and schedules CD.
|
|
6649
|
+
*/
|
|
6650
|
+
isStacked = signal(false, ...(ngDevMode ? [{ debugName: "isStacked" }] : []));
|
|
6625
6651
|
ModalKind = ModalKind;
|
|
6626
6652
|
previouslyFocusedElement = null;
|
|
6627
6653
|
focusTrapListener = null;
|
|
@@ -6796,7 +6822,7 @@ class MnModalShellComponent {
|
|
|
6796
6822
|
? this.config.animation
|
|
6797
6823
|
: this.config.animation?.type || 'slide';
|
|
6798
6824
|
const animation = ` anim-${animType}`;
|
|
6799
|
-
const stacked = this.isStacked ? ' is-stacked' : '';
|
|
6825
|
+
const stacked = this.isStacked() ? ' is-stacked' : '';
|
|
6800
6826
|
const mobileSheet = this.isMobileSheet ? ' mobile-sheet' : '';
|
|
6801
6827
|
const swiping = this.swipeDismissing ? ' swipe-dismissing' : '';
|
|
6802
6828
|
return `modal-shell modal-${size}${animation}${stacked}${mobileSheet}${swiping}`;
|
|
@@ -7061,7 +7087,7 @@ class MnModalService {
|
|
|
7061
7087
|
// Update stack and dim previous modal
|
|
7062
7088
|
if (this.modalStack.length > 0) {
|
|
7063
7089
|
const prevModal = this.modalStack[this.modalStack.length - 1];
|
|
7064
|
-
prevModal.component.isStacked
|
|
7090
|
+
prevModal.component.isStacked.set(true);
|
|
7065
7091
|
}
|
|
7066
7092
|
this.modalStack.push(modalRef);
|
|
7067
7093
|
// Attach to application
|
|
@@ -7078,7 +7104,7 @@ class MnModalService {
|
|
|
7078
7104
|
this.modalStack.splice(index, 1);
|
|
7079
7105
|
if (this.modalStack.length > 0) {
|
|
7080
7106
|
const topModal = this.modalStack[this.modalStack.length - 1];
|
|
7081
|
-
topModal.component.isStacked
|
|
7107
|
+
topModal.component.isStacked.set(false);
|
|
7082
7108
|
}
|
|
7083
7109
|
}
|
|
7084
7110
|
});
|