@mk-kit/ui 0.52.1 → 0.54.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.
- package/fesm2022/mk-kit-ui-data.mjs +5 -417
- package/fesm2022/mk-kit-ui-data.mjs.map +1 -1
- package/fesm2022/mk-kit-ui-feedback.mjs +1 -1
- package/fesm2022/mk-kit-ui-feedback.mjs.map +1 -1
- package/fesm2022/mk-kit-ui-kanban.mjs +205 -0
- package/fesm2022/mk-kit-ui-kanban.mjs.map +1 -0
- package/fesm2022/mk-kit-ui-status.mjs +318 -0
- package/fesm2022/mk-kit-ui-status.mjs.map +1 -0
- package/fesm2022/mk-kit-ui-table.mjs +49 -1
- package/fesm2022/mk-kit-ui-table.mjs.map +1 -1
- package/fesm2022/mk-kit-ui.mjs +2 -0
- package/fesm2022/mk-kit-ui.mjs.map +1 -1
- package/package.json +9 -1
- package/types/mk-kit-ui-data.d.ts +3 -247
- package/types/mk-kit-ui-kanban.d.ts +153 -0
- package/types/mk-kit-ui-status.d.ts +158 -0
- package/types/mk-kit-ui-table.d.ts +26 -2
- package/types/mk-kit-ui.d.ts +2 -0
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
import { NgTemplateOutlet } from '@angular/common';
|
|
2
|
+
import * as i0 from '@angular/core';
|
|
3
|
+
import { inject, TemplateRef, Directive, model, input, booleanAttribute, output, contentChild, contentChildren, computed, ChangeDetectionStrategy, Component } from '@angular/core';
|
|
4
|
+
import { mkUniqueId } from '@mk-kit/ui/core';
|
|
5
|
+
import { mkMoveItemInArray, MkDropList, MkDrag } from '@mk-kit/ui/dnd';
|
|
6
|
+
|
|
7
|
+
/** Stable empty fallback so out-of-range lookups don't allocate per call. */
|
|
8
|
+
const NO_CONNECTIONS = [];
|
|
9
|
+
/**
|
|
10
|
+
* Marks the projected card renderer explicitly:
|
|
11
|
+
* `<ng-template mkKanbanCard let-card let-column="column">`. Without it the
|
|
12
|
+
* first plain `<ng-template>` inside `<mk-kanban>` is used, as before 0.54.
|
|
13
|
+
*/
|
|
14
|
+
class MkKanbanCardDef {
|
|
15
|
+
template = inject(TemplateRef);
|
|
16
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.7", ngImport: i0, type: MkKanbanCardDef, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
17
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.0.7", type: MkKanbanCardDef, isStandalone: true, selector: "ng-template[mkKanbanCard]", ngImport: i0 });
|
|
18
|
+
}
|
|
19
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImport: i0, type: MkKanbanCardDef, decorators: [{
|
|
20
|
+
type: Directive,
|
|
21
|
+
args: [{ selector: 'ng-template[mkKanbanCard]' }]
|
|
22
|
+
}] });
|
|
23
|
+
/**
|
|
24
|
+
* Replaces the default column header (title + count):
|
|
25
|
+
* `<ng-template mkKanbanColumnHeader let-column let-count="count">`.
|
|
26
|
+
*/
|
|
27
|
+
class MkKanbanColumnHeaderDef {
|
|
28
|
+
template = inject(TemplateRef);
|
|
29
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.7", ngImport: i0, type: MkKanbanColumnHeaderDef, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
30
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.0.7", type: MkKanbanColumnHeaderDef, isStandalone: true, selector: "ng-template[mkKanbanColumnHeader]", ngImport: i0 });
|
|
31
|
+
}
|
|
32
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImport: i0, type: MkKanbanColumnHeaderDef, decorators: [{
|
|
33
|
+
type: Directive,
|
|
34
|
+
args: [{ selector: 'ng-template[mkKanbanColumnHeader]' }]
|
|
35
|
+
}] });
|
|
36
|
+
/**
|
|
37
|
+
* Rendered under each column's card list — a "quick add" input, say:
|
|
38
|
+
* `<ng-template mkKanbanColumnFooter let-column>`.
|
|
39
|
+
*/
|
|
40
|
+
class MkKanbanColumnFooterDef {
|
|
41
|
+
template = inject(TemplateRef);
|
|
42
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.7", ngImport: i0, type: MkKanbanColumnFooterDef, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
43
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.0.7", type: MkKanbanColumnFooterDef, isStandalone: true, selector: "ng-template[mkKanbanColumnFooter]", ngImport: i0 });
|
|
44
|
+
}
|
|
45
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImport: i0, type: MkKanbanColumnFooterDef, decorators: [{
|
|
46
|
+
type: Directive,
|
|
47
|
+
args: [{ selector: 'ng-template[mkKanbanColumnFooter]' }]
|
|
48
|
+
}] });
|
|
49
|
+
/**
|
|
50
|
+
* Kanban board — a horizontal row of columns whose cards can be dragged and
|
|
51
|
+
* reordered within a column or transferred between columns. Built on the dnd
|
|
52
|
+
* group's connected `[mkDropList]` / `[mkDrag]` directives, so it inherits both
|
|
53
|
+
* pointer and keyboard dragging (WCAG 2.1.1) for free.
|
|
54
|
+
*
|
|
55
|
+
* Bind `columns` two-way; the model is updated immutably on each drop. Project
|
|
56
|
+
* an `<ng-template>` to render custom card content — it receives the card as
|
|
57
|
+
* `$implicit` and the owning column as `column`. Column headers and footers
|
|
58
|
+
* take their own templates (`mkKanbanColumnHeader` / `mkKanbanColumnFooter`,
|
|
59
|
+
* context `{ $implicit: column, index, count }`):
|
|
60
|
+
*
|
|
61
|
+
* ```html
|
|
62
|
+
* <mk-kanban [(columns)]="board" (cardMoved)="onMoved($event)">
|
|
63
|
+
* <ng-template mkKanbanColumnHeader let-column let-count="count">
|
|
64
|
+
* <strong>{{ column.title }}</strong> {{ count }}
|
|
65
|
+
* <button (click)="rename(column)">Rename</button>
|
|
66
|
+
* </ng-template>
|
|
67
|
+
* <ng-template mkKanbanCard let-card let-column="column">
|
|
68
|
+
* <strong>{{ card.title }}</strong>
|
|
69
|
+
* </ng-template>
|
|
70
|
+
* <ng-template mkKanbanColumnFooter let-column>
|
|
71
|
+
* <button (click)="add(column)">+ Card</button>
|
|
72
|
+
* </ng-template>
|
|
73
|
+
* </mk-kanban>
|
|
74
|
+
* ```
|
|
75
|
+
*/
|
|
76
|
+
class MkKanban {
|
|
77
|
+
/** The board's columns and their cards (two-way). Updated on every drop. */
|
|
78
|
+
columns = model([], /* @ts-ignore */
|
|
79
|
+
...(ngDevMode ? [{ debugName: "columns" }] : /* istanbul ignore next */ []));
|
|
80
|
+
/** Disable all dragging on the board. */
|
|
81
|
+
disabled = input(false, { ...(ngDevMode ? { debugName: "disabled" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
82
|
+
/** Emitted after the model is updated when a card is moved. */
|
|
83
|
+
cardMoved = output();
|
|
84
|
+
cardDef = contentChild(MkKanbanCardDef, /* @ts-ignore */
|
|
85
|
+
...(ngDevMode ? [{ debugName: "cardDef" }] : /* istanbul ignore next */ []));
|
|
86
|
+
headerDef = contentChild(MkKanbanColumnHeaderDef, /* @ts-ignore */
|
|
87
|
+
...(ngDevMode ? [{ debugName: "headerDef" }] : /* istanbul ignore next */ []));
|
|
88
|
+
footerDef = contentChild(MkKanbanColumnFooterDef, /* @ts-ignore */
|
|
89
|
+
...(ngDevMode ? [{ debugName: "footerDef" }] : /* istanbul ignore next */ []));
|
|
90
|
+
/** Every projected template, for the pre-0.54 "first plain template is the card" rule. */
|
|
91
|
+
templates = contentChildren(TemplateRef, /* @ts-ignore */
|
|
92
|
+
...(ngDevMode ? [{ debugName: "templates" }] : /* istanbul ignore next */ []));
|
|
93
|
+
/** Consumer-projected card renderer: `[mkKanbanCard]`, else the first template that is not a header/footer. */
|
|
94
|
+
cardTemplate = computed(() => {
|
|
95
|
+
const explicit = this.cardDef()?.template;
|
|
96
|
+
if (explicit)
|
|
97
|
+
return explicit;
|
|
98
|
+
// A query and a directive's injected TemplateRef are different instances
|
|
99
|
+
// of the same template — compare their anchor nodes.
|
|
100
|
+
const taken = new Set([this.headerDef(), this.footerDef()].map((d) => d?.template.elementRef.nativeElement));
|
|
101
|
+
return this.templates().find((t) => !taken.has(t.elementRef.nativeElement));
|
|
102
|
+
}, /* @ts-ignore */
|
|
103
|
+
...(ngDevMode ? [{ debugName: "cardTemplate" }] : /* istanbul ignore next */ []));
|
|
104
|
+
/** Optional column header renderer (replaces title + count). */
|
|
105
|
+
headerTemplate = computed(() => this.headerDef()?.template, /* @ts-ignore */
|
|
106
|
+
...(ngDevMode ? [{ debugName: "headerTemplate" }] : /* istanbul ignore next */ []));
|
|
107
|
+
/** Optional column footer renderer (under the card list). */
|
|
108
|
+
footerTemplate = computed(() => this.footerDef()?.template, /* @ts-ignore */
|
|
109
|
+
...(ngDevMode ? [{ debugName: "footerTemplate" }] : /* istanbul ignore next */ []));
|
|
110
|
+
/** Board-scoped prefix so this board's drop-list ids never collide. */
|
|
111
|
+
boardId = mkUniqueId('mk-kanban');
|
|
112
|
+
/** Column count as its own computed so id arrays survive card-only updates. */
|
|
113
|
+
columnCount = computed(() => this.columns().length, /* @ts-ignore */
|
|
114
|
+
...(ngDevMode ? [{ debugName: "columnCount" }] : /* istanbul ignore next */ []));
|
|
115
|
+
/** Stable drop-list id per column, positionally matched to `columns()`. */
|
|
116
|
+
listIds = computed(() => Array.from({ length: this.columnCount() }, (_, i) => `${this.boardId}-col-${i}`), /* @ts-ignore */
|
|
117
|
+
...(ngDevMode ? [{ debugName: "listIds" }] : /* istanbul ignore next */ []));
|
|
118
|
+
/**
|
|
119
|
+
* Per-column connected-target ids (every other column's list). One stable
|
|
120
|
+
* array per column, recomputed only when the column count changes — a fresh
|
|
121
|
+
* array per change-detection pass would dirty each `MkDropList`'s
|
|
122
|
+
* `connectedTo` input identity on every cycle.
|
|
123
|
+
*/
|
|
124
|
+
connections = computed(() => {
|
|
125
|
+
const ids = this.listIds();
|
|
126
|
+
return ids.map((_, i) => ids.filter((_, j) => j !== i));
|
|
127
|
+
}, /* @ts-ignore */
|
|
128
|
+
...(ngDevMode ? [{ debugName: "connections" }] : /* istanbul ignore next */ []));
|
|
129
|
+
/** Ids of every other column's list — the connected transfer targets for `i`. */
|
|
130
|
+
connectedTo(index) {
|
|
131
|
+
return this.connections()[index] ?? NO_CONNECTIONS;
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Apply a drop from the dnd module. Reads the source/target columns from the
|
|
135
|
+
* event's containers (matched by their drop-list ids), updates the two-way
|
|
136
|
+
* `columns` model immutably, then emits {@link cardMoved}.
|
|
137
|
+
*/
|
|
138
|
+
onDrop(event) {
|
|
139
|
+
const ids = this.listIds();
|
|
140
|
+
const fromCol = ids.indexOf(event.previousContainer.id());
|
|
141
|
+
const toCol = ids.indexOf(event.container.id());
|
|
142
|
+
if (fromCol === -1 || toCol === -1)
|
|
143
|
+
return;
|
|
144
|
+
this.moveCard(fromCol, toCol, event.previousIndex, event.currentIndex);
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Immutably move the card at `fromIndex` of column `fromCol` to `toIndex` of
|
|
148
|
+
* column `toCol`, updating the model and emitting {@link cardMoved}. Column
|
|
149
|
+
* indices refer to positions in `columns()`.
|
|
150
|
+
*/
|
|
151
|
+
moveCard(fromCol, toCol, fromIndex, toIndex) {
|
|
152
|
+
const cols = this.columns();
|
|
153
|
+
const source = cols[fromCol];
|
|
154
|
+
const target = cols[toCol];
|
|
155
|
+
if (!source || !target)
|
|
156
|
+
return;
|
|
157
|
+
const card = source.cards[fromIndex];
|
|
158
|
+
if (!card)
|
|
159
|
+
return;
|
|
160
|
+
const next = cols.map((col, i) => {
|
|
161
|
+
if (fromCol === toCol && i === fromCol) {
|
|
162
|
+
const cards = [...col.cards];
|
|
163
|
+
mkMoveItemInArray(cards, fromIndex, toIndex);
|
|
164
|
+
return { ...col, cards };
|
|
165
|
+
}
|
|
166
|
+
if (i === fromCol) {
|
|
167
|
+
return { ...col, cards: col.cards.filter((_, idx) => idx !== fromIndex) };
|
|
168
|
+
}
|
|
169
|
+
if (i === toCol) {
|
|
170
|
+
const cards = [...col.cards];
|
|
171
|
+
cards.splice(Math.max(0, Math.min(toIndex, cards.length)), 0, card);
|
|
172
|
+
return { ...col, cards };
|
|
173
|
+
}
|
|
174
|
+
return col;
|
|
175
|
+
});
|
|
176
|
+
this.columns.set(next);
|
|
177
|
+
this.cardMoved.emit({
|
|
178
|
+
card,
|
|
179
|
+
from: source.id,
|
|
180
|
+
to: target.id,
|
|
181
|
+
fromIndex,
|
|
182
|
+
toIndex,
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.7", ngImport: i0, type: MkKanban, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
186
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.7", type: MkKanban, isStandalone: true, selector: "mk-kanban", inputs: { columns: { classPropertyName: "columns", publicName: "columns", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { columns: "columnsChange", cardMoved: "cardMoved" }, host: { classAttribute: "mk-kanban" }, queries: [{ propertyName: "cardDef", first: true, predicate: MkKanbanCardDef, descendants: true, isSignal: true }, { propertyName: "headerDef", first: true, predicate: MkKanbanColumnHeaderDef, descendants: true, isSignal: true }, { propertyName: "footerDef", first: true, predicate: MkKanbanColumnFooterDef, descendants: true, isSignal: true }, { propertyName: "templates", predicate: TemplateRef, isSignal: true }], ngImport: i0, template: "<div class=\"mk-kanban__board\">\n @for (column of columns(); track column.id; let colIndex = $index) {\n <section\n class=\"mk-kanban__column\"\n role=\"region\"\n [attr.aria-label]=\"column.title\"\n >\n <header class=\"mk-kanban__column-header\">\n @if (headerTemplate(); as header) {\n <ng-container\n [ngTemplateOutlet]=\"header\"\n [ngTemplateOutletContext]=\"{ $implicit: column, index: colIndex, count: column.cards.length }\"\n />\n } @else {\n <span class=\"mk-kanban__column-title\">{{ column.title }}</span>\n <span class=\"mk-kanban__column-count\" aria-hidden=\"true\">\n {{ column.cards.length }}\n </span>\n }\n </header>\n\n <div\n mkDropList\n class=\"mk-kanban__cards\"\n [mkDropListData]=\"column.cards\"\n [mkDropListId]=\"listIds()[colIndex]\"\n [mkDropListLabel]=\"column.title\"\n [mkDropListConnectedTo]=\"connectedTo(colIndex)\"\n [mkDropListDisabled]=\"disabled()\"\n mkDropListOrientation=\"vertical\"\n (mkDropListDropped)=\"onDrop($event)\"\n >\n @for (card of column.cards; track card.id) {\n <div\n mkDrag\n class=\"mk-kanban__card\"\n [mkDragData]=\"card\"\n [mkDragDisabled]=\"disabled()\"\n >\n <ng-container\n [ngTemplateOutlet]=\"cardTemplate() ?? defaultCard\"\n [ngTemplateOutletContext]=\"{ $implicit: card, column: column }\"\n />\n </div>\n }\n </div>\n\n @if (footerTemplate(); as footer) {\n <div class=\"mk-kanban__column-footer\">\n <ng-container\n [ngTemplateOutlet]=\"footer\"\n [ngTemplateOutletContext]=\"{ $implicit: column, index: colIndex, count: column.cards.length }\"\n />\n </div>\n }\n </section>\n }\n</div>\n\n<ng-template #defaultCard let-card>\n <span class=\"mk-kanban__card-title\">{{ card.title }}</span>\n</ng-template>\n", styles: [":host{display:block;color:var(--mk-text)}.mk-kanban__board{display:flex;gap:var(--mk-space-4);align-items:flex-start;overflow-x:auto;padding-bottom:var(--mk-space-2)}.mk-kanban__column{display:flex;flex-direction:column;flex:0 0 auto;width:17rem;max-height:100%;background-color:var(--mk-surface-2);border:var(--mk-border-width) solid var(--mk-border);border-radius:var(--mk-radius-lg)}.mk-kanban__column-header{display:flex;align-items:center;justify-content:space-between;gap:var(--mk-space-2);padding:var(--mk-space-3) var(--mk-space-4);font-weight:600;border-bottom:var(--mk-border-width) solid var(--mk-border)}.mk-kanban__column-title{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.mk-kanban__column-count{flex:0 0 auto;min-width:1.5rem;padding:0 var(--mk-space-2);font-size:.75rem;font-weight:600;line-height:1.5rem;text-align:center;color:var(--mk-text-muted);background-color:var(--mk-surface);border-radius:var(--mk-radius-md)}.mk-kanban__cards{display:flex;flex-direction:column;gap:var(--mk-space-2);flex:1 1 auto;min-height:var(--mk-space-8);padding:var(--mk-space-3);overflow-y:auto}.mk-kanban__column-footer{flex:0 0 auto;padding:var(--mk-space-2) var(--mk-space-3) var(--mk-space-3);border-top:var(--mk-border-width) solid var(--mk-border)}.mk-kanban__card{padding:var(--mk-space-3) var(--mk-space-4);background-color:var(--mk-surface);border:var(--mk-border-width) solid var(--mk-border);border-radius:var(--mk-radius-md);box-shadow:var(--mk-shadow-sm)}.mk-kanban__card:focus-visible{outline:var(--mk-focus-ring-width) solid var(--mk-focus-ring);outline-offset:var(--mk-focus-ring-offset)}.mk-kanban__card-title{display:block;min-width:0;overflow-wrap:anywhere}\n"], dependencies: [{ kind: "component", type: MkDropList, selector: "[mkDropList]", inputs: ["mkDropListData", "mkDropListId", "mkDropListConnectedTo", "mkDropListLabel", "mkDropListLabelledBy", "mkDropListOrientation", "mkDropListDisabled"], outputs: ["mkDropListDropped"], exportAs: ["mkDropList"] }, { kind: "component", type: MkDrag, selector: "[mkDrag]", inputs: ["mkDragData", "mkDragDisabled", "mkDragTouchDelay"], exportAs: ["mkDrag"] }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
187
|
+
}
|
|
188
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImport: i0, type: MkKanban, decorators: [{
|
|
189
|
+
type: Component,
|
|
190
|
+
args: [{ selector: 'mk-kanban', changeDetection: ChangeDetectionStrategy.OnPush, imports: [MkDropList, MkDrag, NgTemplateOutlet], host: { class: 'mk-kanban' }, template: "<div class=\"mk-kanban__board\">\n @for (column of columns(); track column.id; let colIndex = $index) {\n <section\n class=\"mk-kanban__column\"\n role=\"region\"\n [attr.aria-label]=\"column.title\"\n >\n <header class=\"mk-kanban__column-header\">\n @if (headerTemplate(); as header) {\n <ng-container\n [ngTemplateOutlet]=\"header\"\n [ngTemplateOutletContext]=\"{ $implicit: column, index: colIndex, count: column.cards.length }\"\n />\n } @else {\n <span class=\"mk-kanban__column-title\">{{ column.title }}</span>\n <span class=\"mk-kanban__column-count\" aria-hidden=\"true\">\n {{ column.cards.length }}\n </span>\n }\n </header>\n\n <div\n mkDropList\n class=\"mk-kanban__cards\"\n [mkDropListData]=\"column.cards\"\n [mkDropListId]=\"listIds()[colIndex]\"\n [mkDropListLabel]=\"column.title\"\n [mkDropListConnectedTo]=\"connectedTo(colIndex)\"\n [mkDropListDisabled]=\"disabled()\"\n mkDropListOrientation=\"vertical\"\n (mkDropListDropped)=\"onDrop($event)\"\n >\n @for (card of column.cards; track card.id) {\n <div\n mkDrag\n class=\"mk-kanban__card\"\n [mkDragData]=\"card\"\n [mkDragDisabled]=\"disabled()\"\n >\n <ng-container\n [ngTemplateOutlet]=\"cardTemplate() ?? defaultCard\"\n [ngTemplateOutletContext]=\"{ $implicit: card, column: column }\"\n />\n </div>\n }\n </div>\n\n @if (footerTemplate(); as footer) {\n <div class=\"mk-kanban__column-footer\">\n <ng-container\n [ngTemplateOutlet]=\"footer\"\n [ngTemplateOutletContext]=\"{ $implicit: column, index: colIndex, count: column.cards.length }\"\n />\n </div>\n }\n </section>\n }\n</div>\n\n<ng-template #defaultCard let-card>\n <span class=\"mk-kanban__card-title\">{{ card.title }}</span>\n</ng-template>\n", styles: [":host{display:block;color:var(--mk-text)}.mk-kanban__board{display:flex;gap:var(--mk-space-4);align-items:flex-start;overflow-x:auto;padding-bottom:var(--mk-space-2)}.mk-kanban__column{display:flex;flex-direction:column;flex:0 0 auto;width:17rem;max-height:100%;background-color:var(--mk-surface-2);border:var(--mk-border-width) solid var(--mk-border);border-radius:var(--mk-radius-lg)}.mk-kanban__column-header{display:flex;align-items:center;justify-content:space-between;gap:var(--mk-space-2);padding:var(--mk-space-3) var(--mk-space-4);font-weight:600;border-bottom:var(--mk-border-width) solid var(--mk-border)}.mk-kanban__column-title{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.mk-kanban__column-count{flex:0 0 auto;min-width:1.5rem;padding:0 var(--mk-space-2);font-size:.75rem;font-weight:600;line-height:1.5rem;text-align:center;color:var(--mk-text-muted);background-color:var(--mk-surface);border-radius:var(--mk-radius-md)}.mk-kanban__cards{display:flex;flex-direction:column;gap:var(--mk-space-2);flex:1 1 auto;min-height:var(--mk-space-8);padding:var(--mk-space-3);overflow-y:auto}.mk-kanban__column-footer{flex:0 0 auto;padding:var(--mk-space-2) var(--mk-space-3) var(--mk-space-3);border-top:var(--mk-border-width) solid var(--mk-border)}.mk-kanban__card{padding:var(--mk-space-3) var(--mk-space-4);background-color:var(--mk-surface);border:var(--mk-border-width) solid var(--mk-border);border-radius:var(--mk-radius-md);box-shadow:var(--mk-shadow-sm)}.mk-kanban__card:focus-visible{outline:var(--mk-focus-ring-width) solid var(--mk-focus-ring);outline-offset:var(--mk-focus-ring-offset)}.mk-kanban__card-title{display:block;min-width:0;overflow-wrap:anywhere}\n"] }]
|
|
191
|
+
}], propDecorators: { columns: [{ type: i0.Input, args: [{ isSignal: true, alias: "columns", required: false }] }, { type: i0.Output, args: ["columnsChange"] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], cardMoved: [{ type: i0.Output, args: ["cardMoved"] }], cardDef: [{ type: i0.ContentChild, args: [i0.forwardRef(() => MkKanbanCardDef), { isSignal: true }] }], headerDef: [{ type: i0.ContentChild, args: [i0.forwardRef(() => MkKanbanColumnHeaderDef), { isSignal: true }] }], footerDef: [{ type: i0.ContentChild, args: [i0.forwardRef(() => MkKanbanColumnFooterDef), { isSignal: true }] }], templates: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => TemplateRef), { isSignal: true }] }] } });
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* @mk-kit/ui/kanban — the board (columns + drag-and-drop cards). Its own
|
|
195
|
+
* entry since 0.53 because it is the only `data` component that needs
|
|
196
|
+
* `@mk-kit/ui/dnd`; keeping it in `data` shipped dnd to every page that
|
|
197
|
+
* rendered a badge.
|
|
198
|
+
*/
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* Generated bundle index. Do not edit.
|
|
202
|
+
*/
|
|
203
|
+
|
|
204
|
+
export { MkKanban, MkKanbanCardDef, MkKanbanColumnFooterDef, MkKanbanColumnHeaderDef };
|
|
205
|
+
//# sourceMappingURL=mk-kit-ui-kanban.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mk-kit-ui-kanban.mjs","sources":["../../../projects/mk-kit/kanban/kanban.ts","../../../projects/mk-kit/kanban/kanban.html","../../../projects/mk-kit/kanban/index.ts","../../../projects/mk-kit/kanban/mk-kit-ui-kanban.ts"],"sourcesContent":["import { NgTemplateOutlet } from '@angular/common';\nimport {\n ChangeDetectionStrategy,\n Component,\n Directive,\n TemplateRef,\n booleanAttribute,\n computed,\n contentChild,\n contentChildren,\n inject,\n input,\n model,\n output,\n} from '@angular/core';\nimport { mkUniqueId } from '@mk-kit/ui/core';\nimport { MkDrag, MkDropList, mkMoveItemInArray } from '@mk-kit/ui/dnd';\nimport type { MkDropEvent } from '@mk-kit/ui/dnd';\n\n/** Stable empty fallback so out-of-range lookups don't allocate per call. */\nconst NO_CONNECTIONS: string[] = [];\n\n/** A single draggable card on a {@link MkKanban} board. */\nexport interface MkKanbanCard {\n /** Stable identity used for tracking and move events. */\n id: unknown;\n /** Text shown by the default card renderer. */\n title: string;\n /** Arbitrary extra fields for custom card templates. */\n [key: string]: unknown;\n}\n\n/** A column (bucket) of {@link MkKanbanCard}s on a {@link MkKanban} board. */\nexport interface MkKanbanColumn {\n /** Stable identity used for tracking and move events. */\n id: unknown;\n /** Heading shown in the column header and used as its aria-label. */\n title: string;\n /** The ordered cards contained in this column. */\n cards: MkKanbanCard[];\n}\n\n/**\n * Emitted by {@link MkKanban} after a card has been moved (between or within\n * columns) and the two-way `columns` model has been updated.\n */\nexport interface MkKanbanCardMovedEvent {\n /** The card that was moved. */\n card: MkKanbanCard;\n /** `id` of the column the card came from. */\n from: unknown;\n /** `id` of the column the card was dropped into. */\n to: unknown;\n /** The card's index within the source column before the move. */\n fromIndex: number;\n /** The card's index within the destination column after the move. */\n toIndex: number;\n}\n\n/** Context handed to the column header / footer templates. */\nexport interface MkKanbanColumnContext {\n /** The column being rendered. */\n $implicit: MkKanbanColumn;\n /** Its position in `columns`. */\n index: number;\n /** `column.cards.length`, for a count badge. */\n count: number;\n}\n\n/**\n * Marks the projected card renderer explicitly:\n * `<ng-template mkKanbanCard let-card let-column=\"column\">`. Without it the\n * first plain `<ng-template>` inside `<mk-kanban>` is used, as before 0.54.\n */\n@Directive({ selector: 'ng-template[mkKanbanCard]' })\nexport class MkKanbanCardDef {\n readonly template = inject<TemplateRef<unknown>>(TemplateRef);\n}\n\n/**\n * Replaces the default column header (title + count):\n * `<ng-template mkKanbanColumnHeader let-column let-count=\"count\">`.\n */\n@Directive({ selector: 'ng-template[mkKanbanColumnHeader]' })\nexport class MkKanbanColumnHeaderDef {\n readonly template = inject<TemplateRef<MkKanbanColumnContext>>(TemplateRef);\n}\n\n/**\n * Rendered under each column's card list — a \"quick add\" input, say:\n * `<ng-template mkKanbanColumnFooter let-column>`.\n */\n@Directive({ selector: 'ng-template[mkKanbanColumnFooter]' })\nexport class MkKanbanColumnFooterDef {\n readonly template = inject<TemplateRef<MkKanbanColumnContext>>(TemplateRef);\n}\n\n/**\n * Kanban board — a horizontal row of columns whose cards can be dragged and\n * reordered within a column or transferred between columns. Built on the dnd\n * group's connected `[mkDropList]` / `[mkDrag]` directives, so it inherits both\n * pointer and keyboard dragging (WCAG 2.1.1) for free.\n *\n * Bind `columns` two-way; the model is updated immutably on each drop. Project\n * an `<ng-template>` to render custom card content — it receives the card as\n * `$implicit` and the owning column as `column`. Column headers and footers\n * take their own templates (`mkKanbanColumnHeader` / `mkKanbanColumnFooter`,\n * context `{ $implicit: column, index, count }`):\n *\n * ```html\n * <mk-kanban [(columns)]=\"board\" (cardMoved)=\"onMoved($event)\">\n * <ng-template mkKanbanColumnHeader let-column let-count=\"count\">\n * <strong>{{ column.title }}</strong> {{ count }}\n * <button (click)=\"rename(column)\">Rename</button>\n * </ng-template>\n * <ng-template mkKanbanCard let-card let-column=\"column\">\n * <strong>{{ card.title }}</strong>\n * </ng-template>\n * <ng-template mkKanbanColumnFooter let-column>\n * <button (click)=\"add(column)\">+ Card</button>\n * </ng-template>\n * </mk-kanban>\n * ```\n */\n@Component({\n selector: 'mk-kanban',\n templateUrl: './kanban.html',\n styleUrl: './kanban.scss',\n changeDetection: ChangeDetectionStrategy.OnPush,\n imports: [MkDropList, MkDrag, NgTemplateOutlet],\n host: { class: 'mk-kanban' },\n})\nexport class MkKanban {\n /** The board's columns and their cards (two-way). Updated on every drop. */\n readonly columns = model<MkKanbanColumn[]>([]);\n\n /** Disable all dragging on the board. */\n readonly disabled = input(false, { transform: booleanAttribute });\n\n /** Emitted after the model is updated when a card is moved. */\n readonly cardMoved = output<MkKanbanCardMovedEvent>();\n\n private readonly cardDef = contentChild(MkKanbanCardDef);\n private readonly headerDef = contentChild(MkKanbanColumnHeaderDef);\n private readonly footerDef = contentChild(MkKanbanColumnFooterDef);\n /** Every projected template, for the pre-0.54 \"first plain template is the card\" rule. */\n private readonly templates = contentChildren(TemplateRef);\n\n /** Consumer-projected card renderer: `[mkKanbanCard]`, else the first template that is not a header/footer. */\n protected readonly cardTemplate = computed<TemplateRef<unknown> | undefined>(() => {\n const explicit = this.cardDef()?.template;\n if (explicit) return explicit;\n // A query and a directive's injected TemplateRef are different instances\n // of the same template — compare their anchor nodes.\n const taken = new Set(\n [this.headerDef(), this.footerDef()].map((d) => d?.template.elementRef.nativeElement as Node | undefined),\n );\n return this.templates().find((t) => !taken.has(t.elementRef.nativeElement as Node));\n });\n /** Optional column header renderer (replaces title + count). */\n protected readonly headerTemplate = computed(() => this.headerDef()?.template);\n /** Optional column footer renderer (under the card list). */\n protected readonly footerTemplate = computed(() => this.footerDef()?.template);\n\n /** Board-scoped prefix so this board's drop-list ids never collide. */\n private readonly boardId = mkUniqueId('mk-kanban');\n\n /** Column count as its own computed so id arrays survive card-only updates. */\n private readonly columnCount = computed(() => this.columns().length);\n\n /** Stable drop-list id per column, positionally matched to `columns()`. */\n protected readonly listIds = computed(() =>\n Array.from({ length: this.columnCount() }, (_, i) => `${this.boardId}-col-${i}`),\n );\n\n /**\n * Per-column connected-target ids (every other column's list). One stable\n * array per column, recomputed only when the column count changes — a fresh\n * array per change-detection pass would dirty each `MkDropList`'s\n * `connectedTo` input identity on every cycle.\n */\n private readonly connections = computed(() => {\n const ids = this.listIds();\n return ids.map((_, i) => ids.filter((_, j) => j !== i));\n });\n\n /** Ids of every other column's list — the connected transfer targets for `i`. */\n protected connectedTo(index: number): string[] {\n return this.connections()[index] ?? NO_CONNECTIONS;\n }\n\n /**\n * Apply a drop from the dnd module. Reads the source/target columns from the\n * event's containers (matched by their drop-list ids), updates the two-way\n * `columns` model immutably, then emits {@link cardMoved}.\n */\n protected onDrop(event: MkDropEvent<MkKanbanCard>): void {\n const ids = this.listIds();\n const fromCol = ids.indexOf(event.previousContainer.id());\n const toCol = ids.indexOf(event.container.id());\n if (fromCol === -1 || toCol === -1) return;\n this.moveCard(fromCol, toCol, event.previousIndex, event.currentIndex);\n }\n\n /**\n * Immutably move the card at `fromIndex` of column `fromCol` to `toIndex` of\n * column `toCol`, updating the model and emitting {@link cardMoved}. Column\n * indices refer to positions in `columns()`.\n */\n private moveCard(\n fromCol: number,\n toCol: number,\n fromIndex: number,\n toIndex: number,\n ): void {\n const cols = this.columns();\n const source = cols[fromCol];\n const target = cols[toCol];\n if (!source || !target) return;\n const card = source.cards[fromIndex];\n if (!card) return;\n\n const next = cols.map((col, i) => {\n if (fromCol === toCol && i === fromCol) {\n const cards = [...col.cards];\n mkMoveItemInArray(cards, fromIndex, toIndex);\n return { ...col, cards };\n }\n if (i === fromCol) {\n return { ...col, cards: col.cards.filter((_, idx) => idx !== fromIndex) };\n }\n if (i === toCol) {\n const cards = [...col.cards];\n cards.splice(Math.max(0, Math.min(toIndex, cards.length)), 0, card);\n return { ...col, cards };\n }\n return col;\n });\n\n this.columns.set(next);\n this.cardMoved.emit({\n card,\n from: source.id,\n to: target.id,\n fromIndex,\n toIndex,\n });\n }\n}\n","<div class=\"mk-kanban__board\">\n @for (column of columns(); track column.id; let colIndex = $index) {\n <section\n class=\"mk-kanban__column\"\n role=\"region\"\n [attr.aria-label]=\"column.title\"\n >\n <header class=\"mk-kanban__column-header\">\n @if (headerTemplate(); as header) {\n <ng-container\n [ngTemplateOutlet]=\"header\"\n [ngTemplateOutletContext]=\"{ $implicit: column, index: colIndex, count: column.cards.length }\"\n />\n } @else {\n <span class=\"mk-kanban__column-title\">{{ column.title }}</span>\n <span class=\"mk-kanban__column-count\" aria-hidden=\"true\">\n {{ column.cards.length }}\n </span>\n }\n </header>\n\n <div\n mkDropList\n class=\"mk-kanban__cards\"\n [mkDropListData]=\"column.cards\"\n [mkDropListId]=\"listIds()[colIndex]\"\n [mkDropListLabel]=\"column.title\"\n [mkDropListConnectedTo]=\"connectedTo(colIndex)\"\n [mkDropListDisabled]=\"disabled()\"\n mkDropListOrientation=\"vertical\"\n (mkDropListDropped)=\"onDrop($event)\"\n >\n @for (card of column.cards; track card.id) {\n <div\n mkDrag\n class=\"mk-kanban__card\"\n [mkDragData]=\"card\"\n [mkDragDisabled]=\"disabled()\"\n >\n <ng-container\n [ngTemplateOutlet]=\"cardTemplate() ?? defaultCard\"\n [ngTemplateOutletContext]=\"{ $implicit: card, column: column }\"\n />\n </div>\n }\n </div>\n\n @if (footerTemplate(); as footer) {\n <div class=\"mk-kanban__column-footer\">\n <ng-container\n [ngTemplateOutlet]=\"footer\"\n [ngTemplateOutletContext]=\"{ $implicit: column, index: colIndex, count: column.cards.length }\"\n />\n </div>\n }\n </section>\n }\n</div>\n\n<ng-template #defaultCard let-card>\n <span class=\"mk-kanban__card-title\">{{ card.title }}</span>\n</ng-template>\n","/**\n * @mk-kit/ui/kanban — the board (columns + drag-and-drop cards). Its own\n * entry since 0.53 because it is the only `data` component that needs\n * `@mk-kit/ui/dnd`; keeping it in `data` shipped dnd to every page that\n * rendered a badge.\n */\nexport * from './kanban';\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,318 @@
|
|
|
1
|
+
import * as i0 from '@angular/core';
|
|
2
|
+
import { inject, input, ChangeDetectionStrategy, Component, booleanAttribute, ElementRef, Renderer2, DOCUMENT, PLATFORM_ID, numberAttribute, computed, effect, Directive } from '@angular/core';
|
|
3
|
+
import { MK_I18N, mkUniqueId } from '@mk-kit/ui/core';
|
|
4
|
+
import { isPlatformBrowser } from '@angular/common';
|
|
5
|
+
import { MkIcon } from '@mk-kit/ui/icon';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Spinner — a circular indeterminate loading indicator. Exposes `role="status"`
|
|
9
|
+
* with a visually-hidden label so assistive tech announces the loading state.
|
|
10
|
+
* The animation slows under `prefers-reduced-motion`.
|
|
11
|
+
*
|
|
12
|
+
* ```html
|
|
13
|
+
* <mk-spinner />
|
|
14
|
+
* <mk-spinner size="lg" tone="neutral" label="Fetching results" />
|
|
15
|
+
* ```
|
|
16
|
+
*/
|
|
17
|
+
class MkSpinner {
|
|
18
|
+
i18n = inject(MK_I18N);
|
|
19
|
+
/** Size scale. */
|
|
20
|
+
size = input('md', /* @ts-ignore */
|
|
21
|
+
...(ngDevMode ? [{ debugName: "size" }] : /* istanbul ignore next */ []));
|
|
22
|
+
/** Semantic color tone. */
|
|
23
|
+
tone = input('primary', /* @ts-ignore */
|
|
24
|
+
...(ngDevMode ? [{ debugName: "tone" }] : /* istanbul ignore next */ []));
|
|
25
|
+
/** Visually-hidden status label announced to screen readers. */
|
|
26
|
+
label = input(this.i18n.loading, /* @ts-ignore */
|
|
27
|
+
...(ngDevMode ? [{ debugName: "label" }] : /* istanbul ignore next */ []));
|
|
28
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.7", ngImport: i0, type: MkSpinner, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
29
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.0.7", type: MkSpinner, isStandalone: true, selector: "mk-spinner", inputs: { size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, tone: { classPropertyName: "tone", publicName: "tone", isSignal: true, isRequired: false, transformFunction: null }, label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null } }, host: { attributes: { "role": "status" }, properties: { "class.mk-spinner--sm": "size() === 'sm'", "class.mk-spinner--md": "size() === 'md'", "class.mk-spinner--lg": "size() === 'lg'", "attr.data-tone": "tone()" }, classAttribute: "mk-spinner" }, ngImport: i0, template: "<span class=\"mk-spinner__circle\" aria-hidden=\"true\"></span>\n<span class=\"mk-spinner__sr\">{{ label() }}</span>\n", styles: [":host{--_color: var(--mk-primary);--_size: var(--mk-spinner-size, 1.5rem);--_thickness: var(--mk-spinner-thickness, 2px);display:inline-flex;align-items:center;justify-content:center;vertical-align:middle;color:var(--_color)}:host([data-tone=primary]){--_color: var(--mk-primary)}:host([data-tone=neutral]){--_color: var(--mk-text-muted)}:host([data-tone=success]){--_color: var(--mk-success)}:host([data-tone=warning]){--_color: var(--mk-warning)}:host([data-tone=danger]){--_color: var(--mk-danger)}:host([data-tone=info]){--_color: var(--mk-info)}:host(.mk-spinner--sm){--_size: var(--mk-spinner-size, 1rem);--_thickness: var(--mk-spinner-thickness, 2px)}:host(.mk-spinner--md){--_size: var(--mk-spinner-size, 1.5rem);--_thickness: var(--mk-spinner-thickness, 2px)}:host(.mk-spinner--lg){--_size: var(--mk-spinner-size, 2.25rem);--_thickness: var(--mk-spinner-thickness, 3px)}.mk-spinner__circle{display:block;width:var(--_size);height:var(--_size);border:var(--_thickness) solid currentColor;border-right-color:transparent;border-radius:50%;opacity:.9;animation:mk-spinner-spin .7s linear infinite}@keyframes mk-spinner-spin{to{transform:rotate(360deg)}}@media(prefers-reduced-motion:reduce){.mk-spinner__circle{animation-duration:1.6s}}.mk-spinner__sr{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap;border:0}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
30
|
+
}
|
|
31
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImport: i0, type: MkSpinner, decorators: [{
|
|
32
|
+
type: Component,
|
|
33
|
+
args: [{ selector: 'mk-spinner', changeDetection: ChangeDetectionStrategy.OnPush, host: {
|
|
34
|
+
class: 'mk-spinner',
|
|
35
|
+
role: 'status',
|
|
36
|
+
'[class.mk-spinner--sm]': "size() === 'sm'",
|
|
37
|
+
'[class.mk-spinner--md]': "size() === 'md'",
|
|
38
|
+
'[class.mk-spinner--lg]': "size() === 'lg'",
|
|
39
|
+
'[attr.data-tone]': 'tone()',
|
|
40
|
+
}, template: "<span class=\"mk-spinner__circle\" aria-hidden=\"true\"></span>\n<span class=\"mk-spinner__sr\">{{ label() }}</span>\n", styles: [":host{--_color: var(--mk-primary);--_size: var(--mk-spinner-size, 1.5rem);--_thickness: var(--mk-spinner-thickness, 2px);display:inline-flex;align-items:center;justify-content:center;vertical-align:middle;color:var(--_color)}:host([data-tone=primary]){--_color: var(--mk-primary)}:host([data-tone=neutral]){--_color: var(--mk-text-muted)}:host([data-tone=success]){--_color: var(--mk-success)}:host([data-tone=warning]){--_color: var(--mk-warning)}:host([data-tone=danger]){--_color: var(--mk-danger)}:host([data-tone=info]){--_color: var(--mk-info)}:host(.mk-spinner--sm){--_size: var(--mk-spinner-size, 1rem);--_thickness: var(--mk-spinner-thickness, 2px)}:host(.mk-spinner--md){--_size: var(--mk-spinner-size, 1.5rem);--_thickness: var(--mk-spinner-thickness, 2px)}:host(.mk-spinner--lg){--_size: var(--mk-spinner-size, 2.25rem);--_thickness: var(--mk-spinner-thickness, 3px)}.mk-spinner__circle{display:block;width:var(--_size);height:var(--_size);border:var(--_thickness) solid currentColor;border-right-color:transparent;border-radius:50%;opacity:.9;animation:mk-spinner-spin .7s linear infinite}@keyframes mk-spinner-spin{to{transform:rotate(360deg)}}@media(prefers-reduced-motion:reduce){.mk-spinner__circle{animation-duration:1.6s}}.mk-spinner__sr{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap;border:0}\n"] }]
|
|
41
|
+
}], propDecorators: { size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], tone: [{ type: i0.Input, args: [{ isSignal: true, alias: "tone", required: false }] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }] } });
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Badge — a compact status pill, typically for counts or short state labels.
|
|
45
|
+
* Set `dot` for a minimal notification indicator with no text.
|
|
46
|
+
*
|
|
47
|
+
* ```html
|
|
48
|
+
* <mk-badge tone="success">Active</mk-badge>
|
|
49
|
+
* <mk-badge tone="danger" variant="solid">3</mk-badge>
|
|
50
|
+
* <mk-badge tone="danger" dot aria-label="Unread messages" />
|
|
51
|
+
* ```
|
|
52
|
+
*/
|
|
53
|
+
class MkBadge {
|
|
54
|
+
/** Semantic color tone. */
|
|
55
|
+
tone = input('primary', /* @ts-ignore */
|
|
56
|
+
...(ngDevMode ? [{ debugName: "tone" }] : /* istanbul ignore next */ []));
|
|
57
|
+
/** Visual treatment. */
|
|
58
|
+
variant = input('soft', /* @ts-ignore */
|
|
59
|
+
...(ngDevMode ? [{ debugName: "variant" }] : /* istanbul ignore next */ []));
|
|
60
|
+
/** Size scale. */
|
|
61
|
+
size = input('md', /* @ts-ignore */
|
|
62
|
+
...(ngDevMode ? [{ debugName: "size" }] : /* istanbul ignore next */ []));
|
|
63
|
+
/** Render as a tiny dot (no text) for notification indicators. */
|
|
64
|
+
dot = input(false, { ...(ngDevMode ? { debugName: "dot" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
65
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.7", ngImport: i0, type: MkBadge, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
66
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.7", type: MkBadge, isStandalone: true, selector: "mk-badge", inputs: { tone: { classPropertyName: "tone", publicName: "tone", isSignal: true, isRequired: false, transformFunction: null }, variant: { classPropertyName: "variant", publicName: "variant", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, dot: { classPropertyName: "dot", publicName: "dot", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class.mk-badge--sm": "size() === 'sm'", "class.mk-badge--md": "size() === 'md'", "class.mk-badge--lg": "size() === 'lg'", "class.mk-badge--solid": "variant() === 'solid'", "class.mk-badge--soft": "variant() === 'soft'", "class.mk-badge--outline": "variant() === 'outline'", "class.mk-badge--dot": "dot()", "attr.data-tone": "tone()" }, classAttribute: "mk-badge" }, ngImport: i0, template: "@if (!dot()) {\n <ng-content />\n}\n", styles: ["@charset \"UTF-8\";:host{--_main: var(--mk-primary);--_contrast: var(--mk-primary-contrast);--_subtle: var(--mk-primary-subtle);--_subtle-text: var(--mk-primary-subtle-text);display:inline-flex;align-items:center;justify-content:center;gap:var(--mk-space-1);height:1.375rem;min-width:1.375rem;padding:0 var(--mk-space-2);border:var(--mk-border-width) solid transparent;border-radius:var(--mk-radius-pill);font-family:inherit;font-size:var(--mk-font-size-xs);font-weight:var(--mk-font-weight-medium);line-height:1;white-space:nowrap;vertical-align:middle}:host([data-tone=primary]){--_main: var(--mk-primary);--_hover: var(--mk-primary-hover);--_active: var(--mk-primary-active);--_contrast: var(--mk-primary-contrast);--_subtle: var(--mk-primary-subtle);--_subtle-text: var(--mk-primary-subtle-text)}:host([data-tone=success]){--_main: var(--mk-success);--_hover: var(--mk-success-hover);--_active: var(--mk-success-hover);--_contrast: var(--mk-success-contrast);--_subtle: var(--mk-success-subtle);--_subtle-text: var(--mk-success-subtle-text)}:host([data-tone=warning]){--_main: var(--mk-warning);--_hover: var(--mk-warning-hover);--_active: var(--mk-warning-hover);--_contrast: var(--mk-warning-contrast);--_subtle: var(--mk-warning-subtle);--_subtle-text: var(--mk-warning-subtle-text)}:host([data-tone=danger]){--_main: var(--mk-danger);--_hover: var(--mk-danger-hover);--_active: var(--mk-danger-hover);--_contrast: var(--mk-danger-contrast);--_subtle: var(--mk-danger-subtle);--_subtle-text: var(--mk-danger-subtle-text)}:host([data-tone=info]){--_main: var(--mk-info);--_hover: var(--mk-info-hover);--_active: var(--mk-info-hover);--_contrast: var(--mk-info-contrast);--_subtle: var(--mk-info-subtle);--_subtle-text: var(--mk-info-subtle-text)}:host([data-tone=neutral]){--_main: var(--mk-surface-inverse);--_hover: var(--mk-surface-inverse);--_active: var(--mk-surface-inverse);--_contrast: var(--mk-text-inverse);--_subtle: var(--mk-neutral-subtle);--_subtle-text: var(--mk-neutral-subtle-text)}:host(.mk-badge--sm){height:1.125rem;min-width:1.125rem;padding:0 var(--mk-space-1);font-size:var(--mk-font-size-xs)}:host(.mk-badge--lg){height:1.625rem;min-width:1.625rem;padding:0 var(--mk-space-3);font-size:var(--mk-font-size-sm)}:host(.mk-badge--solid){background-color:var(--_main);color:var(--_contrast)}:host(.mk-badge--soft){background-color:var(--_subtle);color:var(--_subtle-text)}:host(.mk-badge--outline){background-color:transparent;color:var(--_subtle-text);border-color:color-mix(in srgb,var(--_main) 42%,var(--mk-border-strong))}:host(.mk-badge--dot){width:.625rem;height:.625rem;min-width:0;padding:0;border-radius:var(--mk-radius-circle)}:host(.mk-badge--dot.mk-badge--sm){width:.5rem;height:.5rem}:host(.mk-badge--dot.mk-badge--lg){width:.75rem;height:.75rem}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
67
|
+
}
|
|
68
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImport: i0, type: MkBadge, decorators: [{
|
|
69
|
+
type: Component,
|
|
70
|
+
args: [{ selector: 'mk-badge', changeDetection: ChangeDetectionStrategy.OnPush, host: {
|
|
71
|
+
class: 'mk-badge',
|
|
72
|
+
'[class.mk-badge--sm]': "size() === 'sm'",
|
|
73
|
+
'[class.mk-badge--md]': "size() === 'md'",
|
|
74
|
+
'[class.mk-badge--lg]': "size() === 'lg'",
|
|
75
|
+
'[class.mk-badge--solid]': "variant() === 'solid'",
|
|
76
|
+
'[class.mk-badge--soft]': "variant() === 'soft'",
|
|
77
|
+
'[class.mk-badge--outline]': "variant() === 'outline'",
|
|
78
|
+
'[class.mk-badge--dot]': 'dot()',
|
|
79
|
+
'[attr.data-tone]': 'tone()',
|
|
80
|
+
}, template: "@if (!dot()) {\n <ng-content />\n}\n", styles: ["@charset \"UTF-8\";:host{--_main: var(--mk-primary);--_contrast: var(--mk-primary-contrast);--_subtle: var(--mk-primary-subtle);--_subtle-text: var(--mk-primary-subtle-text);display:inline-flex;align-items:center;justify-content:center;gap:var(--mk-space-1);height:1.375rem;min-width:1.375rem;padding:0 var(--mk-space-2);border:var(--mk-border-width) solid transparent;border-radius:var(--mk-radius-pill);font-family:inherit;font-size:var(--mk-font-size-xs);font-weight:var(--mk-font-weight-medium);line-height:1;white-space:nowrap;vertical-align:middle}:host([data-tone=primary]){--_main: var(--mk-primary);--_hover: var(--mk-primary-hover);--_active: var(--mk-primary-active);--_contrast: var(--mk-primary-contrast);--_subtle: var(--mk-primary-subtle);--_subtle-text: var(--mk-primary-subtle-text)}:host([data-tone=success]){--_main: var(--mk-success);--_hover: var(--mk-success-hover);--_active: var(--mk-success-hover);--_contrast: var(--mk-success-contrast);--_subtle: var(--mk-success-subtle);--_subtle-text: var(--mk-success-subtle-text)}:host([data-tone=warning]){--_main: var(--mk-warning);--_hover: var(--mk-warning-hover);--_active: var(--mk-warning-hover);--_contrast: var(--mk-warning-contrast);--_subtle: var(--mk-warning-subtle);--_subtle-text: var(--mk-warning-subtle-text)}:host([data-tone=danger]){--_main: var(--mk-danger);--_hover: var(--mk-danger-hover);--_active: var(--mk-danger-hover);--_contrast: var(--mk-danger-contrast);--_subtle: var(--mk-danger-subtle);--_subtle-text: var(--mk-danger-subtle-text)}:host([data-tone=info]){--_main: var(--mk-info);--_hover: var(--mk-info-hover);--_active: var(--mk-info-hover);--_contrast: var(--mk-info-contrast);--_subtle: var(--mk-info-subtle);--_subtle-text: var(--mk-info-subtle-text)}:host([data-tone=neutral]){--_main: var(--mk-surface-inverse);--_hover: var(--mk-surface-inverse);--_active: var(--mk-surface-inverse);--_contrast: var(--mk-text-inverse);--_subtle: var(--mk-neutral-subtle);--_subtle-text: var(--mk-neutral-subtle-text)}:host(.mk-badge--sm){height:1.125rem;min-width:1.125rem;padding:0 var(--mk-space-1);font-size:var(--mk-font-size-xs)}:host(.mk-badge--lg){height:1.625rem;min-width:1.625rem;padding:0 var(--mk-space-3);font-size:var(--mk-font-size-sm)}:host(.mk-badge--solid){background-color:var(--_main);color:var(--_contrast)}:host(.mk-badge--soft){background-color:var(--_subtle);color:var(--_subtle-text)}:host(.mk-badge--outline){background-color:transparent;color:var(--_subtle-text);border-color:color-mix(in srgb,var(--_main) 42%,var(--mk-border-strong))}:host(.mk-badge--dot){width:.625rem;height:.625rem;min-width:0;padding:0;border-radius:var(--mk-radius-circle)}:host(.mk-badge--dot.mk-badge--sm){width:.5rem;height:.5rem}:host(.mk-badge--dot.mk-badge--lg){width:.75rem;height:.75rem}\n"] }]
|
|
81
|
+
}], propDecorators: { tone: [{ type: i0.Input, args: [{ isSignal: true, alias: "tone", required: false }] }], variant: [{ type: i0.Input, args: [{ isSignal: true, alias: "variant", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], dot: [{ type: i0.Input, args: [{ isSignal: true, alias: "dot", required: false }] }] } });
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Badge overlay — anchors a small badge (count, dot or short text) to a corner
|
|
85
|
+
* of ANY element: an icon button, an avatar, a tab label. The host stays a
|
|
86
|
+
* single tab stop; the badge is a decorative, non-focusable child.
|
|
87
|
+
*
|
|
88
|
+
* - Numbers above `mkBadgeOverlayMax` (default 99) collapse to `99+`.
|
|
89
|
+
* - `mkBadgeOverlayDot` renders a textless dot ("something is new").
|
|
90
|
+
* - `mkBadgeOverlayAriaLabel` is the screen-reader text ("3 unread"). It is
|
|
91
|
+
* rendered in a visually hidden span and wired to the host with
|
|
92
|
+
* `aria-describedby`, so it is announced after the host's own name even
|
|
93
|
+
* when the host has an `aria-label`. When it is given, the visible badge is
|
|
94
|
+
* `aria-hidden` so the count is not read twice.
|
|
95
|
+
* - Positions use logical insets, so `top-end` sits at the top-right in LTR
|
|
96
|
+
* and the top-left in RTL.
|
|
97
|
+
*
|
|
98
|
+
* The badge is painted with the global `.mk-badge-overlay` styles from
|
|
99
|
+
* `@mk-kit/ui/styles.css` and follows the `--mk-*` tone tokens.
|
|
100
|
+
*
|
|
101
|
+
* ```html
|
|
102
|
+
* <button mkButton iconOnly aria-label="Notifications"
|
|
103
|
+
* [mkBadgeOverlay]="unread()" mkBadgeOverlayTone="danger"
|
|
104
|
+
* [mkBadgeOverlayAriaLabel]="unread() + ' unread'">
|
|
105
|
+
* <mk-icon name="bell" />
|
|
106
|
+
* </button>
|
|
107
|
+
*
|
|
108
|
+
* <mk-avatar name="Ada Lovelace" mkBadgeOverlay mkBadgeOverlayDot
|
|
109
|
+
* mkBadgeOverlayTone="success" mkBadgeOverlayPosition="bottom-end" />
|
|
110
|
+
* ```
|
|
111
|
+
*/
|
|
112
|
+
class MkBadgeOverlay {
|
|
113
|
+
host = inject(ElementRef);
|
|
114
|
+
renderer = inject(Renderer2);
|
|
115
|
+
document = inject(DOCUMENT);
|
|
116
|
+
isBrowser = isPlatformBrowser(inject(PLATFORM_ID));
|
|
117
|
+
/**
|
|
118
|
+
* Badge content — a count or a short label. `null` / empty hides the badge
|
|
119
|
+
* unless `mkBadgeOverlayDot` is set. Numeric strings are treated as numbers
|
|
120
|
+
* so `mkBadgeOverlay="120"` also collapses to `99+`.
|
|
121
|
+
*/
|
|
122
|
+
content = input(null, { ...(ngDevMode ? { debugName: "content" } : /* istanbul ignore next */ {}), alias: 'mkBadgeOverlay' });
|
|
123
|
+
/** Corner the badge is anchored to (logical: `end` flips in RTL). */
|
|
124
|
+
position = input('top-end', { ...(ngDevMode ? { debugName: "position" } : /* istanbul ignore next */ {}), alias: 'mkBadgeOverlayPosition' });
|
|
125
|
+
/** Semantic color tone (same scale as `mk-badge`). */
|
|
126
|
+
tone = input('primary', { ...(ngDevMode ? { debugName: "tone" } : /* istanbul ignore next */ {}), alias: 'mkBadgeOverlayTone' });
|
|
127
|
+
/** Render a textless dot instead of the content. */
|
|
128
|
+
dot = input(false, { ...(ngDevMode ? { debugName: "dot" } : /* istanbul ignore next */ {}), alias: 'mkBadgeOverlayDot',
|
|
129
|
+
transform: booleanAttribute });
|
|
130
|
+
/** Numbers above this render as `<max>+`. `0` disables the cap. */
|
|
131
|
+
max = input(99, { ...(ngDevMode ? { debugName: "max" } : /* istanbul ignore next */ {}), alias: 'mkBadgeOverlayMax',
|
|
132
|
+
transform: numberAttribute });
|
|
133
|
+
/** Hide the badge while keeping the directive (and its aria wiring) in place. */
|
|
134
|
+
hidden = input(false, { ...(ngDevMode ? { debugName: "hidden" } : /* istanbul ignore next */ {}), alias: 'mkBadgeOverlayHidden',
|
|
135
|
+
transform: booleanAttribute });
|
|
136
|
+
/**
|
|
137
|
+
* Screen-reader text for the badge, e.g. `"3 unread"`. Announced as the
|
|
138
|
+
* host's description; the visible badge becomes `aria-hidden`.
|
|
139
|
+
*/
|
|
140
|
+
ariaLabel = input('', { ...(ngDevMode ? { debugName: "ariaLabel" } : /* istanbul ignore next */ {}), alias: 'mkBadgeOverlayAriaLabel' });
|
|
141
|
+
/** The text painted in the badge (`''` in dot mode). */
|
|
142
|
+
label = computed(() => {
|
|
143
|
+
if (this.dot())
|
|
144
|
+
return '';
|
|
145
|
+
const raw = this.content();
|
|
146
|
+
if (raw === null || raw === undefined || raw === '')
|
|
147
|
+
return '';
|
|
148
|
+
const max = this.max();
|
|
149
|
+
const n = typeof raw === 'number' ? raw : /^\d+$/.test(raw) ? Number(raw) : NaN;
|
|
150
|
+
if (Number.isFinite(n) && max > 0 && n > max)
|
|
151
|
+
return `${max}+`;
|
|
152
|
+
return String(raw);
|
|
153
|
+
}, /* @ts-ignore */
|
|
154
|
+
...(ngDevMode ? [{ debugName: "label" }] : /* istanbul ignore next */ []));
|
|
155
|
+
/** Whether the badge is currently rendered. */
|
|
156
|
+
visible = computed(() => !this.hidden() && (this.dot() || this.label() !== ''), /* @ts-ignore */
|
|
157
|
+
...(ngDevMode ? [{ debugName: "visible" }] : /* istanbul ignore next */ []));
|
|
158
|
+
srId = mkUniqueId('mk-badge-overlay');
|
|
159
|
+
badge = null;
|
|
160
|
+
sr = null;
|
|
161
|
+
/** Set when WE made the host positioned, so we can undo it on removal. */
|
|
162
|
+
positionedHost = false;
|
|
163
|
+
constructor() {
|
|
164
|
+
if (!this.isBrowser)
|
|
165
|
+
return;
|
|
166
|
+
effect(() => {
|
|
167
|
+
const visible = this.visible();
|
|
168
|
+
if (!visible) {
|
|
169
|
+
this.detach();
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
172
|
+
const badge = this.attach();
|
|
173
|
+
const dot = this.dot();
|
|
174
|
+
const srText = this.ariaLabel().trim();
|
|
175
|
+
this.renderer.setAttribute(badge, 'data-tone', this.tone());
|
|
176
|
+
this.renderer.setAttribute(badge, 'data-position', this.position());
|
|
177
|
+
if (dot) {
|
|
178
|
+
this.renderer.addClass(badge, 'mk-badge-overlay--dot');
|
|
179
|
+
}
|
|
180
|
+
else {
|
|
181
|
+
this.renderer.removeClass(badge, 'mk-badge-overlay--dot');
|
|
182
|
+
}
|
|
183
|
+
this.renderer.setProperty(badge, 'textContent', this.label());
|
|
184
|
+
// A dot has nothing to read; a labelled badge is described via the
|
|
185
|
+
// hidden span instead — either way the visual is hidden from AT.
|
|
186
|
+
if (dot || srText) {
|
|
187
|
+
this.renderer.setAttribute(badge, 'aria-hidden', 'true');
|
|
188
|
+
}
|
|
189
|
+
else {
|
|
190
|
+
this.renderer.removeAttribute(badge, 'aria-hidden');
|
|
191
|
+
}
|
|
192
|
+
this.syncSr(srText);
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
attach() {
|
|
196
|
+
if (this.badge)
|
|
197
|
+
return this.badge;
|
|
198
|
+
const el = this.host.nativeElement;
|
|
199
|
+
const view = this.document.defaultView;
|
|
200
|
+
const position = view?.getComputedStyle(el).position;
|
|
201
|
+
if (!position || position === 'static') {
|
|
202
|
+
this.renderer.setStyle(el, 'position', 'relative');
|
|
203
|
+
this.positionedHost = true;
|
|
204
|
+
}
|
|
205
|
+
const badge = this.renderer.createElement('span');
|
|
206
|
+
this.renderer.addClass(badge, 'mk-badge-overlay');
|
|
207
|
+
this.renderer.appendChild(el, badge);
|
|
208
|
+
this.badge = badge;
|
|
209
|
+
return badge;
|
|
210
|
+
}
|
|
211
|
+
detach() {
|
|
212
|
+
if (this.badge) {
|
|
213
|
+
this.renderer.removeChild(this.host.nativeElement, this.badge);
|
|
214
|
+
this.badge = null;
|
|
215
|
+
}
|
|
216
|
+
this.syncSr('');
|
|
217
|
+
if (this.positionedHost) {
|
|
218
|
+
this.renderer.removeStyle(this.host.nativeElement, 'position');
|
|
219
|
+
this.positionedHost = false;
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
/** Create / update / remove the hidden description and its aria-describedby link. */
|
|
223
|
+
syncSr(text) {
|
|
224
|
+
const el = this.host.nativeElement;
|
|
225
|
+
if (text) {
|
|
226
|
+
if (!this.sr) {
|
|
227
|
+
const sr = this.renderer.createElement('span');
|
|
228
|
+
this.renderer.addClass(sr, 'mk-visually-hidden');
|
|
229
|
+
this.renderer.setAttribute(sr, 'id', this.srId);
|
|
230
|
+
this.renderer.appendChild(el, sr);
|
|
231
|
+
this.sr = sr;
|
|
232
|
+
const ids = (el.getAttribute('aria-describedby') ?? '').split(/\s+/).filter(Boolean);
|
|
233
|
+
if (!ids.includes(this.srId))
|
|
234
|
+
ids.push(this.srId);
|
|
235
|
+
this.renderer.setAttribute(el, 'aria-describedby', ids.join(' '));
|
|
236
|
+
}
|
|
237
|
+
this.renderer.setProperty(this.sr, 'textContent', text);
|
|
238
|
+
}
|
|
239
|
+
else if (this.sr) {
|
|
240
|
+
this.renderer.removeChild(el, this.sr);
|
|
241
|
+
this.sr = null;
|
|
242
|
+
const ids = (el.getAttribute('aria-describedby') ?? '')
|
|
243
|
+
.split(/\s+/)
|
|
244
|
+
.filter((id) => id && id !== this.srId);
|
|
245
|
+
if (ids.length) {
|
|
246
|
+
this.renderer.setAttribute(el, 'aria-describedby', ids.join(' '));
|
|
247
|
+
}
|
|
248
|
+
else {
|
|
249
|
+
this.renderer.removeAttribute(el, 'aria-describedby');
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.7", ngImport: i0, type: MkBadgeOverlay, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
254
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.0.7", type: MkBadgeOverlay, isStandalone: true, selector: "[mkBadgeOverlay]", inputs: { content: { classPropertyName: "content", publicName: "mkBadgeOverlay", isSignal: true, isRequired: false, transformFunction: null }, position: { classPropertyName: "position", publicName: "mkBadgeOverlayPosition", isSignal: true, isRequired: false, transformFunction: null }, tone: { classPropertyName: "tone", publicName: "mkBadgeOverlayTone", isSignal: true, isRequired: false, transformFunction: null }, dot: { classPropertyName: "dot", publicName: "mkBadgeOverlayDot", isSignal: true, isRequired: false, transformFunction: null }, max: { classPropertyName: "max", publicName: "mkBadgeOverlayMax", isSignal: true, isRequired: false, transformFunction: null }, hidden: { classPropertyName: "hidden", publicName: "mkBadgeOverlayHidden", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "mkBadgeOverlayAriaLabel", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0 });
|
|
255
|
+
}
|
|
256
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImport: i0, type: MkBadgeOverlay, decorators: [{
|
|
257
|
+
type: Directive,
|
|
258
|
+
args: [{
|
|
259
|
+
selector: '[mkBadgeOverlay]',
|
|
260
|
+
}]
|
|
261
|
+
}], ctorParameters: () => [], propDecorators: { content: [{ type: i0.Input, args: [{ isSignal: true, alias: "mkBadgeOverlay", required: false }] }], position: [{ type: i0.Input, args: [{ isSignal: true, alias: "mkBadgeOverlayPosition", required: false }] }], tone: [{ type: i0.Input, args: [{ isSignal: true, alias: "mkBadgeOverlayTone", required: false }] }], dot: [{ type: i0.Input, args: [{ isSignal: true, alias: "mkBadgeOverlayDot", required: false }] }], max: [{ type: i0.Input, args: [{ isSignal: true, alias: "mkBadgeOverlayMax", required: false }] }], hidden: [{ type: i0.Input, args: [{ isSignal: true, alias: "mkBadgeOverlayHidden", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "mkBadgeOverlayAriaLabel", required: false }] }] } });
|
|
262
|
+
|
|
263
|
+
/**
|
|
264
|
+
* EmptyState — a centered placeholder for "nothing here yet" situations: empty
|
|
265
|
+
* tables, no search results, a fresh dashboard, a cleared inbox. Provide a
|
|
266
|
+
* `title` (and usually a `description`), an `icon` or projected media, and
|
|
267
|
+
* call-to-action buttons via the actions slot.
|
|
268
|
+
*
|
|
269
|
+
* Slots: `[mkEmptyStateMedia]` (custom illustration, overrides `icon`), default
|
|
270
|
+
* content (extra body), `[mkEmptyStateActions]` (buttons).
|
|
271
|
+
*
|
|
272
|
+
* ```html
|
|
273
|
+
* <mk-empty-state icon="search" title="No results"
|
|
274
|
+
* description="Try adjusting your filters.">
|
|
275
|
+
* <button mkButton mkEmptyStateActions variant="outline">Clear filters</button>
|
|
276
|
+
* </mk-empty-state>
|
|
277
|
+
* ```
|
|
278
|
+
*/
|
|
279
|
+
class MkEmptyState {
|
|
280
|
+
/** Name of a registered icon shown above the title (ignored if media is projected). */
|
|
281
|
+
icon = input('', /* @ts-ignore */
|
|
282
|
+
...(ngDevMode ? [{ debugName: "icon" }] : /* istanbul ignore next */ []));
|
|
283
|
+
/** Main heading, e.g. "No invoices yet". */
|
|
284
|
+
title = input('', /* @ts-ignore */
|
|
285
|
+
...(ngDevMode ? [{ debugName: "title" }] : /* istanbul ignore next */ []));
|
|
286
|
+
/** Supporting sentence under the title. */
|
|
287
|
+
description = input('', /* @ts-ignore */
|
|
288
|
+
...(ngDevMode ? [{ debugName: "description" }] : /* istanbul ignore next */ []));
|
|
289
|
+
/** Overall scale of the block. */
|
|
290
|
+
size = input('md', /* @ts-ignore */
|
|
291
|
+
...(ngDevMode ? [{ debugName: "size" }] : /* istanbul ignore next */ []));
|
|
292
|
+
iconSize = () => this.size() === 'sm' ? 28 : this.size() === 'lg' ? 48 : 36;
|
|
293
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.7", ngImport: i0, type: MkEmptyState, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
294
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.7", type: MkEmptyState, isStandalone: true, selector: "mk-empty-state", inputs: { icon: { classPropertyName: "icon", publicName: "icon", isSignal: true, isRequired: false, transformFunction: null }, title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: false, transformFunction: null }, description: { classPropertyName: "description", publicName: "description", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class.mk-empty-state--sm": "size() === 'sm'", "class.mk-empty-state--lg": "size() === 'lg'" }, classAttribute: "mk-empty-state" }, ngImport: i0, template: "@if (icon()) {\n <span class=\"mk-empty-state__icon\" aria-hidden=\"true\">\n <mk-icon [name]=\"icon()\" [size]=\"iconSize()\" />\n </span>\n}\n<ng-content select=\"[mkEmptyStateMedia]\" />\n\n@if (title()) {\n <p class=\"mk-empty-state__title\">{{ title() }}</p>\n}\n@if (description()) {\n <p class=\"mk-empty-state__description\">{{ description() }}</p>\n}\n\n<ng-content />\n\n<div class=\"mk-empty-state__actions\">\n <ng-content select=\"[mkEmptyStateActions]\" />\n</div>\n", styles: [":host{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:var(--mk-space-2);padding:var(--mk-space-8) var(--mk-space-5);text-align:center;font-family:var(--mk-font-sans);color:var(--mk-text)}:host(.mk-empty-state--sm){padding:var(--mk-space-5) var(--mk-space-4)}:host(.mk-empty-state--lg){padding:var(--mk-space-12) var(--mk-space-6)}.mk-empty-state__icon{display:inline-flex;align-items:center;justify-content:center;margin-bottom:var(--mk-space-1);color:var(--mk-text-subtle)}.mk-empty-state__title{margin:0;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)}:host(.mk-empty-state--sm) .mk-empty-state__title{font-size:var(--mk-font-size-md)}:host(.mk-empty-state--lg) .mk-empty-state__title{font-size:var(--mk-font-size-xl)}.mk-empty-state__description{margin:0;max-width:34ch;font-size:var(--mk-font-size-md);line-height:var(--mk-line-height-normal);color:var(--mk-text-muted)}.mk-empty-state__actions{display:flex;flex-wrap:wrap;align-items:center;justify-content:center;gap:var(--mk-space-2);margin-top:var(--mk-space-3)}.mk-empty-state__actions:empty{display:none}\n"], dependencies: [{ kind: "component", type: MkIcon, selector: "mk-icon", inputs: ["name", "size", "label"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
295
|
+
}
|
|
296
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImport: i0, type: MkEmptyState, decorators: [{
|
|
297
|
+
type: Component,
|
|
298
|
+
args: [{ selector: 'mk-empty-state', changeDetection: ChangeDetectionStrategy.OnPush, imports: [MkIcon], host: {
|
|
299
|
+
class: 'mk-empty-state',
|
|
300
|
+
'[class.mk-empty-state--sm]': "size() === 'sm'",
|
|
301
|
+
'[class.mk-empty-state--lg]': "size() === 'lg'",
|
|
302
|
+
}, template: "@if (icon()) {\n <span class=\"mk-empty-state__icon\" aria-hidden=\"true\">\n <mk-icon [name]=\"icon()\" [size]=\"iconSize()\" />\n </span>\n}\n<ng-content select=\"[mkEmptyStateMedia]\" />\n\n@if (title()) {\n <p class=\"mk-empty-state__title\">{{ title() }}</p>\n}\n@if (description()) {\n <p class=\"mk-empty-state__description\">{{ description() }}</p>\n}\n\n<ng-content />\n\n<div class=\"mk-empty-state__actions\">\n <ng-content select=\"[mkEmptyStateActions]\" />\n</div>\n", styles: [":host{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:var(--mk-space-2);padding:var(--mk-space-8) var(--mk-space-5);text-align:center;font-family:var(--mk-font-sans);color:var(--mk-text)}:host(.mk-empty-state--sm){padding:var(--mk-space-5) var(--mk-space-4)}:host(.mk-empty-state--lg){padding:var(--mk-space-12) var(--mk-space-6)}.mk-empty-state__icon{display:inline-flex;align-items:center;justify-content:center;margin-bottom:var(--mk-space-1);color:var(--mk-text-subtle)}.mk-empty-state__title{margin:0;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)}:host(.mk-empty-state--sm) .mk-empty-state__title{font-size:var(--mk-font-size-md)}:host(.mk-empty-state--lg) .mk-empty-state__title{font-size:var(--mk-font-size-xl)}.mk-empty-state__description{margin:0;max-width:34ch;font-size:var(--mk-font-size-md);line-height:var(--mk-line-height-normal);color:var(--mk-text-muted)}.mk-empty-state__actions{display:flex;flex-wrap:wrap;align-items:center;justify-content:center;gap:var(--mk-space-2);margin-top:var(--mk-space-3)}.mk-empty-state__actions:empty{display:none}\n"] }]
|
|
303
|
+
}], propDecorators: { icon: [{ type: i0.Input, args: [{ isSignal: true, alias: "icon", required: false }] }], title: [{ type: i0.Input, args: [{ isSignal: true, alias: "title", required: false }] }], description: [{ type: i0.Input, args: [{ isSignal: true, alias: "description", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }] } });
|
|
304
|
+
|
|
305
|
+
/**
|
|
306
|
+
* @mk-kit/ui/status — the small status indicators every other group leans on:
|
|
307
|
+
* spinner, badge (+ overlay) and empty state. They lived in `@mk-kit/ui/data`
|
|
308
|
+
* until 0.53; that entry still re-exports them, but `feedback` imports them
|
|
309
|
+
* from here so a dialog or toast service no longer drags the whole data group
|
|
310
|
+
* (charts, viewers, kanban → dnd) into an app's initial bundle.
|
|
311
|
+
*/
|
|
312
|
+
|
|
313
|
+
/**
|
|
314
|
+
* Generated bundle index. Do not edit.
|
|
315
|
+
*/
|
|
316
|
+
|
|
317
|
+
export { MkBadge, MkBadgeOverlay, MkEmptyState, MkSpinner };
|
|
318
|
+
//# sourceMappingURL=mk-kit-ui-status.mjs.map
|