@vadem/ui 0.0.3 → 0.2.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,1021 @@
1
+ import { NgTemplateOutlet } from "@angular/common";
2
+ import * as i0 from "@angular/core";
3
+ import { ChangeDetectionStrategy, Component, DestroyRef, Directive, ElementRef, TemplateRef, ViewEncapsulation, afterNextRender, afterRenderEffect, booleanAttribute, computed, contentChild, effect, inject, input, output, signal } from "@angular/core";
4
+ import { VdButton } from "@vadem/ui/button";
5
+ import { vdUniqueId } from "@vadem/ui/field";
6
+ import { VdIcon } from "@vadem/ui/icon";
7
+ import { vdTexts } from "@vadem/ui/locale";
8
+ import * as i1 from "@vadem/ui/menu";
9
+ import { VD_MENU } from "@vadem/ui/menu";
10
+ function vdKanbanValid(columns) {
11
+ const columnIds = /* @__PURE__ */ new Set();
12
+ const cardIds = /* @__PURE__ */ new Set();
13
+ for (const column of columns) {
14
+ if (!isId(column.id) || columnIds.has(column.id)) return false;
15
+ columnIds.add(column.id);
16
+ for (const card of column.cards) {
17
+ if (!isId(card.id) || cardIds.has(card.id)) return false;
18
+ cardIds.add(card.id);
19
+ }
20
+ }
21
+ return true;
22
+ }
23
+ function vdKanbanFind(columns, cardId) {
24
+ for (const column of columns) {
25
+ const index = column.cards.findIndex((card) => card.id === cardId);
26
+ if (index >= 0) return {
27
+ columnId: column.id,
28
+ index
29
+ };
30
+ }
31
+ return null;
32
+ }
33
+ function vdKanbanMoveRequest(columns, cardId, to, method) {
34
+ const from = vdKanbanFind(columns, cardId);
35
+ const target = columns.find((column) => column.id === to.columnId);
36
+ if (!from || !target || !Number.isInteger(to.index)) return null;
37
+ const size = target.cards.length - (target.id === from.columnId ? 1 : 0);
38
+ const index = Math.max(0, Math.min(to.index, size));
39
+ if (target.id === from.columnId && index === from.index) return null;
40
+ return {
41
+ cardId,
42
+ fromColumnId: from.columnId,
43
+ toColumnId: target.id,
44
+ fromIndex: from.index,
45
+ toIndex: index,
46
+ method
47
+ };
48
+ }
49
+ function vdKanbanApplyMove(columns, move) {
50
+ const from = vdKanbanFind(columns, move.cardId);
51
+ if (!from || !columns.some((column) => column.id === move.toColumnId)) return columns;
52
+ const card = columns.find((column) => column.id === from.columnId).cards[from.index];
53
+ return columns.map((column) => {
54
+ if (column.id !== from.columnId && column.id !== move.toColumnId) return column;
55
+ const cards = column.cards.filter((c) => c.id !== move.cardId);
56
+ if (column.id === move.toColumnId) cards.splice(Math.max(0, Math.min(move.toIndex, cards.length)), 0, card);
57
+ return {
58
+ ...column,
59
+ cards
60
+ };
61
+ });
62
+ }
63
+ function vdKanbanInsertIndex(y, cards) {
64
+ if (!Number.isFinite(y)) return 0;
65
+ let index = 0;
66
+ for (const rect of cards) if (y > (rect.top + rect.bottom) / 2) index++;
67
+ return index;
68
+ }
69
+ function vdKanbanColumnAt(x, columns) {
70
+ if (!Number.isFinite(x)) return -1;
71
+ return columns.findIndex((rect) => x >= rect.left && x <= rect.right);
72
+ }
73
+ function isId(id) {
74
+ return typeof id === "string" && id.trim().length > 0;
75
+ }
76
+ var VdKanbanCard = class VdKanbanCard {
77
+ template = inject(TemplateRef);
78
+ static ɵfac = i0.ɵɵngDeclareFactory({
79
+ minVersion: "12.0.0",
80
+ version: "22.2.1",
81
+ ngImport: i0,
82
+ type: VdKanbanCard,
83
+ deps: [],
84
+ target: i0.ɵɵFactoryTarget.Directive
85
+ });
86
+ static ɵdir = i0.ɵɵngDeclareDirective({
87
+ minVersion: "14.0.0",
88
+ version: "22.2.1",
89
+ type: VdKanbanCard,
90
+ isStandalone: true,
91
+ selector: "ng-template[vdKanbanCard]",
92
+ ngImport: i0
93
+ });
94
+ };
95
+ i0.ɵɵngDeclareClassMetadata({
96
+ minVersion: "12.0.0",
97
+ version: "22.2.1",
98
+ ngImport: i0,
99
+ type: VdKanbanCard,
100
+ decorators: [{
101
+ type: Directive,
102
+ args: [{ selector: "ng-template[vdKanbanCard]" }]
103
+ }]
104
+ });
105
+ var VdKanbanColumnHeader = class VdKanbanColumnHeader {
106
+ template = inject(TemplateRef);
107
+ static ɵfac = i0.ɵɵngDeclareFactory({
108
+ minVersion: "12.0.0",
109
+ version: "22.2.1",
110
+ ngImport: i0,
111
+ type: VdKanbanColumnHeader,
112
+ deps: [],
113
+ target: i0.ɵɵFactoryTarget.Directive
114
+ });
115
+ static ɵdir = i0.ɵɵngDeclareDirective({
116
+ minVersion: "14.0.0",
117
+ version: "22.2.1",
118
+ type: VdKanbanColumnHeader,
119
+ isStandalone: true,
120
+ selector: "ng-template[vdKanbanColumnHeader]",
121
+ ngImport: i0
122
+ });
123
+ };
124
+ i0.ɵɵngDeclareClassMetadata({
125
+ minVersion: "12.0.0",
126
+ version: "22.2.1",
127
+ ngImport: i0,
128
+ type: VdKanbanColumnHeader,
129
+ decorators: [{
130
+ type: Directive,
131
+ args: [{ selector: "ng-template[vdKanbanColumnHeader]" }]
132
+ }]
133
+ });
134
+ const INTERACTIVE = "a, button, input, select, textarea, label, [contenteditable], [tabindex]";
135
+ const INTERACTIVE_TOUCH = "button, input, select, textarea, label, [contenteditable]";
136
+ const LONG_PRESS_MS = 400;
137
+ const TOUCH_SLOP = 8;
138
+ const EDGE = 48;
139
+ const EDGE_STEP = 14;
140
+ var VdKanban = class VdKanban {
141
+ columns = input([], ...ngDevMode ? [{ debugName: "columns" }] : /* istanbul ignore next */ []);
142
+ disabled = input(false, {
143
+ ...ngDevMode ? { debugName: "disabled" } : /* istanbul ignore next */ {},
144
+ transform: booleanAttribute
145
+ });
146
+ label = input(...ngDevMode ? [void 0, { debugName: "label" }] : /* istanbul ignore next */ []);
147
+ instructions = input(...ngDevMode ? [void 0, { debugName: "instructions" }] : /* istanbul ignore next */ []);
148
+ emptyText = input(...ngDevMode ? [void 0, { debugName: "emptyText" }] : /* istanbul ignore next */ []);
149
+ invalidText = input(...ngDevMode ? [void 0, { debugName: "invalidText" }] : /* istanbul ignore next */ []);
150
+ countText = input(...ngDevMode ? [void 0, { debugName: "countText" }] : /* istanbul ignore next */ []);
151
+ moveLabel = input(...ngDevMode ? [void 0, { debugName: "moveLabel" }] : /* istanbul ignore next */ []);
152
+ upText = input(...ngDevMode ? [void 0, { debugName: "upText" }] : /* istanbul ignore next */ []);
153
+ downText = input(...ngDevMode ? [void 0, { debugName: "downText" }] : /* istanbul ignore next */ []);
154
+ moveToText = input(...ngDevMode ? [void 0, { debugName: "moveToText" }] : /* istanbul ignore next */ []);
155
+ movedText = input(...ngDevMode ? [void 0, { debugName: "movedText" }] : /* istanbul ignore next */ []);
156
+ canceledText = input(...ngDevMode ? [void 0, { debugName: "canceledText" }] : /* istanbul ignore next */ []);
157
+ cardMove = output();
158
+ t = vdTexts("kanban", {
159
+ label: this.label,
160
+ instructions: this.instructions,
161
+ emptyText: this.emptyText,
162
+ invalidText: this.invalidText,
163
+ countText: this.countText,
164
+ moveLabel: this.moveLabel,
165
+ upText: this.upText,
166
+ downText: this.downText,
167
+ moveToText: this.moveToText,
168
+ movedText: this.movedText,
169
+ canceledText: this.canceledText
170
+ });
171
+ cardTemplate = contentChild(VdKanbanCard, ...ngDevMode ? [{ debugName: "cardTemplate" }] : /* istanbul ignore next */ []);
172
+ columnHeader = contentChild(VdKanbanColumnHeader, ...ngDevMode ? [{ debugName: "columnHeader" }] : /* istanbul ignore next */ []);
173
+ valid = computed(() => vdKanbanValid(this.columns()), ...ngDevMode ? [{ debugName: "valid" }] : /* istanbul ignore next */ []);
174
+ baseId = vdUniqueId("vd-kanban");
175
+ instructionsId = this.baseId + "-instructions";
176
+ draggingId = signal(null, ...ngDevMode ? [{ debugName: "draggingId" }] : /* istanbul ignore next */ []);
177
+ drop = signal(null, ...ngDevMode ? [{ debugName: "drop" }] : /* istanbul ignore next */ []);
178
+ messages = signal([], ...ngDevMode ? [{ debugName: "messages" }] : /* istanbul ignore next */ []);
179
+ host = inject(ElementRef).nativeElement;
180
+ drag = null;
181
+ pendingFocus = null;
182
+ suppressClick = false;
183
+ serial = 0;
184
+ constructor() {
185
+ effect(() => {
186
+ const columns = this.columns(), disabled = this.disabled();
187
+ if (this.drag && (disabled || columns !== this.drag.columns)) this.finish(true);
188
+ });
189
+ afterRenderEffect(() => {
190
+ this.columns();
191
+ const id = this.pendingFocus;
192
+ if (!id) return;
193
+ const card = this.cardElement(id);
194
+ if (!card) return;
195
+ this.pendingFocus = null;
196
+ const active = this.host.ownerDocument.activeElement;
197
+ if (!(!active || active === this.host.ownerDocument.body || !active.isConnected) && !this.host.contains(active)) return;
198
+ if (card.contains(active)) return;
199
+ const target = card.querySelector(".vd-kanban__move:not(:disabled)") ?? card;
200
+ target.focus({ preventScroll: true });
201
+ target.scrollIntoView({
202
+ block: "nearest",
203
+ inline: "nearest",
204
+ behavior: "instant"
205
+ });
206
+ });
207
+ const destroyRef = inject(DestroyRef);
208
+ destroyRef.onDestroy(() => this.finish(false));
209
+ afterNextRender(() => {
210
+ const touchMove = (event) => {
211
+ if (this.drag?.touch && this.drag.active) event.preventDefault();
212
+ };
213
+ const contextMenu = (event) => {
214
+ if (this.drag?.touch) event.preventDefault();
215
+ };
216
+ const click = (event) => {
217
+ if (!this.suppressClick) return;
218
+ this.suppressClick = false;
219
+ event.preventDefault();
220
+ event.stopPropagation();
221
+ };
222
+ this.host.addEventListener("touchmove", touchMove, { passive: false });
223
+ this.host.addEventListener("contextmenu", contextMenu);
224
+ this.host.addEventListener("click", click, true);
225
+ destroyRef.onDestroy(() => {
226
+ this.host.removeEventListener("touchmove", touchMove);
227
+ this.host.removeEventListener("contextmenu", contextMenu);
228
+ this.host.removeEventListener("click", click, true);
229
+ });
230
+ });
231
+ }
232
+ moveTo(cardId, columnId, index) {
233
+ if (this.disabled() || !this.valid() || this.drag) return;
234
+ this.request(vdKanbanMoveRequest(this.columns(), cardId, {
235
+ columnId,
236
+ index
237
+ }, "menu"));
238
+ }
239
+ request(move) {
240
+ if (!move) return;
241
+ const columns = this.columns();
242
+ const target = columns.find((column) => column.id === move.toColumnId);
243
+ const card = columns.find((c) => c.id === move.fromColumnId).cards[move.fromIndex];
244
+ const total = target.cards.length + (move.fromColumnId === move.toColumnId ? 0 : 1);
245
+ this.pendingFocus = move.cardId;
246
+ this.announce(this.t().movedText(card.label, target.title, move.toIndex + 1, total));
247
+ this.cardMove.emit(move);
248
+ }
249
+ start(event, card) {
250
+ this.suppressClick = false;
251
+ if (!event.isPrimary || event.button !== 0 || this.disabled() || !this.valid() || this.drag) return;
252
+ const element = event.currentTarget;
253
+ const touch = event.pointerType === "touch";
254
+ const hit = event.target?.closest(touch ? INTERACTIVE_TOUCH : INTERACTIVE);
255
+ if (hit && hit !== element && element.contains(hit)) return;
256
+ if (typeof element.setPointerCapture !== "function") return;
257
+ this.drag = {
258
+ pointer: event.pointerId,
259
+ element,
260
+ columns: this.columns(),
261
+ cardId: card.id,
262
+ label: card.label,
263
+ touch,
264
+ x: event.clientX,
265
+ y: event.clientY,
266
+ lastX: event.clientX,
267
+ lastY: event.clientY,
268
+ active: false,
269
+ timer: null,
270
+ ghost: null,
271
+ offsetX: 0,
272
+ offsetY: 0,
273
+ scroll: 0,
274
+ frame: null
275
+ };
276
+ if (touch) {
277
+ const drag = this.drag;
278
+ drag.timer = setTimeout(() => {
279
+ drag.timer = null;
280
+ if (this.drag !== drag) return;
281
+ try {
282
+ element.setPointerCapture(drag.pointer);
283
+ } catch {
284
+ this.finish(false);
285
+ return;
286
+ }
287
+ this.activate(drag);
288
+ }, LONG_PRESS_MS);
289
+ return;
290
+ }
291
+ try {
292
+ element.setPointerCapture(event.pointerId);
293
+ } catch {
294
+ this.drag = null;
295
+ return;
296
+ }
297
+ event.preventDefault();
298
+ }
299
+ pointerMove(event) {
300
+ const drag = this.drag;
301
+ if (!drag || drag.pointer !== event.pointerId) return;
302
+ drag.lastX = event.clientX;
303
+ drag.lastY = event.clientY;
304
+ const distance = Math.hypot(event.clientX - drag.x, event.clientY - drag.y);
305
+ if (!drag.active) {
306
+ if (drag.touch) {
307
+ if (distance > TOUCH_SLOP) this.finish(false);
308
+ return;
309
+ }
310
+ if (event.buttons === 0) {
311
+ this.finish(false);
312
+ return;
313
+ }
314
+ if (distance < 4) return;
315
+ this.activate(drag);
316
+ }
317
+ this.track(drag);
318
+ }
319
+ end(event) {
320
+ const drag = this.drag;
321
+ if (!drag || drag.pointer !== event.pointerId) return;
322
+ const drop = drag.active ? this.target(event.clientX, event.clientY) : null;
323
+ this.suppressClick = drag.active;
324
+ this.finish(drag.active && !drop);
325
+ if (drop) this.request(vdKanbanMoveRequest(this.columns(), drag.cardId, drop, "pointer"));
326
+ }
327
+ pointerCancel(event) {
328
+ if (event.pointerId === this.drag?.pointer) this.finish(!!this.drag?.active);
329
+ }
330
+ key(event) {
331
+ if (!this.drag || event.isComposing) return;
332
+ if (event.key === "Escape" || event.key === "Tab") {
333
+ if (event.key === "Escape") {
334
+ event.preventDefault();
335
+ event.stopPropagation();
336
+ }
337
+ this.finish(true);
338
+ }
339
+ }
340
+ activate(drag) {
341
+ drag.active = true;
342
+ const { element } = drag;
343
+ if (!element.contains(this.host.ownerDocument.activeElement)) element.focus({ preventScroll: true });
344
+ const rect = element.getBoundingClientRect();
345
+ drag.offsetX = drag.x - rect.left;
346
+ drag.offsetY = drag.y - rect.top;
347
+ const ghost = element.cloneNode(true);
348
+ ghost.querySelectorAll("[popover]").forEach((node) => node.remove());
349
+ ghost.querySelectorAll("[id]").forEach((node) => node.removeAttribute("id"));
350
+ for (const name of [
351
+ "data-card-id",
352
+ "data-dragging",
353
+ "data-drop",
354
+ "tabindex"
355
+ ]) ghost.removeAttribute(name);
356
+ ghost.className = "vd-kanban__card vd-kanban__ghost";
357
+ ghost.setAttribute("aria-hidden", "true");
358
+ ghost.inert = true;
359
+ ghost.style.setProperty("inline-size", `${rect.width}px`);
360
+ ghost.setAttribute("popover", "manual");
361
+ this.host.append(ghost);
362
+ ghost.showPopover?.();
363
+ drag.ghost = ghost;
364
+ this.draggingId.set(drag.cardId);
365
+ this.track(drag);
366
+ }
367
+ track(drag) {
368
+ const ghost = drag.ghost;
369
+ if (ghost) {
370
+ const rtl = this.host.ownerDocument.defaultView?.getComputedStyle(this.host).direction === "rtl";
371
+ const left = drag.lastX - drag.offsetX;
372
+ const x = rtl ? left + ghost.offsetWidth - this.host.ownerDocument.documentElement.clientWidth : left;
373
+ ghost.style.setProperty("--vd-kanban-ghost-x", `${x}px`);
374
+ ghost.style.setProperty("--vd-kanban-ghost-y", `${drag.lastY - drag.offsetY}px`);
375
+ }
376
+ this.drop.set(this.target(drag.lastX, drag.lastY));
377
+ const scroller = this.host.querySelector(".vd-kanban__columns");
378
+ if (!scroller) return;
379
+ const rect = scroller.getBoundingClientRect();
380
+ drag.scroll = drag.lastX < rect.left + EDGE ? -1 : drag.lastX > rect.right - EDGE ? 1 : 0;
381
+ if (drag.scroll && drag.frame === null) {
382
+ const view = this.host.ownerDocument.defaultView;
383
+ const step = () => {
384
+ if (this.drag !== drag || !drag.scroll || !view) {
385
+ drag.frame = null;
386
+ return;
387
+ }
388
+ scroller.scrollBy({
389
+ left: drag.scroll * EDGE_STEP,
390
+ behavior: "instant"
391
+ });
392
+ this.drop.set(this.target(drag.lastX, drag.lastY));
393
+ drag.frame = view.requestAnimationFrame(step);
394
+ };
395
+ drag.frame = view?.requestAnimationFrame(step) ?? null;
396
+ }
397
+ }
398
+ target(x, y) {
399
+ const drag = this.drag;
400
+ if (!drag) return null;
401
+ const sections = Array.from(this.host.querySelectorAll(".vd-kanban__column[data-column-id]"));
402
+ const index = vdKanbanColumnAt(x, sections.map((s) => s.getBoundingClientRect()));
403
+ if (index < 0) return null;
404
+ const section = sections[index];
405
+ const rect = section.getBoundingClientRect();
406
+ if (y < rect.top || y > rect.bottom) return null;
407
+ const others = Array.from(section.querySelectorAll(".vd-kanban__card[data-card-id]")).filter((el) => el.dataset["cardId"] !== drag.cardId);
408
+ const at = vdKanbanInsertIndex(y, others.map((el) => el.getBoundingClientRect()));
409
+ const columnId = section.dataset["columnId"];
410
+ const from = vdKanbanFind(this.columns(), drag.cardId);
411
+ if (from && from.columnId === columnId && from.index === at) return null;
412
+ const marker = others[at] ?? others[others.length - 1] ?? null;
413
+ return {
414
+ columnId,
415
+ index: at,
416
+ cardId: marker?.dataset["cardId"] ?? null,
417
+ edge: marker ? others[at] ? "before" : "after" : null
418
+ };
419
+ }
420
+ finish(announce) {
421
+ const drag = this.drag;
422
+ this.drag = null;
423
+ this.draggingId.set(null);
424
+ this.drop.set(null);
425
+ if (!drag) return;
426
+ if (drag.timer !== null) clearTimeout(drag.timer);
427
+ if (drag.frame !== null) this.host.ownerDocument.defaultView?.cancelAnimationFrame(drag.frame);
428
+ drag.ghost?.remove();
429
+ if (drag.element.hasPointerCapture?.(drag.pointer)) drag.element.releasePointerCapture(drag.pointer);
430
+ if (announce && drag.active) this.announce(this.t().canceledText(drag.label));
431
+ }
432
+ announce(text) {
433
+ this.messages.set([{
434
+ serial: ++this.serial,
435
+ text
436
+ }]);
437
+ }
438
+ cardElement(id) {
439
+ return Array.from(this.host.querySelectorAll(".vd-kanban__card[data-card-id]")).find((el) => el.dataset["cardId"] === id) ?? null;
440
+ }
441
+ static ɵfac = i0.ɵɵngDeclareFactory({
442
+ minVersion: "12.0.0",
443
+ version: "22.2.1",
444
+ ngImport: i0,
445
+ type: VdKanban,
446
+ deps: [],
447
+ target: i0.ɵɵFactoryTarget.Component
448
+ });
449
+ static ɵcmp = i0.ɵɵngDeclareComponent({
450
+ minVersion: "17.0.0",
451
+ version: "22.2.1",
452
+ type: VdKanban,
453
+ isStandalone: true,
454
+ selector: "vd-kanban",
455
+ inputs: {
456
+ columns: {
457
+ classPropertyName: "columns",
458
+ publicName: "columns",
459
+ isSignal: true,
460
+ isRequired: false,
461
+ transformFunction: null
462
+ },
463
+ disabled: {
464
+ classPropertyName: "disabled",
465
+ publicName: "disabled",
466
+ isSignal: true,
467
+ isRequired: false,
468
+ transformFunction: null
469
+ },
470
+ label: {
471
+ classPropertyName: "label",
472
+ publicName: "label",
473
+ isSignal: true,
474
+ isRequired: false,
475
+ transformFunction: null
476
+ },
477
+ instructions: {
478
+ classPropertyName: "instructions",
479
+ publicName: "instructions",
480
+ isSignal: true,
481
+ isRequired: false,
482
+ transformFunction: null
483
+ },
484
+ emptyText: {
485
+ classPropertyName: "emptyText",
486
+ publicName: "emptyText",
487
+ isSignal: true,
488
+ isRequired: false,
489
+ transformFunction: null
490
+ },
491
+ invalidText: {
492
+ classPropertyName: "invalidText",
493
+ publicName: "invalidText",
494
+ isSignal: true,
495
+ isRequired: false,
496
+ transformFunction: null
497
+ },
498
+ countText: {
499
+ classPropertyName: "countText",
500
+ publicName: "countText",
501
+ isSignal: true,
502
+ isRequired: false,
503
+ transformFunction: null
504
+ },
505
+ moveLabel: {
506
+ classPropertyName: "moveLabel",
507
+ publicName: "moveLabel",
508
+ isSignal: true,
509
+ isRequired: false,
510
+ transformFunction: null
511
+ },
512
+ upText: {
513
+ classPropertyName: "upText",
514
+ publicName: "upText",
515
+ isSignal: true,
516
+ isRequired: false,
517
+ transformFunction: null
518
+ },
519
+ downText: {
520
+ classPropertyName: "downText",
521
+ publicName: "downText",
522
+ isSignal: true,
523
+ isRequired: false,
524
+ transformFunction: null
525
+ },
526
+ moveToText: {
527
+ classPropertyName: "moveToText",
528
+ publicName: "moveToText",
529
+ isSignal: true,
530
+ isRequired: false,
531
+ transformFunction: null
532
+ },
533
+ movedText: {
534
+ classPropertyName: "movedText",
535
+ publicName: "movedText",
536
+ isSignal: true,
537
+ isRequired: false,
538
+ transformFunction: null
539
+ },
540
+ canceledText: {
541
+ classPropertyName: "canceledText",
542
+ publicName: "canceledText",
543
+ isSignal: true,
544
+ isRequired: false,
545
+ transformFunction: null
546
+ }
547
+ },
548
+ outputs: { cardMove: "cardMove" },
549
+ host: {
550
+ listeners: { "keydown": "key($event)" },
551
+ properties: { "attr.data-state": "!valid() ? \"invalid\" : disabled() ? \"disabled\" : draggingId() ? \"dragging\" : \"idle\"" },
552
+ classAttribute: "vd-kanban"
553
+ },
554
+ queries: [{
555
+ propertyName: "cardTemplate",
556
+ first: true,
557
+ predicate: VdKanbanCard,
558
+ descendants: true,
559
+ isSignal: true
560
+ }, {
561
+ propertyName: "columnHeader",
562
+ first: true,
563
+ predicate: VdKanbanColumnHeader,
564
+ descendants: true,
565
+ isSignal: true
566
+ }],
567
+ ngImport: i0,
568
+ template: `
569
+ <p class="vd-visually-hidden" [id]="instructionsId">{{ t().instructions }}</p>
570
+ @if (!valid()) {
571
+ <p class="vd-kanban__invalid">{{ t().invalidText }}</p>
572
+ } @else {
573
+ <div
574
+ class="vd-kanban__columns"
575
+ role="group"
576
+ [attr.aria-label]="t().label"
577
+ [attr.aria-describedby]="instructionsId"
578
+ >
579
+ @for (column of columns(); track column.id; let c = $index) {
580
+ <section
581
+ class="vd-kanban__column"
582
+ [attr.aria-labelledby]="baseId + '-title-' + c"
583
+ [attr.data-column-id]="column.id"
584
+ [attr.data-drop-target]="drop()?.columnId === column.id ? '' : null"
585
+ >
586
+ <header class="vd-kanban__header">
587
+ <p class="vd-kanban__title" [id]="baseId + '-title-' + c">
588
+ <bdi>{{ column.title }}</bdi>
589
+ <span class="vd-kanban__count" aria-hidden="true">{{ column.cards.length }}</span>
590
+ <span class="vd-visually-hidden">, {{ t().countText(column.cards.length) }}</span>
591
+ </p>
592
+ @if (columnHeader(); as header) {
593
+ <div class="vd-kanban__header-extra">
594
+ <ng-container
595
+ [ngTemplateOutlet]="header.template"
596
+ [ngTemplateOutletContext]="{ $implicit: column }"
597
+ />
598
+ </div>
599
+ }
600
+ </header>
601
+ <ul
602
+ class="vd-kanban__cards"
603
+ role="list"
604
+ [attr.aria-labelledby]="baseId + '-title-' + c"
605
+ >
606
+ @for (card of column.cards; track card.id; let i = $index, last = $last) {
607
+ <li
608
+ class="vd-kanban__card"
609
+ tabindex="-1"
610
+ [attr.data-card-id]="card.id"
611
+ [attr.data-dragging]="draggingId() === card.id ? '' : null"
612
+ [attr.data-drop]="drop()?.cardId === card.id ? drop()?.edge : null"
613
+ (pointerdown)="start($event, card)"
614
+ (pointermove)="pointerMove($event)"
615
+ (pointerup)="end($event)"
616
+ (pointercancel)="pointerCancel($event)"
617
+ (lostpointercapture)="pointerCancel($event)"
618
+ >
619
+ <div class="vd-kanban__content">
620
+ @if (cardTemplate(); as tpl) {
621
+ <ng-container
622
+ [ngTemplateOutlet]="tpl.template"
623
+ [ngTemplateOutletContext]="{ $implicit: card, column, index: i }"
624
+ />
625
+ } @else {
626
+ <bdi>{{ card.label }}</bdi>
627
+ }
628
+ </div>
629
+ <button
630
+ vdButton
631
+ class="vd-kanban__move"
632
+ variant="ghost"
633
+ size="sm"
634
+ iconOnly
635
+ type="button"
636
+ [disabled]="disabled()"
637
+ [attr.aria-label]="t().moveLabel(card.label)"
638
+ [vdMenuTrigger]="menu"
639
+ >
640
+ <vd-icon name="more-horizontal" />
641
+ </button>
642
+ <div vdMenu #menu="vdMenu">
643
+ <button
644
+ vdMenuItem
645
+ [disabled]="i === 0"
646
+ (click)="moveTo(card.id, column.id, i - 1)"
647
+ >
648
+ <vd-icon name="arrow-up" />{{ t().upText }}
649
+ </button>
650
+ <button
651
+ vdMenuItem
652
+ [disabled]="last"
653
+ (click)="moveTo(card.id, column.id, i + 1)"
654
+ >
655
+ <vd-icon name="arrow-down" />{{ t().downText }}
656
+ </button>
657
+ @if (columns().length > 1) {
658
+ <div vdMenuSeparator></div>
659
+ <p vdMenuLabel>{{ t().moveToText }}</p>
660
+ @for (target of columns(); track target.id) {
661
+ @if (target.id !== column.id) {
662
+ <button
663
+ vdMenuItem
664
+ (click)="moveTo(card.id, target.id, target.cards.length)"
665
+ >
666
+ <bdi>{{ target.title }}</bdi>
667
+ </button>
668
+ }
669
+ }
670
+ }
671
+ </div>
672
+ </li>
673
+ } @empty {
674
+ <li class="vd-kanban__empty">{{ t().emptyText }}</li>
675
+ }
676
+ </ul>
677
+ </section>
678
+ }
679
+ </div>
680
+ }
681
+ <p class="vd-visually-hidden" role="status" aria-atomic="true">
682
+ @for (message of messages(); track message.serial) {
683
+ <span>{{ message.text }}</span>
684
+ }
685
+ </p>
686
+ `,
687
+ isInline: true,
688
+ dependencies: [
689
+ {
690
+ kind: "directive",
691
+ type: NgTemplateOutlet,
692
+ selector: "[ngTemplateOutlet]",
693
+ inputs: [
694
+ "ngTemplateOutletContext",
695
+ "ngTemplateOutlet",
696
+ "ngTemplateOutletInjector"
697
+ ]
698
+ },
699
+ {
700
+ kind: "component",
701
+ type: VdButton,
702
+ selector: "button[vdButton], a[vdButton]",
703
+ inputs: [
704
+ "variant",
705
+ "size",
706
+ "disabled",
707
+ "loading",
708
+ "iconOnly",
709
+ "type"
710
+ ]
711
+ },
712
+ {
713
+ kind: "component",
714
+ type: VdIcon,
715
+ selector: "vd-icon",
716
+ inputs: ["name", "label"]
717
+ },
718
+ {
719
+ kind: "directive",
720
+ type: i1.VdMenu,
721
+ selector: "[vdMenu]",
722
+ exportAs: ["vdMenu"]
723
+ },
724
+ {
725
+ kind: "directive",
726
+ type: i1.VdMenuTrigger,
727
+ selector: "button[vdMenuTrigger]",
728
+ inputs: ["vdMenuTrigger"]
729
+ },
730
+ {
731
+ kind: "directive",
732
+ type: i1.VdMenuItem,
733
+ selector: "button[vdMenuItem], a[vdMenuItem]",
734
+ inputs: ["variant", "disabled"]
735
+ },
736
+ {
737
+ kind: "directive",
738
+ type: i1.VdMenuSeparator,
739
+ selector: "[vdMenuSeparator]"
740
+ },
741
+ {
742
+ kind: "directive",
743
+ type: i1.VdMenuLabel,
744
+ selector: "[vdMenuLabel]"
745
+ }
746
+ ],
747
+ changeDetection: i0.ChangeDetectionStrategy.OnPush,
748
+ encapsulation: i0.ViewEncapsulation.None
749
+ });
750
+ };
751
+ i0.ɵɵngDeclareClassMetadata({
752
+ minVersion: "12.0.0",
753
+ version: "22.2.1",
754
+ ngImport: i0,
755
+ type: VdKanban,
756
+ decorators: [{
757
+ type: Component,
758
+ args: [{
759
+ selector: "vd-kanban",
760
+ imports: [
761
+ NgTemplateOutlet,
762
+ VdButton,
763
+ VdIcon,
764
+ VD_MENU
765
+ ],
766
+ template: `
767
+ <p class="vd-visually-hidden" [id]="instructionsId">{{ t().instructions }}</p>
768
+ @if (!valid()) {
769
+ <p class="vd-kanban__invalid">{{ t().invalidText }}</p>
770
+ } @else {
771
+ <div
772
+ class="vd-kanban__columns"
773
+ role="group"
774
+ [attr.aria-label]="t().label"
775
+ [attr.aria-describedby]="instructionsId"
776
+ >
777
+ @for (column of columns(); track column.id; let c = $index) {
778
+ <section
779
+ class="vd-kanban__column"
780
+ [attr.aria-labelledby]="baseId + '-title-' + c"
781
+ [attr.data-column-id]="column.id"
782
+ [attr.data-drop-target]="drop()?.columnId === column.id ? '' : null"
783
+ >
784
+ <header class="vd-kanban__header">
785
+ <p class="vd-kanban__title" [id]="baseId + '-title-' + c">
786
+ <bdi>{{ column.title }}</bdi>
787
+ <span class="vd-kanban__count" aria-hidden="true">{{ column.cards.length }}</span>
788
+ <span class="vd-visually-hidden">, {{ t().countText(column.cards.length) }}</span>
789
+ </p>
790
+ @if (columnHeader(); as header) {
791
+ <div class="vd-kanban__header-extra">
792
+ <ng-container
793
+ [ngTemplateOutlet]="header.template"
794
+ [ngTemplateOutletContext]="{ $implicit: column }"
795
+ />
796
+ </div>
797
+ }
798
+ </header>
799
+ <ul
800
+ class="vd-kanban__cards"
801
+ role="list"
802
+ [attr.aria-labelledby]="baseId + '-title-' + c"
803
+ >
804
+ @for (card of column.cards; track card.id; let i = $index, last = $last) {
805
+ <li
806
+ class="vd-kanban__card"
807
+ tabindex="-1"
808
+ [attr.data-card-id]="card.id"
809
+ [attr.data-dragging]="draggingId() === card.id ? '' : null"
810
+ [attr.data-drop]="drop()?.cardId === card.id ? drop()?.edge : null"
811
+ (pointerdown)="start($event, card)"
812
+ (pointermove)="pointerMove($event)"
813
+ (pointerup)="end($event)"
814
+ (pointercancel)="pointerCancel($event)"
815
+ (lostpointercapture)="pointerCancel($event)"
816
+ >
817
+ <div class="vd-kanban__content">
818
+ @if (cardTemplate(); as tpl) {
819
+ <ng-container
820
+ [ngTemplateOutlet]="tpl.template"
821
+ [ngTemplateOutletContext]="{ $implicit: card, column, index: i }"
822
+ />
823
+ } @else {
824
+ <bdi>{{ card.label }}</bdi>
825
+ }
826
+ </div>
827
+ <button
828
+ vdButton
829
+ class="vd-kanban__move"
830
+ variant="ghost"
831
+ size="sm"
832
+ iconOnly
833
+ type="button"
834
+ [disabled]="disabled()"
835
+ [attr.aria-label]="t().moveLabel(card.label)"
836
+ [vdMenuTrigger]="menu"
837
+ >
838
+ <vd-icon name="more-horizontal" />
839
+ </button>
840
+ <div vdMenu #menu="vdMenu">
841
+ <button
842
+ vdMenuItem
843
+ [disabled]="i === 0"
844
+ (click)="moveTo(card.id, column.id, i - 1)"
845
+ >
846
+ <vd-icon name="arrow-up" />{{ t().upText }}
847
+ </button>
848
+ <button
849
+ vdMenuItem
850
+ [disabled]="last"
851
+ (click)="moveTo(card.id, column.id, i + 1)"
852
+ >
853
+ <vd-icon name="arrow-down" />{{ t().downText }}
854
+ </button>
855
+ @if (columns().length > 1) {
856
+ <div vdMenuSeparator></div>
857
+ <p vdMenuLabel>{{ t().moveToText }}</p>
858
+ @for (target of columns(); track target.id) {
859
+ @if (target.id !== column.id) {
860
+ <button
861
+ vdMenuItem
862
+ (click)="moveTo(card.id, target.id, target.cards.length)"
863
+ >
864
+ <bdi>{{ target.title }}</bdi>
865
+ </button>
866
+ }
867
+ }
868
+ }
869
+ </div>
870
+ </li>
871
+ } @empty {
872
+ <li class="vd-kanban__empty">{{ t().emptyText }}</li>
873
+ }
874
+ </ul>
875
+ </section>
876
+ }
877
+ </div>
878
+ }
879
+ <p class="vd-visually-hidden" role="status" aria-atomic="true">
880
+ @for (message of messages(); track message.serial) {
881
+ <span>{{ message.text }}</span>
882
+ }
883
+ </p>
884
+ `,
885
+ host: {
886
+ class: "vd-kanban",
887
+ "[attr.data-state]": "!valid() ? \"invalid\" : disabled() ? \"disabled\" : draggingId() ? \"dragging\" : \"idle\"",
888
+ "(keydown)": "key($event)"
889
+ },
890
+ changeDetection: ChangeDetectionStrategy.OnPush,
891
+ encapsulation: ViewEncapsulation.None
892
+ }]
893
+ }],
894
+ ctorParameters: () => [],
895
+ propDecorators: {
896
+ columns: [{
897
+ type: i0.Input,
898
+ args: [{
899
+ isSignal: true,
900
+ alias: "columns",
901
+ required: false
902
+ }]
903
+ }],
904
+ disabled: [{
905
+ type: i0.Input,
906
+ args: [{
907
+ isSignal: true,
908
+ alias: "disabled",
909
+ required: false
910
+ }]
911
+ }],
912
+ label: [{
913
+ type: i0.Input,
914
+ args: [{
915
+ isSignal: true,
916
+ alias: "label",
917
+ required: false
918
+ }]
919
+ }],
920
+ instructions: [{
921
+ type: i0.Input,
922
+ args: [{
923
+ isSignal: true,
924
+ alias: "instructions",
925
+ required: false
926
+ }]
927
+ }],
928
+ emptyText: [{
929
+ type: i0.Input,
930
+ args: [{
931
+ isSignal: true,
932
+ alias: "emptyText",
933
+ required: false
934
+ }]
935
+ }],
936
+ invalidText: [{
937
+ type: i0.Input,
938
+ args: [{
939
+ isSignal: true,
940
+ alias: "invalidText",
941
+ required: false
942
+ }]
943
+ }],
944
+ countText: [{
945
+ type: i0.Input,
946
+ args: [{
947
+ isSignal: true,
948
+ alias: "countText",
949
+ required: false
950
+ }]
951
+ }],
952
+ moveLabel: [{
953
+ type: i0.Input,
954
+ args: [{
955
+ isSignal: true,
956
+ alias: "moveLabel",
957
+ required: false
958
+ }]
959
+ }],
960
+ upText: [{
961
+ type: i0.Input,
962
+ args: [{
963
+ isSignal: true,
964
+ alias: "upText",
965
+ required: false
966
+ }]
967
+ }],
968
+ downText: [{
969
+ type: i0.Input,
970
+ args: [{
971
+ isSignal: true,
972
+ alias: "downText",
973
+ required: false
974
+ }]
975
+ }],
976
+ moveToText: [{
977
+ type: i0.Input,
978
+ args: [{
979
+ isSignal: true,
980
+ alias: "moveToText",
981
+ required: false
982
+ }]
983
+ }],
984
+ movedText: [{
985
+ type: i0.Input,
986
+ args: [{
987
+ isSignal: true,
988
+ alias: "movedText",
989
+ required: false
990
+ }]
991
+ }],
992
+ canceledText: [{
993
+ type: i0.Input,
994
+ args: [{
995
+ isSignal: true,
996
+ alias: "canceledText",
997
+ required: false
998
+ }]
999
+ }],
1000
+ cardMove: [{
1001
+ type: i0.Output,
1002
+ args: ["cardMove"]
1003
+ }],
1004
+ cardTemplate: [{
1005
+ type: i0.ContentChild,
1006
+ args: [i0.forwardRef(() => VdKanbanCard), { isSignal: true }]
1007
+ }],
1008
+ columnHeader: [{
1009
+ type: i0.ContentChild,
1010
+ args: [i0.forwardRef(() => VdKanbanColumnHeader), { isSignal: true }]
1011
+ }]
1012
+ }
1013
+ });
1014
+ const VD_KANBAN = [
1015
+ VdKanban,
1016
+ VdKanbanCard,
1017
+ VdKanbanColumnHeader
1018
+ ];
1019
+ export { VD_KANBAN, VdKanban, VdKanbanCard, VdKanbanColumnHeader, vdKanbanApplyMove };
1020
+
1021
+ //# sourceMappingURL=vadem-ui-kanban.mjs.map