@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.
@@ -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,2 @@
1
+ export {};
2
+ //# sourceMappingURL=kanban.models.js.map
@@ -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,7 @@
1
+ /**
2
+ * Public API Surface of @memberjunction/ng-kanban
3
+ */
4
+ export * from './lib/kanban.module';
5
+ export * from './lib/components/kanban-board.component';
6
+ export * from './lib/models/kanban.models';
7
+ //# sourceMappingURL=public-api.d.ts.map
@@ -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,7 @@
1
+ /**
2
+ * Public API Surface of @memberjunction/ng-kanban
3
+ */
4
+ export * from './lib/kanban.module';
5
+ export * from './lib/components/kanban-board.component';
6
+ export * from './lib/models/kanban.models';
7
+ //# sourceMappingURL=public-api.js.map
@@ -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": "0.0.1",
4
- "description": "OIDC trusted publishing setup package for @memberjunction/ng-kanban",
5
- "keywords": [
6
- "oidc",
7
- "trusted-publishing",
8
- "setup"
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**