@memberjunction/ng-kanban 0.0.1 → 5.24.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/dist/lib/components/kanban-board.component.d.ts +58 -0
- package/dist/lib/components/kanban-board.component.d.ts.map +1 -0
- package/dist/lib/components/kanban-board.component.js +279 -0
- package/dist/lib/components/kanban-board.component.js.map +1 -0
- package/dist/lib/kanban.module.d.ts +8 -0
- package/dist/lib/kanban.module.d.ts.map +1 -0
- package/dist/lib/kanban.module.js +17 -0
- package/dist/lib/kanban.module.js.map +1 -0
- package/dist/lib/models/kanban.models.d.ts +46 -0
- package/dist/lib/models/kanban.models.d.ts.map +1 -0
- package/dist/lib/models/kanban.models.js +2 -0
- package/dist/lib/models/kanban.models.js.map +1 -0
- package/dist/public-api.d.ts +7 -0
- package/dist/public-api.d.ts.map +1 -0
- package/dist/public-api.js +7 -0
- package/dist/public-api.js.map +1 -0
- package/package.json +35 -7
- package/README.md +0 -45
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { EventEmitter } from '@angular/core';
|
|
2
|
+
import { KanbanCardData, KanbanColumnDef, KanbanCardMovedEvent } from '../models/kanban.models';
|
|
3
|
+
import * as i0 from "@angular/core";
|
|
4
|
+
/**
|
|
5
|
+
* Generic Kanban board component with drag-and-drop column management.
|
|
6
|
+
*
|
|
7
|
+
* This component is entity-agnostic — it renders cards and columns from
|
|
8
|
+
* the data you provide and emits events when cards are moved or clicked.
|
|
9
|
+
* It does NOT manage persistence; the consuming application handles saves.
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* ```html
|
|
13
|
+
* <mj-kanban-board
|
|
14
|
+
* [Columns]="statusColumns"
|
|
15
|
+
* [Cards]="taskCards"
|
|
16
|
+
* (CardMoved)="onCardMoved($event)"
|
|
17
|
+
* (CardClicked)="onCardClicked($event)">
|
|
18
|
+
* </mj-kanban-board>
|
|
19
|
+
* ```
|
|
20
|
+
*/
|
|
21
|
+
export declare class MjKanbanBoardComponent {
|
|
22
|
+
/** Column definitions. Order determines display order. */
|
|
23
|
+
Columns: KanbanColumnDef[];
|
|
24
|
+
/** All cards to display. Each card's `ColumnKey` determines which column it appears in. */
|
|
25
|
+
Cards: KanbanCardData[];
|
|
26
|
+
/** Disables drag-and-drop when true. */
|
|
27
|
+
ReadOnly: boolean;
|
|
28
|
+
/** Emitted when a card is dragged to a different column. */
|
|
29
|
+
CardMoved: EventEmitter<KanbanCardMovedEvent>;
|
|
30
|
+
/** Emitted when a card is clicked (not dragged). */
|
|
31
|
+
CardClicked: EventEmitter<KanbanCardData>;
|
|
32
|
+
/** @internal */
|
|
33
|
+
dragCardID: string | null;
|
|
34
|
+
/** @internal */
|
|
35
|
+
dragCard: KanbanCardData | null;
|
|
36
|
+
/** @internal */
|
|
37
|
+
dragSourceColumn: string | null;
|
|
38
|
+
/** @internal */
|
|
39
|
+
dragOverColumn: string | null;
|
|
40
|
+
private cdr;
|
|
41
|
+
/** @internal Returns true if the given card is currently being dragged. */
|
|
42
|
+
isDragging(cardID: string): boolean;
|
|
43
|
+
/** Returns cards belonging to a specific column. */
|
|
44
|
+
getColumnCards(columnKey: string): KanbanCardData[];
|
|
45
|
+
/** @internal */
|
|
46
|
+
onDragStart(event: DragEvent, card: KanbanCardData, columnKey: string): void;
|
|
47
|
+
/** @internal */
|
|
48
|
+
onDragEnd(): void;
|
|
49
|
+
/** @internal */
|
|
50
|
+
onDragOver(event: DragEvent, columnKey: string): void;
|
|
51
|
+
/** @internal */
|
|
52
|
+
onDragLeave(columnKey: string): void;
|
|
53
|
+
/** @internal */
|
|
54
|
+
onDrop(event: DragEvent, targetColumn: string): void;
|
|
55
|
+
static ɵfac: i0.ɵɵFactoryDeclaration<MjKanbanBoardComponent, never>;
|
|
56
|
+
static ɵcmp: i0.ɵɵComponentDeclaration<MjKanbanBoardComponent, "mj-kanban-board", never, { "Columns": { "alias": "Columns"; "required": false; }; "Cards": { "alias": "Cards"; "required": false; }; "ReadOnly": { "alias": "ReadOnly"; "required": false; }; }, { "CardMoved": "CardMoved"; "CardClicked": "CardClicked"; }, never, never, true, never>;
|
|
57
|
+
}
|
|
58
|
+
//# sourceMappingURL=kanban-board.component.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"kanban-board.component.d.ts","sourceRoot":"","sources":["../../../src/lib/components/kanban-board.component.ts"],"names":[],"mappings":"AAAA,OAAO,EAA4B,YAAY,EAAsD,MAAM,eAAe,CAAC;AAG3H,OAAO,EAAE,cAAc,EAAE,eAAe,EAAE,oBAAoB,EAAE,MAAM,yBAAyB,CAAC;;AAEhG;;;;;;;;;;;;;;;;GAgBG;AACH,qBAoLa,sBAAsB;IAC/B,0DAA0D;IACjD,OAAO,EAAE,eAAe,EAAE,CAAM;IAEzC,2FAA2F;IAClF,KAAK,EAAE,cAAc,EAAE,CAAM;IAEtC,wCAAwC;IAC/B,QAAQ,UAAS;IAE1B,4DAA4D;IAClD,SAAS,qCAA4C;IAE/D,oDAAoD;IAC1C,WAAW,+BAAsC;IAE3D,gBAAgB;IAChB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAQ;IACjC,gBAAgB;IAChB,QAAQ,EAAE,cAAc,GAAG,IAAI,CAAQ;IACvC,gBAAgB;IAChB,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAQ;IACvC,gBAAgB;IAChB,cAAc,EAAE,MAAM,GAAG,IAAI,CAAQ;IAErC,OAAO,CAAC,GAAG,CAA6B;IAExC,2EAA2E;IAC3E,UAAU,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO;IAInC,oDAAoD;IACpD,cAAc,CAAC,SAAS,EAAE,MAAM,GAAG,cAAc,EAAE;IAInD,gBAAgB;IAChB,WAAW,CAAC,KAAK,EAAE,SAAS,EAAE,IAAI,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,GAAG,IAAI;IAU5E,gBAAgB;IAChB,SAAS,IAAI,IAAI;IAQjB,gBAAgB;IAChB,UAAU,CAAC,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,GAAG,IAAI;IAWrD,gBAAgB;IAChB,WAAW,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI;IAOpC,gBAAgB;IAChB,MAAM,CAAC,KAAK,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,GAAG,IAAI;yCA9E3C,sBAAsB;2CAAtB,sBAAsB;CA+FlC"}
|
|
@@ -0,0 +1,279 @@
|
|
|
1
|
+
import { Component, Input, Output, EventEmitter, ChangeDetectionStrategy, ChangeDetectorRef, inject } from '@angular/core';
|
|
2
|
+
import { CommonModule } from '@angular/common';
|
|
3
|
+
import { UUIDsEqual } from '@memberjunction/global';
|
|
4
|
+
import * as i0 from "@angular/core";
|
|
5
|
+
const _forTrack0 = ($index, $item) => $item.Key;
|
|
6
|
+
const _forTrack1 = ($index, $item) => $item.ID;
|
|
7
|
+
function MjKanbanBoardComponent_For_2_For_8_Conditional_3_Template(rf, ctx) { if (rf & 1) {
|
|
8
|
+
i0.ɵɵdomElementStart(0, "div", 11);
|
|
9
|
+
i0.ɵɵtext(1);
|
|
10
|
+
i0.ɵɵdomElementEnd();
|
|
11
|
+
} if (rf & 2) {
|
|
12
|
+
const card_r5 = i0.ɵɵnextContext().$implicit;
|
|
13
|
+
i0.ɵɵadvance();
|
|
14
|
+
i0.ɵɵtextInterpolate(card_r5.Subtitle);
|
|
15
|
+
} }
|
|
16
|
+
function MjKanbanBoardComponent_For_2_For_8_Conditional_5_Template(rf, ctx) { if (rf & 1) {
|
|
17
|
+
i0.ɵɵdomElementStart(0, "span", 15);
|
|
18
|
+
i0.ɵɵtext(1);
|
|
19
|
+
i0.ɵɵdomElementEnd();
|
|
20
|
+
} if (rf & 2) {
|
|
21
|
+
const card_r5 = i0.ɵɵnextContext().$implicit;
|
|
22
|
+
i0.ɵɵstyleProp("background", card_r5.BadgeColor || "var(--mj-bg-surface-sunken)")("color", card_r5.BadgeColor ? "var(--mj-text-inverse)" : "var(--mj-text-secondary)");
|
|
23
|
+
i0.ɵɵadvance();
|
|
24
|
+
i0.ɵɵtextInterpolate1(" ", card_r5.BadgeText, " ");
|
|
25
|
+
} }
|
|
26
|
+
function MjKanbanBoardComponent_For_2_For_8_Conditional_6_Template(rf, ctx) { if (rf & 1) {
|
|
27
|
+
i0.ɵɵdomElementStart(0, "span", 14);
|
|
28
|
+
i0.ɵɵtext(1);
|
|
29
|
+
i0.ɵɵdomElementEnd();
|
|
30
|
+
} if (rf & 2) {
|
|
31
|
+
const card_r5 = i0.ɵɵnextContext().$implicit;
|
|
32
|
+
i0.ɵɵadvance();
|
|
33
|
+
i0.ɵɵtextInterpolate(card_r5.FooterText);
|
|
34
|
+
} }
|
|
35
|
+
function MjKanbanBoardComponent_For_2_For_8_Template(rf, ctx) { if (rf & 1) {
|
|
36
|
+
const _r4 = i0.ɵɵgetCurrentView();
|
|
37
|
+
i0.ɵɵdomElementStart(0, "div", 9);
|
|
38
|
+
i0.ɵɵdomListener("dragstart", function MjKanbanBoardComponent_For_2_For_8_Template_div_dragstart_0_listener($event) { const card_r5 = i0.ɵɵrestoreView(_r4).$implicit; const col_r2 = i0.ɵɵnextContext().$implicit; const ctx_r2 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r2.onDragStart($event, card_r5, col_r2.Key)); })("dragend", function MjKanbanBoardComponent_For_2_For_8_Template_div_dragend_0_listener() { i0.ɵɵrestoreView(_r4); const ctx_r2 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r2.onDragEnd()); })("click", function MjKanbanBoardComponent_For_2_For_8_Template_div_click_0_listener() { const card_r5 = i0.ɵɵrestoreView(_r4).$implicit; const ctx_r2 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r2.CardClicked.emit(card_r5)); });
|
|
39
|
+
i0.ɵɵdomElementStart(1, "div", 10);
|
|
40
|
+
i0.ɵɵtext(2);
|
|
41
|
+
i0.ɵɵdomElementEnd();
|
|
42
|
+
i0.ɵɵconditionalCreate(3, MjKanbanBoardComponent_For_2_For_8_Conditional_3_Template, 2, 1, "div", 11);
|
|
43
|
+
i0.ɵɵdomElementStart(4, "div", 12);
|
|
44
|
+
i0.ɵɵconditionalCreate(5, MjKanbanBoardComponent_For_2_For_8_Conditional_5_Template, 2, 5, "span", 13);
|
|
45
|
+
i0.ɵɵconditionalCreate(6, MjKanbanBoardComponent_For_2_For_8_Conditional_6_Template, 2, 1, "span", 14);
|
|
46
|
+
i0.ɵɵdomElementEnd()();
|
|
47
|
+
} if (rf & 2) {
|
|
48
|
+
const card_r5 = ctx.$implicit;
|
|
49
|
+
const ctx_r2 = i0.ɵɵnextContext(2);
|
|
50
|
+
i0.ɵɵstyleProp("border-left-color", card_r5.Color || "transparent");
|
|
51
|
+
i0.ɵɵclassProp("dragging", ctx_r2.isDragging(card_r5.ID));
|
|
52
|
+
i0.ɵɵdomProperty("draggable", !ctx_r2.ReadOnly);
|
|
53
|
+
i0.ɵɵadvance(2);
|
|
54
|
+
i0.ɵɵtextInterpolate(card_r5.Title);
|
|
55
|
+
i0.ɵɵadvance();
|
|
56
|
+
i0.ɵɵconditional(card_r5.Subtitle ? 3 : -1);
|
|
57
|
+
i0.ɵɵadvance(2);
|
|
58
|
+
i0.ɵɵconditional(card_r5.BadgeText ? 5 : -1);
|
|
59
|
+
i0.ɵɵadvance();
|
|
60
|
+
i0.ɵɵconditional(card_r5.FooterText ? 6 : -1);
|
|
61
|
+
} }
|
|
62
|
+
function MjKanbanBoardComponent_For_2_Conditional_9_Conditional_1_Template(rf, ctx) { if (rf & 1) {
|
|
63
|
+
i0.ɵɵtext(0, " Drop here ");
|
|
64
|
+
} }
|
|
65
|
+
function MjKanbanBoardComponent_For_2_Conditional_9_Conditional_2_Template(rf, ctx) { if (rf & 1) {
|
|
66
|
+
i0.ɵɵtext(0, " No items ");
|
|
67
|
+
} }
|
|
68
|
+
function MjKanbanBoardComponent_For_2_Conditional_9_Template(rf, ctx) { if (rf & 1) {
|
|
69
|
+
i0.ɵɵdomElementStart(0, "div", 8);
|
|
70
|
+
i0.ɵɵconditionalCreate(1, MjKanbanBoardComponent_For_2_Conditional_9_Conditional_1_Template, 1, 0)(2, MjKanbanBoardComponent_For_2_Conditional_9_Conditional_2_Template, 1, 0);
|
|
71
|
+
i0.ɵɵdomElementEnd();
|
|
72
|
+
} if (rf & 2) {
|
|
73
|
+
const ctx_r2 = i0.ɵɵnextContext(2);
|
|
74
|
+
i0.ɵɵadvance();
|
|
75
|
+
i0.ɵɵconditional(!ctx_r2.ReadOnly ? 1 : 2);
|
|
76
|
+
} }
|
|
77
|
+
function MjKanbanBoardComponent_For_2_Template(rf, ctx) { if (rf & 1) {
|
|
78
|
+
const _r1 = i0.ɵɵgetCurrentView();
|
|
79
|
+
i0.ɵɵdomElementStart(0, "div", 2);
|
|
80
|
+
i0.ɵɵdomListener("dragover", function MjKanbanBoardComponent_For_2_Template_div_dragover_0_listener($event) { const col_r2 = i0.ɵɵrestoreView(_r1).$implicit; const ctx_r2 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r2.onDragOver($event, col_r2.Key)); })("dragleave", function MjKanbanBoardComponent_For_2_Template_div_dragleave_0_listener() { const col_r2 = i0.ɵɵrestoreView(_r1).$implicit; const ctx_r2 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r2.onDragLeave(col_r2.Key)); })("drop", function MjKanbanBoardComponent_For_2_Template_div_drop_0_listener($event) { const col_r2 = i0.ɵɵrestoreView(_r1).$implicit; const ctx_r2 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r2.onDrop($event, col_r2.Key)); });
|
|
81
|
+
i0.ɵɵdomElementStart(1, "div", 3)(2, "span", 4);
|
|
82
|
+
i0.ɵɵtext(3);
|
|
83
|
+
i0.ɵɵdomElementEnd();
|
|
84
|
+
i0.ɵɵdomElementStart(4, "span", 5);
|
|
85
|
+
i0.ɵɵtext(5);
|
|
86
|
+
i0.ɵɵdomElementEnd()();
|
|
87
|
+
i0.ɵɵdomElementStart(6, "div", 6);
|
|
88
|
+
i0.ɵɵrepeaterCreate(7, MjKanbanBoardComponent_For_2_For_8_Template, 7, 9, "div", 7, _forTrack1);
|
|
89
|
+
i0.ɵɵconditionalCreate(9, MjKanbanBoardComponent_For_2_Conditional_9_Template, 3, 1, "div", 8);
|
|
90
|
+
i0.ɵɵdomElementEnd()();
|
|
91
|
+
} if (rf & 2) {
|
|
92
|
+
const col_r2 = ctx.$implicit;
|
|
93
|
+
const ctx_r2 = i0.ɵɵnextContext();
|
|
94
|
+
i0.ɵɵclassProp("drag-over", ctx_r2.dragOverColumn === col_r2.Key);
|
|
95
|
+
i0.ɵɵadvance();
|
|
96
|
+
i0.ɵɵstyleProp("border-top-color", col_r2.Color || "var(--mj-border-default)");
|
|
97
|
+
i0.ɵɵadvance(2);
|
|
98
|
+
i0.ɵɵtextInterpolate(col_r2.Label);
|
|
99
|
+
i0.ɵɵadvance(2);
|
|
100
|
+
i0.ɵɵtextInterpolate(ctx_r2.getColumnCards(col_r2.Key).length);
|
|
101
|
+
i0.ɵɵadvance(2);
|
|
102
|
+
i0.ɵɵrepeater(ctx_r2.getColumnCards(col_r2.Key));
|
|
103
|
+
i0.ɵɵadvance(2);
|
|
104
|
+
i0.ɵɵconditional(ctx_r2.getColumnCards(col_r2.Key).length === 0 ? 9 : -1);
|
|
105
|
+
} }
|
|
106
|
+
/**
|
|
107
|
+
* Generic Kanban board component with drag-and-drop column management.
|
|
108
|
+
*
|
|
109
|
+
* This component is entity-agnostic — it renders cards and columns from
|
|
110
|
+
* the data you provide and emits events when cards are moved or clicked.
|
|
111
|
+
* It does NOT manage persistence; the consuming application handles saves.
|
|
112
|
+
*
|
|
113
|
+
* @example
|
|
114
|
+
* ```html
|
|
115
|
+
* <mj-kanban-board
|
|
116
|
+
* [Columns]="statusColumns"
|
|
117
|
+
* [Cards]="taskCards"
|
|
118
|
+
* (CardMoved)="onCardMoved($event)"
|
|
119
|
+
* (CardClicked)="onCardClicked($event)">
|
|
120
|
+
* </mj-kanban-board>
|
|
121
|
+
* ```
|
|
122
|
+
*/
|
|
123
|
+
export class MjKanbanBoardComponent {
|
|
124
|
+
/** Column definitions. Order determines display order. */
|
|
125
|
+
Columns = [];
|
|
126
|
+
/** All cards to display. Each card's `ColumnKey` determines which column it appears in. */
|
|
127
|
+
Cards = [];
|
|
128
|
+
/** Disables drag-and-drop when true. */
|
|
129
|
+
ReadOnly = false;
|
|
130
|
+
/** Emitted when a card is dragged to a different column. */
|
|
131
|
+
CardMoved = new EventEmitter();
|
|
132
|
+
/** Emitted when a card is clicked (not dragged). */
|
|
133
|
+
CardClicked = new EventEmitter();
|
|
134
|
+
/** @internal */
|
|
135
|
+
dragCardID = null;
|
|
136
|
+
/** @internal */
|
|
137
|
+
dragCard = null;
|
|
138
|
+
/** @internal */
|
|
139
|
+
dragSourceColumn = null;
|
|
140
|
+
/** @internal */
|
|
141
|
+
dragOverColumn = null;
|
|
142
|
+
cdr = inject(ChangeDetectorRef);
|
|
143
|
+
/** @internal Returns true if the given card is currently being dragged. */
|
|
144
|
+
isDragging(cardID) {
|
|
145
|
+
return this.dragCardID != null && UUIDsEqual(this.dragCardID, cardID);
|
|
146
|
+
}
|
|
147
|
+
/** Returns cards belonging to a specific column. */
|
|
148
|
+
getColumnCards(columnKey) {
|
|
149
|
+
return this.Cards.filter(c => c.ColumnKey === columnKey);
|
|
150
|
+
}
|
|
151
|
+
/** @internal */
|
|
152
|
+
onDragStart(event, card, columnKey) {
|
|
153
|
+
this.dragCardID = card.ID;
|
|
154
|
+
this.dragCard = card;
|
|
155
|
+
this.dragSourceColumn = columnKey;
|
|
156
|
+
event.dataTransfer?.setData('text/plain', card.ID);
|
|
157
|
+
if (event.dataTransfer) {
|
|
158
|
+
event.dataTransfer.effectAllowed = 'move';
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
/** @internal */
|
|
162
|
+
onDragEnd() {
|
|
163
|
+
this.dragCardID = null;
|
|
164
|
+
this.dragCard = null;
|
|
165
|
+
this.dragSourceColumn = null;
|
|
166
|
+
this.dragOverColumn = null;
|
|
167
|
+
this.cdr.markForCheck();
|
|
168
|
+
}
|
|
169
|
+
/** @internal */
|
|
170
|
+
onDragOver(event, columnKey) {
|
|
171
|
+
event.preventDefault();
|
|
172
|
+
if (event.dataTransfer) {
|
|
173
|
+
event.dataTransfer.dropEffect = 'move';
|
|
174
|
+
}
|
|
175
|
+
if (this.dragOverColumn !== columnKey) {
|
|
176
|
+
this.dragOverColumn = columnKey;
|
|
177
|
+
this.cdr.markForCheck();
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
/** @internal */
|
|
181
|
+
onDragLeave(columnKey) {
|
|
182
|
+
if (this.dragOverColumn === columnKey) {
|
|
183
|
+
this.dragOverColumn = null;
|
|
184
|
+
this.cdr.markForCheck();
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
/** @internal */
|
|
188
|
+
onDrop(event, targetColumn) {
|
|
189
|
+
event.preventDefault();
|
|
190
|
+
this.dragOverColumn = null;
|
|
191
|
+
if (!this.dragCard || this.dragSourceColumn === targetColumn) {
|
|
192
|
+
this.onDragEnd();
|
|
193
|
+
return;
|
|
194
|
+
}
|
|
195
|
+
this.CardMoved.emit({
|
|
196
|
+
Card: this.dragCard,
|
|
197
|
+
FromColumn: this.dragSourceColumn,
|
|
198
|
+
ToColumn: targetColumn,
|
|
199
|
+
});
|
|
200
|
+
this.onDragEnd();
|
|
201
|
+
}
|
|
202
|
+
static ɵfac = function MjKanbanBoardComponent_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || MjKanbanBoardComponent)(); };
|
|
203
|
+
static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: MjKanbanBoardComponent, selectors: [["mj-kanban-board"]], inputs: { Columns: "Columns", Cards: "Cards", ReadOnly: "ReadOnly" }, outputs: { CardMoved: "CardMoved", CardClicked: "CardClicked" }, decls: 3, vars: 0, consts: [[1, "mj-kanban-board"], [1, "mj-kanban-column", 3, "drag-over"], [1, "mj-kanban-column", 3, "dragover", "dragleave", "drop"], [1, "column-header"], [1, "column-title"], [1, "column-count"], [1, "column-body"], [1, "mj-kanban-card", 3, "border-left-color", "dragging", "draggable"], [1, "empty-column"], [1, "mj-kanban-card", 3, "dragstart", "dragend", "click", "draggable"], [1, "card-title"], [1, "card-subtitle"], [1, "card-footer"], [1, "card-badge", 3, "background", "color"], [1, "card-footer-text"], [1, "card-badge"]], template: function MjKanbanBoardComponent_Template(rf, ctx) { if (rf & 1) {
|
|
204
|
+
i0.ɵɵdomElementStart(0, "div", 0);
|
|
205
|
+
i0.ɵɵrepeaterCreate(1, MjKanbanBoardComponent_For_2_Template, 10, 7, "div", 1, _forTrack0);
|
|
206
|
+
i0.ɵɵdomElementEnd();
|
|
207
|
+
} if (rf & 2) {
|
|
208
|
+
i0.ɵɵadvance();
|
|
209
|
+
i0.ɵɵrepeater(ctx.Columns);
|
|
210
|
+
} }, dependencies: [CommonModule], styles: ["[_nghost-%COMP%] { display: block; font-family: var(--mj-font-family); }\n\n .mj-kanban-board[_ngcontent-%COMP%] {\n display: flex;\n gap: var(--mj-space-3);\n overflow-x: auto;\n padding: var(--mj-space-1) 0 var(--mj-space-2) 0;\n min-height: 300px;\n }\n\n .mj-kanban-column[_ngcontent-%COMP%] {\n flex: 0 0 260px;\n background: var(--mj-bg-surface-sunken);\n border-radius: var(--mj-radius-lg);\n display: flex;\n flex-direction: column;\n transition: background var(--mj-transition-fast);\n }\n\n .mj-kanban-column.drag-over[_ngcontent-%COMP%] {\n background: var(--mj-brand-primary-light);\n }\n\n .column-header[_ngcontent-%COMP%] {\n padding: var(--mj-space-2-5) var(--mj-space-3-5);\n font-weight: var(--mj-font-semibold);\n font-size: var(--mj-text-sm);\n display: flex;\n justify-content: space-between;\n align-items: center;\n border-top: 3px solid var(--mj-border-default);\n border-radius: var(--mj-radius-lg) var(--mj-radius-lg) 0 0;\n color: var(--mj-text-primary);\n }\n\n .column-count[_ngcontent-%COMP%] {\n background: var(--mj-bg-surface-hover);\n border-radius: var(--mj-radius-lg);\n padding: var(--mj-space-px) var(--mj-space-2);\n font-size: var(--mj-text-xs);\n font-weight: var(--mj-font-bold);\n color: var(--mj-text-secondary);\n }\n\n .column-body[_ngcontent-%COMP%] {\n flex: 1;\n padding: var(--mj-space-2);\n display: flex;\n flex-direction: column;\n gap: var(--mj-space-1-5);\n min-height: 80px;\n }\n\n .mj-kanban-card[_ngcontent-%COMP%] {\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-subtle);\n border-left: 3px solid transparent;\n border-radius: var(--mj-radius-md);\n padding: var(--mj-space-2-5) var(--mj-space-3);\n cursor: grab;\n transition: box-shadow var(--mj-transition-fast), opacity var(--mj-transition-fast);\n }\n\n .mj-kanban-card[_ngcontent-%COMP%]:hover {\n box-shadow: var(--mj-shadow-sm);\n }\n\n .mj-kanban-card[_ngcontent-%COMP%]:active { cursor: grabbing; }\n\n .mj-kanban-card.dragging[_ngcontent-%COMP%] {\n opacity: 0.4;\n }\n\n .card-title[_ngcontent-%COMP%] {\n font-size: var(--mj-text-sm);\n font-weight: var(--mj-font-medium);\n color: var(--mj-text-primary);\n line-height: var(--mj-leading-snug);\n }\n\n .card-subtitle[_ngcontent-%COMP%] {\n font-size: var(--mj-text-xs);\n color: var(--mj-text-muted);\n margin-top: var(--mj-space-1);\n line-height: var(--mj-leading-snug);\n display: -webkit-box;\n -webkit-line-clamp: 2;\n -webkit-box-orient: vertical;\n overflow: hidden;\n }\n\n .card-footer[_ngcontent-%COMP%] {\n display: flex;\n justify-content: space-between;\n align-items: center;\n margin-top: var(--mj-space-2);\n gap: var(--mj-space-2);\n }\n\n .card-badge[_ngcontent-%COMP%] {\n font-size: var(--mj-text-xs);\n font-weight: var(--mj-font-semibold);\n padding: var(--mj-space-0-5) var(--mj-space-2);\n border-radius: var(--mj-radius-sm);\n }\n\n .card-footer-text[_ngcontent-%COMP%] {\n font-size: var(--mj-text-xs);\n color: var(--mj-text-muted);\n }\n\n .empty-column[_ngcontent-%COMP%] {\n text-align: center;\n color: var(--mj-text-disabled);\n font-size: var(--mj-text-sm);\n padding: var(--mj-space-6) var(--mj-space-2);\n border: 2px dashed var(--mj-border-subtle);\n border-radius: var(--mj-radius-md);\n }"], changeDetection: 0 });
|
|
211
|
+
}
|
|
212
|
+
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MjKanbanBoardComponent, [{
|
|
213
|
+
type: Component,
|
|
214
|
+
args: [{ selector: 'mj-kanban-board', standalone: true, imports: [CommonModule], changeDetection: ChangeDetectionStrategy.OnPush, template: `
|
|
215
|
+
<div class="mj-kanban-board">
|
|
216
|
+
@for (col of Columns; track col.Key) {
|
|
217
|
+
<div class="mj-kanban-column"
|
|
218
|
+
[class.drag-over]="dragOverColumn === col.Key"
|
|
219
|
+
(dragover)="onDragOver($event, col.Key)"
|
|
220
|
+
(dragleave)="onDragLeave(col.Key)"
|
|
221
|
+
(drop)="onDrop($event, col.Key)">
|
|
222
|
+
<div class="column-header" [style.border-top-color]="col.Color || 'var(--mj-border-default)'">
|
|
223
|
+
<span class="column-title">{{ col.Label }}</span>
|
|
224
|
+
<span class="column-count">{{ getColumnCards(col.Key).length }}</span>
|
|
225
|
+
</div>
|
|
226
|
+
<div class="column-body">
|
|
227
|
+
@for (card of getColumnCards(col.Key); track card.ID) {
|
|
228
|
+
<div class="mj-kanban-card"
|
|
229
|
+
[style.border-left-color]="card.Color || 'transparent'"
|
|
230
|
+
[class.dragging]="isDragging(card.ID)"
|
|
231
|
+
[draggable]="!ReadOnly"
|
|
232
|
+
(dragstart)="onDragStart($event, card, col.Key)"
|
|
233
|
+
(dragend)="onDragEnd()"
|
|
234
|
+
(click)="CardClicked.emit(card)">
|
|
235
|
+
<div class="card-title">{{ card.Title }}</div>
|
|
236
|
+
@if (card.Subtitle) {
|
|
237
|
+
<div class="card-subtitle">{{ card.Subtitle }}</div>
|
|
238
|
+
}
|
|
239
|
+
<div class="card-footer">
|
|
240
|
+
@if (card.BadgeText) {
|
|
241
|
+
<span class="card-badge"
|
|
242
|
+
[style.background]="card.BadgeColor || 'var(--mj-bg-surface-sunken)'"
|
|
243
|
+
[style.color]="card.BadgeColor ? 'var(--mj-text-inverse)' : 'var(--mj-text-secondary)'">
|
|
244
|
+
{{ card.BadgeText }}
|
|
245
|
+
</span>
|
|
246
|
+
}
|
|
247
|
+
@if (card.FooterText) {
|
|
248
|
+
<span class="card-footer-text">{{ card.FooterText }}</span>
|
|
249
|
+
}
|
|
250
|
+
</div>
|
|
251
|
+
</div>
|
|
252
|
+
}
|
|
253
|
+
@if (getColumnCards(col.Key).length === 0) {
|
|
254
|
+
<div class="empty-column">
|
|
255
|
+
@if (!ReadOnly) {
|
|
256
|
+
Drop here
|
|
257
|
+
} @else {
|
|
258
|
+
No items
|
|
259
|
+
}
|
|
260
|
+
</div>
|
|
261
|
+
}
|
|
262
|
+
</div>
|
|
263
|
+
</div>
|
|
264
|
+
}
|
|
265
|
+
</div>
|
|
266
|
+
`, styles: ["\n :host { display: block; font-family: var(--mj-font-family); }\n\n .mj-kanban-board {\n display: flex;\n gap: var(--mj-space-3);\n overflow-x: auto;\n padding: var(--mj-space-1) 0 var(--mj-space-2) 0;\n min-height: 300px;\n }\n\n .mj-kanban-column {\n flex: 0 0 260px;\n background: var(--mj-bg-surface-sunken);\n border-radius: var(--mj-radius-lg);\n display: flex;\n flex-direction: column;\n transition: background var(--mj-transition-fast);\n }\n\n .mj-kanban-column.drag-over {\n background: var(--mj-brand-primary-light);\n }\n\n .column-header {\n padding: var(--mj-space-2-5) var(--mj-space-3-5);\n font-weight: var(--mj-font-semibold);\n font-size: var(--mj-text-sm);\n display: flex;\n justify-content: space-between;\n align-items: center;\n border-top: 3px solid var(--mj-border-default);\n border-radius: var(--mj-radius-lg) var(--mj-radius-lg) 0 0;\n color: var(--mj-text-primary);\n }\n\n .column-count {\n background: var(--mj-bg-surface-hover);\n border-radius: var(--mj-radius-lg);\n padding: var(--mj-space-px) var(--mj-space-2);\n font-size: var(--mj-text-xs);\n font-weight: var(--mj-font-bold);\n color: var(--mj-text-secondary);\n }\n\n .column-body {\n flex: 1;\n padding: var(--mj-space-2);\n display: flex;\n flex-direction: column;\n gap: var(--mj-space-1-5);\n min-height: 80px;\n }\n\n .mj-kanban-card {\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-subtle);\n border-left: 3px solid transparent;\n border-radius: var(--mj-radius-md);\n padding: var(--mj-space-2-5) var(--mj-space-3);\n cursor: grab;\n transition: box-shadow var(--mj-transition-fast), opacity var(--mj-transition-fast);\n }\n\n .mj-kanban-card:hover {\n box-shadow: var(--mj-shadow-sm);\n }\n\n .mj-kanban-card:active { cursor: grabbing; }\n\n .mj-kanban-card.dragging {\n opacity: 0.4;\n }\n\n .card-title {\n font-size: var(--mj-text-sm);\n font-weight: var(--mj-font-medium);\n color: var(--mj-text-primary);\n line-height: var(--mj-leading-snug);\n }\n\n .card-subtitle {\n font-size: var(--mj-text-xs);\n color: var(--mj-text-muted);\n margin-top: var(--mj-space-1);\n line-height: var(--mj-leading-snug);\n display: -webkit-box;\n -webkit-line-clamp: 2;\n -webkit-box-orient: vertical;\n overflow: hidden;\n }\n\n .card-footer {\n display: flex;\n justify-content: space-between;\n align-items: center;\n margin-top: var(--mj-space-2);\n gap: var(--mj-space-2);\n }\n\n .card-badge {\n font-size: var(--mj-text-xs);\n font-weight: var(--mj-font-semibold);\n padding: var(--mj-space-0-5) var(--mj-space-2);\n border-radius: var(--mj-radius-sm);\n }\n\n .card-footer-text {\n font-size: var(--mj-text-xs);\n color: var(--mj-text-muted);\n }\n\n .empty-column {\n text-align: center;\n color: var(--mj-text-disabled);\n font-size: var(--mj-text-sm);\n padding: var(--mj-space-6) var(--mj-space-2);\n border: 2px dashed var(--mj-border-subtle);\n border-radius: var(--mj-radius-md);\n }\n "] }]
|
|
267
|
+
}], null, { Columns: [{
|
|
268
|
+
type: Input
|
|
269
|
+
}], Cards: [{
|
|
270
|
+
type: Input
|
|
271
|
+
}], ReadOnly: [{
|
|
272
|
+
type: Input
|
|
273
|
+
}], CardMoved: [{
|
|
274
|
+
type: Output
|
|
275
|
+
}], CardClicked: [{
|
|
276
|
+
type: Output
|
|
277
|
+
}] }); })();
|
|
278
|
+
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(MjKanbanBoardComponent, { className: "MjKanbanBoardComponent", filePath: "src/lib/components/kanban-board.component.ts", lineNumber: 203 }); })();
|
|
279
|
+
//# sourceMappingURL=kanban-board.component.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"kanban-board.component.js","sourceRoot":"","sources":["../../../src/lib/components/kanban-board.component.ts"],"names":[],"mappings":"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","sourcesContent":["import { Component, Input, Output, EventEmitter, ChangeDetectionStrategy, ChangeDetectorRef, inject } from '@angular/core';\nimport { CommonModule } from '@angular/common';\nimport { UUIDsEqual } from '@memberjunction/global';\nimport { KanbanCardData, KanbanColumnDef, KanbanCardMovedEvent } from '../models/kanban.models';\n\n/**\n * Generic Kanban board component with drag-and-drop column management.\n *\n * This component is entity-agnostic — it renders cards and columns from\n * the data you provide and emits events when cards are moved or clicked.\n * It does NOT manage persistence; the consuming application handles saves.\n *\n * @example\n * ```html\n * <mj-kanban-board\n * [Columns]=\"statusColumns\"\n * [Cards]=\"taskCards\"\n * (CardMoved)=\"onCardMoved($event)\"\n * (CardClicked)=\"onCardClicked($event)\">\n * </mj-kanban-board>\n * ```\n */\n@Component({\n selector: 'mj-kanban-board',\n standalone: true,\n imports: [CommonModule],\n changeDetection: ChangeDetectionStrategy.OnPush,\n template: `\n <div class=\"mj-kanban-board\">\n @for (col of Columns; track col.Key) {\n <div class=\"mj-kanban-column\"\n [class.drag-over]=\"dragOverColumn === col.Key\"\n (dragover)=\"onDragOver($event, col.Key)\"\n (dragleave)=\"onDragLeave(col.Key)\"\n (drop)=\"onDrop($event, col.Key)\">\n <div class=\"column-header\" [style.border-top-color]=\"col.Color || 'var(--mj-border-default)'\">\n <span class=\"column-title\">{{ col.Label }}</span>\n <span class=\"column-count\">{{ getColumnCards(col.Key).length }}</span>\n </div>\n <div class=\"column-body\">\n @for (card of getColumnCards(col.Key); track card.ID) {\n <div class=\"mj-kanban-card\"\n [style.border-left-color]=\"card.Color || 'transparent'\"\n [class.dragging]=\"isDragging(card.ID)\"\n [draggable]=\"!ReadOnly\"\n (dragstart)=\"onDragStart($event, card, col.Key)\"\n (dragend)=\"onDragEnd()\"\n (click)=\"CardClicked.emit(card)\">\n <div class=\"card-title\">{{ card.Title }}</div>\n @if (card.Subtitle) {\n <div class=\"card-subtitle\">{{ card.Subtitle }}</div>\n }\n <div class=\"card-footer\">\n @if (card.BadgeText) {\n <span class=\"card-badge\"\n [style.background]=\"card.BadgeColor || 'var(--mj-bg-surface-sunken)'\"\n [style.color]=\"card.BadgeColor ? 'var(--mj-text-inverse)' : 'var(--mj-text-secondary)'\">\n {{ card.BadgeText }}\n </span>\n }\n @if (card.FooterText) {\n <span class=\"card-footer-text\">{{ card.FooterText }}</span>\n }\n </div>\n </div>\n }\n @if (getColumnCards(col.Key).length === 0) {\n <div class=\"empty-column\">\n @if (!ReadOnly) {\n Drop here\n } @else {\n No items\n }\n </div>\n }\n </div>\n </div>\n }\n </div>\n `,\n styles: [`\n :host { display: block; font-family: var(--mj-font-family); }\n\n .mj-kanban-board {\n display: flex;\n gap: var(--mj-space-3);\n overflow-x: auto;\n padding: var(--mj-space-1) 0 var(--mj-space-2) 0;\n min-height: 300px;\n }\n\n .mj-kanban-column {\n flex: 0 0 260px;\n background: var(--mj-bg-surface-sunken);\n border-radius: var(--mj-radius-lg);\n display: flex;\n flex-direction: column;\n transition: background var(--mj-transition-fast);\n }\n\n .mj-kanban-column.drag-over {\n background: var(--mj-brand-primary-light);\n }\n\n .column-header {\n padding: var(--mj-space-2-5) var(--mj-space-3-5);\n font-weight: var(--mj-font-semibold);\n font-size: var(--mj-text-sm);\n display: flex;\n justify-content: space-between;\n align-items: center;\n border-top: 3px solid var(--mj-border-default);\n border-radius: var(--mj-radius-lg) var(--mj-radius-lg) 0 0;\n color: var(--mj-text-primary);\n }\n\n .column-count {\n background: var(--mj-bg-surface-hover);\n border-radius: var(--mj-radius-lg);\n padding: var(--mj-space-px) var(--mj-space-2);\n font-size: var(--mj-text-xs);\n font-weight: var(--mj-font-bold);\n color: var(--mj-text-secondary);\n }\n\n .column-body {\n flex: 1;\n padding: var(--mj-space-2);\n display: flex;\n flex-direction: column;\n gap: var(--mj-space-1-5);\n min-height: 80px;\n }\n\n .mj-kanban-card {\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-subtle);\n border-left: 3px solid transparent;\n border-radius: var(--mj-radius-md);\n padding: var(--mj-space-2-5) var(--mj-space-3);\n cursor: grab;\n transition: box-shadow var(--mj-transition-fast), opacity var(--mj-transition-fast);\n }\n\n .mj-kanban-card:hover {\n box-shadow: var(--mj-shadow-sm);\n }\n\n .mj-kanban-card:active { cursor: grabbing; }\n\n .mj-kanban-card.dragging {\n opacity: 0.4;\n }\n\n .card-title {\n font-size: var(--mj-text-sm);\n font-weight: var(--mj-font-medium);\n color: var(--mj-text-primary);\n line-height: var(--mj-leading-snug);\n }\n\n .card-subtitle {\n font-size: var(--mj-text-xs);\n color: var(--mj-text-muted);\n margin-top: var(--mj-space-1);\n line-height: var(--mj-leading-snug);\n display: -webkit-box;\n -webkit-line-clamp: 2;\n -webkit-box-orient: vertical;\n overflow: hidden;\n }\n\n .card-footer {\n display: flex;\n justify-content: space-between;\n align-items: center;\n margin-top: var(--mj-space-2);\n gap: var(--mj-space-2);\n }\n\n .card-badge {\n font-size: var(--mj-text-xs);\n font-weight: var(--mj-font-semibold);\n padding: var(--mj-space-0-5) var(--mj-space-2);\n border-radius: var(--mj-radius-sm);\n }\n\n .card-footer-text {\n font-size: var(--mj-text-xs);\n color: var(--mj-text-muted);\n }\n\n .empty-column {\n text-align: center;\n color: var(--mj-text-disabled);\n font-size: var(--mj-text-sm);\n padding: var(--mj-space-6) var(--mj-space-2);\n border: 2px dashed var(--mj-border-subtle);\n border-radius: var(--mj-radius-md);\n }\n `]\n})\nexport class MjKanbanBoardComponent {\n /** Column definitions. Order determines display order. */\n @Input() Columns: KanbanColumnDef[] = [];\n\n /** All cards to display. Each card's `ColumnKey` determines which column it appears in. */\n @Input() Cards: KanbanCardData[] = [];\n\n /** Disables drag-and-drop when true. */\n @Input() ReadOnly = false;\n\n /** Emitted when a card is dragged to a different column. */\n @Output() CardMoved = new EventEmitter<KanbanCardMovedEvent>();\n\n /** Emitted when a card is clicked (not dragged). */\n @Output() CardClicked = new EventEmitter<KanbanCardData>();\n\n /** @internal */\n dragCardID: string | null = null;\n /** @internal */\n dragCard: KanbanCardData | null = null;\n /** @internal */\n dragSourceColumn: string | null = null;\n /** @internal */\n dragOverColumn: string | null = null;\n\n private cdr = inject(ChangeDetectorRef);\n\n /** @internal Returns true if the given card is currently being dragged. */\n isDragging(cardID: string): boolean {\n return this.dragCardID != null && UUIDsEqual(this.dragCardID, cardID);\n }\n\n /** Returns cards belonging to a specific column. */\n getColumnCards(columnKey: string): KanbanCardData[] {\n return this.Cards.filter(c => c.ColumnKey === columnKey);\n }\n\n /** @internal */\n onDragStart(event: DragEvent, card: KanbanCardData, columnKey: string): void {\n this.dragCardID = card.ID;\n this.dragCard = card;\n this.dragSourceColumn = columnKey;\n event.dataTransfer?.setData('text/plain', card.ID);\n if (event.dataTransfer) {\n event.dataTransfer.effectAllowed = 'move';\n }\n }\n\n /** @internal */\n onDragEnd(): void {\n this.dragCardID = null;\n this.dragCard = null;\n this.dragSourceColumn = null;\n this.dragOverColumn = null;\n this.cdr.markForCheck();\n }\n\n /** @internal */\n onDragOver(event: DragEvent, columnKey: string): void {\n event.preventDefault();\n if (event.dataTransfer) {\n event.dataTransfer.dropEffect = 'move';\n }\n if (this.dragOverColumn !== columnKey) {\n this.dragOverColumn = columnKey;\n this.cdr.markForCheck();\n }\n }\n\n /** @internal */\n onDragLeave(columnKey: string): void {\n if (this.dragOverColumn === columnKey) {\n this.dragOverColumn = null;\n this.cdr.markForCheck();\n }\n }\n\n /** @internal */\n onDrop(event: DragEvent, targetColumn: string): void {\n event.preventDefault();\n this.dragOverColumn = null;\n\n if (!this.dragCard || this.dragSourceColumn === targetColumn) {\n this.onDragEnd();\n return;\n }\n\n this.CardMoved.emit({\n Card: this.dragCard,\n FromColumn: this.dragSourceColumn!,\n ToColumn: targetColumn,\n });\n\n this.onDragEnd();\n }\n}\n"]}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import * as i0 from "@angular/core";
|
|
2
|
+
import * as i1 from "./components/kanban-board.component";
|
|
3
|
+
export declare class MjKanbanModule {
|
|
4
|
+
static ɵfac: i0.ɵɵFactoryDeclaration<MjKanbanModule, never>;
|
|
5
|
+
static ɵmod: i0.ɵɵNgModuleDeclaration<MjKanbanModule, never, [typeof i1.MjKanbanBoardComponent], [typeof i1.MjKanbanBoardComponent]>;
|
|
6
|
+
static ɵinj: i0.ɵɵInjectorDeclaration<MjKanbanModule>;
|
|
7
|
+
}
|
|
8
|
+
//# sourceMappingURL=kanban.module.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"kanban.module.d.ts","sourceRoot":"","sources":["../../src/lib/kanban.module.ts"],"names":[],"mappings":";;AAGA,qBAIa,cAAc;yCAAd,cAAc;0CAAd,cAAc;0CAAd,cAAc;CAAI"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { NgModule } from '@angular/core';
|
|
2
|
+
import { MjKanbanBoardComponent } from './components/kanban-board.component';
|
|
3
|
+
import * as i0 from "@angular/core";
|
|
4
|
+
export class MjKanbanModule {
|
|
5
|
+
static ɵfac = function MjKanbanModule_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || MjKanbanModule)(); };
|
|
6
|
+
static ɵmod = /*@__PURE__*/ i0.ɵɵdefineNgModule({ type: MjKanbanModule });
|
|
7
|
+
static ɵinj = /*@__PURE__*/ i0.ɵɵdefineInjector({ imports: [MjKanbanBoardComponent] });
|
|
8
|
+
}
|
|
9
|
+
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MjKanbanModule, [{
|
|
10
|
+
type: NgModule,
|
|
11
|
+
args: [{
|
|
12
|
+
imports: [MjKanbanBoardComponent],
|
|
13
|
+
exports: [MjKanbanBoardComponent]
|
|
14
|
+
}]
|
|
15
|
+
}], null, null); })();
|
|
16
|
+
(function () { (typeof ngJitMode === "undefined" || ngJitMode) && i0.ɵɵsetNgModuleScope(MjKanbanModule, { imports: [MjKanbanBoardComponent], exports: [MjKanbanBoardComponent] }); })();
|
|
17
|
+
//# sourceMappingURL=kanban.module.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"kanban.module.js","sourceRoot":"","sources":["../../src/lib/kanban.module.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,EAAE,sBAAsB,EAAE,MAAM,qCAAqC,CAAC;;AAM7E,MAAM,OAAO,cAAc;wGAAd,cAAc;4DAAd,cAAc;gEAHb,sBAAsB;;iFAGvB,cAAc;cAJ1B,QAAQ;eAAC;gBACN,OAAO,EAAE,CAAC,sBAAsB,CAAC;gBACjC,OAAO,EAAE,CAAC,sBAAsB,CAAC;aACpC;;wFACY,cAAc,cAHb,sBAAsB,aACtB,sBAAsB","sourcesContent":["import { NgModule } from '@angular/core';\nimport { MjKanbanBoardComponent } from './components/kanban-board.component';\n\n@NgModule({\n imports: [MjKanbanBoardComponent],\n exports: [MjKanbanBoardComponent]\n})\nexport class MjKanbanModule { }\n"]}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A single card displayed on the Kanban board.
|
|
3
|
+
*/
|
|
4
|
+
export interface KanbanCardData {
|
|
5
|
+
/** Unique identifier for this card. */
|
|
6
|
+
ID: string;
|
|
7
|
+
/** Primary text displayed on the card. */
|
|
8
|
+
Title: string;
|
|
9
|
+
/** Secondary text displayed below the title. */
|
|
10
|
+
Subtitle?: string;
|
|
11
|
+
/** Left-border accent color (CSS color value). */
|
|
12
|
+
Color?: string;
|
|
13
|
+
/** Small badge text, e.g. priority level. */
|
|
14
|
+
BadgeText?: string;
|
|
15
|
+
/** Background color for the badge. */
|
|
16
|
+
BadgeColor?: string;
|
|
17
|
+
/** Text shown in the card footer, e.g. a due date. */
|
|
18
|
+
FooterText?: string;
|
|
19
|
+
/** The column key this card belongs to. */
|
|
20
|
+
ColumnKey: string;
|
|
21
|
+
/** Arbitrary consumer data passed through with events. */
|
|
22
|
+
Data?: any;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Defines a single column on the Kanban board.
|
|
26
|
+
*/
|
|
27
|
+
export interface KanbanColumnDef {
|
|
28
|
+
/** Unique key identifying this column. Cards with matching `ColumnKey` appear here. */
|
|
29
|
+
Key: string;
|
|
30
|
+
/** Display label shown in the column header. */
|
|
31
|
+
Label: string;
|
|
32
|
+
/** Header accent color (CSS color value). */
|
|
33
|
+
Color?: string;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Event emitted when a card is dragged from one column to another.
|
|
37
|
+
*/
|
|
38
|
+
export interface KanbanCardMovedEvent {
|
|
39
|
+
/** The card that was moved. */
|
|
40
|
+
Card: KanbanCardData;
|
|
41
|
+
/** The column key the card was dragged from. */
|
|
42
|
+
FromColumn: string;
|
|
43
|
+
/** The column key the card was dropped into. */
|
|
44
|
+
ToColumn: string;
|
|
45
|
+
}
|
|
46
|
+
//# sourceMappingURL=kanban.models.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"kanban.models.d.ts","sourceRoot":"","sources":["../../../src/lib/models/kanban.models.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,WAAW,cAAc;IAC3B,uCAAuC;IACvC,EAAE,EAAE,MAAM,CAAC;IACX,0CAA0C;IAC1C,KAAK,EAAE,MAAM,CAAC;IACd,gDAAgD;IAChD,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,kDAAkD;IAClD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,6CAA6C;IAC7C,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,sCAAsC;IACtC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,sDAAsD;IACtD,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,2CAA2C;IAC3C,SAAS,EAAE,MAAM,CAAC;IAClB,0DAA0D;IAC1D,IAAI,CAAC,EAAE,GAAG,CAAC;CACd;AAED;;GAEG;AACH,MAAM,WAAW,eAAe;IAC5B,uFAAuF;IACvF,GAAG,EAAE,MAAM,CAAC;IACZ,gDAAgD;IAChD,KAAK,EAAE,MAAM,CAAC;IACd,6CAA6C;IAC7C,KAAK,CAAC,EAAE,MAAM,CAAC;CAClB;AAED;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACjC,+BAA+B;IAC/B,IAAI,EAAE,cAAc,CAAC;IACrB,gDAAgD;IAChD,UAAU,EAAE,MAAM,CAAC;IACnB,gDAAgD;IAChD,QAAQ,EAAE,MAAM,CAAC;CACpB"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"kanban.models.js","sourceRoot":"","sources":["../../../src/lib/models/kanban.models.ts"],"names":[],"mappings":"","sourcesContent":["/**\n * A single card displayed on the Kanban board.\n */\nexport interface KanbanCardData {\n /** Unique identifier for this card. */\n ID: string;\n /** Primary text displayed on the card. */\n Title: string;\n /** Secondary text displayed below the title. */\n Subtitle?: string;\n /** Left-border accent color (CSS color value). */\n Color?: string;\n /** Small badge text, e.g. priority level. */\n BadgeText?: string;\n /** Background color for the badge. */\n BadgeColor?: string;\n /** Text shown in the card footer, e.g. a due date. */\n FooterText?: string;\n /** The column key this card belongs to. */\n ColumnKey: string;\n /** Arbitrary consumer data passed through with events. */\n Data?: any;\n}\n\n/**\n * Defines a single column on the Kanban board.\n */\nexport interface KanbanColumnDef {\n /** Unique key identifying this column. Cards with matching `ColumnKey` appear here. */\n Key: string;\n /** Display label shown in the column header. */\n Label: string;\n /** Header accent color (CSS color value). */\n Color?: string;\n}\n\n/**\n * Event emitted when a card is dragged from one column to another.\n */\nexport interface KanbanCardMovedEvent {\n /** The card that was moved. */\n Card: KanbanCardData;\n /** The column key the card was dragged from. */\n FromColumn: string;\n /** The column key the card was dropped into. */\n ToColumn: string;\n}\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"public-api.d.ts","sourceRoot":"","sources":["../src/public-api.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,cAAc,qBAAqB,CAAC;AACpC,cAAc,yCAAyC,CAAC;AACxD,cAAc,4BAA4B,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"public-api.js","sourceRoot":"","sources":["../src/public-api.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,cAAc,qBAAqB,CAAC;AACpC,cAAc,yCAAyC,CAAC;AACxD,cAAc,4BAA4B,CAAC","sourcesContent":["/**\n * Public API Surface of @memberjunction/ng-kanban\n */\nexport * from './lib/kanban.module';\nexport * from './lib/components/kanban-board.component';\nexport * from './lib/models/kanban.models';\n"]}
|
package/package.json
CHANGED
|
@@ -1,10 +1,38 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@memberjunction/ng-kanban",
|
|
3
|
-
"version": "
|
|
4
|
-
"description": "
|
|
5
|
-
"
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
"
|
|
9
|
-
]
|
|
3
|
+
"version": "5.24.0",
|
|
4
|
+
"description": "MemberJunction: Generic Kanban board component with drag-and-drop column management",
|
|
5
|
+
"main": "./dist/public-api.js",
|
|
6
|
+
"typings": "./dist/public-api.d.ts",
|
|
7
|
+
"files": [
|
|
8
|
+
"/dist"
|
|
9
|
+
],
|
|
10
|
+
"scripts": {
|
|
11
|
+
"test": "echo \"No tests configured yet\"",
|
|
12
|
+
"build": "ngc",
|
|
13
|
+
"test:watch": "vitest"
|
|
14
|
+
},
|
|
15
|
+
"keywords": [],
|
|
16
|
+
"author": "",
|
|
17
|
+
"license": "ISC",
|
|
18
|
+
"devDependencies": {
|
|
19
|
+
"@angular/compiler": "21.1.3",
|
|
20
|
+
"@angular/compiler-cli": "21.1.3",
|
|
21
|
+
"vitest": "^4.0.18"
|
|
22
|
+
},
|
|
23
|
+
"peerDependencies": {
|
|
24
|
+
"@angular/common": "21.1.3",
|
|
25
|
+
"@angular/core": "21.1.3"
|
|
26
|
+
},
|
|
27
|
+
"dependencies": {
|
|
28
|
+
"@angular/common": "21.1.3",
|
|
29
|
+
"@angular/core": "21.1.3",
|
|
30
|
+
"@memberjunction/global": "5.24.0",
|
|
31
|
+
"tslib": "^2.8.1"
|
|
32
|
+
},
|
|
33
|
+
"sideEffects": false,
|
|
34
|
+
"repository": {
|
|
35
|
+
"type": "git",
|
|
36
|
+
"url": "https://github.com/MemberJunction/MJ"
|
|
37
|
+
}
|
|
10
38
|
}
|
package/README.md
DELETED
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
# @memberjunction/ng-kanban
|
|
2
|
-
|
|
3
|
-
## ⚠️ IMPORTANT NOTICE ⚠️
|
|
4
|
-
|
|
5
|
-
**This package is created solely for the purpose of setting up OIDC (OpenID Connect) trusted publishing with npm.**
|
|
6
|
-
|
|
7
|
-
This is **NOT** a functional package and contains **NO** code or functionality beyond the OIDC setup configuration.
|
|
8
|
-
|
|
9
|
-
## Purpose
|
|
10
|
-
|
|
11
|
-
This package exists to:
|
|
12
|
-
1. Configure OIDC trusted publishing for the package name `@memberjunction/ng-kanban`
|
|
13
|
-
2. Enable secure, token-less publishing from CI/CD workflows
|
|
14
|
-
3. Establish provenance for packages published under this name
|
|
15
|
-
|
|
16
|
-
## What is OIDC Trusted Publishing?
|
|
17
|
-
|
|
18
|
-
OIDC trusted publishing allows package maintainers to publish packages directly from their CI/CD workflows without needing to manage npm access tokens. Instead, it uses OpenID Connect to establish trust between the CI/CD provider (like GitHub Actions) and npm.
|
|
19
|
-
|
|
20
|
-
## Setup Instructions
|
|
21
|
-
|
|
22
|
-
To properly configure OIDC trusted publishing for this package:
|
|
23
|
-
|
|
24
|
-
1. Go to [npmjs.com](https://www.npmjs.com/) and navigate to your package settings
|
|
25
|
-
2. Configure the trusted publisher (e.g., GitHub Actions)
|
|
26
|
-
3. Specify the repository and workflow that should be allowed to publish
|
|
27
|
-
4. Use the configured workflow to publish your actual package
|
|
28
|
-
|
|
29
|
-
## DO NOT USE THIS PACKAGE
|
|
30
|
-
|
|
31
|
-
This package is a placeholder for OIDC configuration only. It:
|
|
32
|
-
- Contains no executable code
|
|
33
|
-
- Provides no functionality
|
|
34
|
-
- Should not be installed as a dependency
|
|
35
|
-
- Exists only for administrative purposes
|
|
36
|
-
|
|
37
|
-
## More Information
|
|
38
|
-
|
|
39
|
-
For more details about npm's trusted publishing feature, see:
|
|
40
|
-
- [npm Trusted Publishing Documentation](https://docs.npmjs.com/generating-provenance-statements)
|
|
41
|
-
- [GitHub Actions OIDC Documentation](https://docs.github.com/en/actions/deployment/security-hardening-your-deployments/about-security-hardening-with-openid-connect)
|
|
42
|
-
|
|
43
|
-
---
|
|
44
|
-
|
|
45
|
-
**Maintained for OIDC setup purposes only**
|