UNPKG

ng-hub-ui-board

Version:

An Angular-based Kanban board component with Trello-like drag-and-drop, customizable columns, and straightforward event handling.

230 lines (220 loc) 20.7 kB
import * as i0 from '@angular/core'; import { Directive, EventEmitter, TemplateRef, Component, Input, ContentChild, Output, NgModule, Pipe } from '@angular/core'; import * as i1 from '@angular/cdk/drag-drop'; import { moveItemInArray, transferArrayItem, DragDropModule } from '@angular/cdk/drag-drop'; import { NgClass, NgStyle, NgTemplateOutlet } from '@angular/common'; class BoardColumnFooterDirective { constructor(templateRef) { this.templateRef = templateRef; } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.0.4", ngImport: i0, type: BoardColumnFooterDirective, deps: [{ token: i0.TemplateRef }], target: i0.ɵɵFactoryTarget.Directive }); } static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "19.0.4", type: BoardColumnFooterDirective, isStandalone: true, selector: "[columnFooterTpt]", ngImport: i0 }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.0.4", ngImport: i0, type: BoardColumnFooterDirective, decorators: [{ type: Directive, args: [{ selector: '[columnFooterTpt]', standalone: true }] }], ctorParameters: () => [{ type: i0.TemplateRef }] }); class BoardColumnHeaderDirective { constructor(templateRef) { this.templateRef = templateRef; } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.0.4", ngImport: i0, type: BoardColumnHeaderDirective, deps: [{ token: i0.TemplateRef }], target: i0.ɵɵFactoryTarget.Directive }); } static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "19.0.4", type: BoardColumnHeaderDirective, isStandalone: true, selector: "[columnHeaderTpt]", ngImport: i0 }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.0.4", ngImport: i0, type: BoardColumnHeaderDirective, decorators: [{ type: Directive, args: [{ selector: '[columnHeaderTpt]', standalone: true }] }], ctorParameters: () => [{ type: i0.TemplateRef }] }); class CardTemplateDirective { constructor(templateRef) { this.templateRef = templateRef; } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.0.4", ngImport: i0, type: CardTemplateDirective, deps: [{ token: i0.TemplateRef }], target: i0.ɵɵFactoryTarget.Directive }); } static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "19.0.4", type: CardTemplateDirective, isStandalone: true, selector: "[cardTpt]", ngImport: i0 }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.0.4", ngImport: i0, type: CardTemplateDirective, decorators: [{ type: Directive, args: [{ selector: '[cardTpt]', standalone: true }] }], ctorParameters: () => [{ type: i0.TemplateRef }] }); class HubBoardComponent { constructor() { // Used to disable the sorting of columns in the board this.columnSortingDisabled = false; // triggered when a card is clicked this.onCardClick = new EventEmitter(); // triggered when a card is moved this.onCardMoved = new EventEmitter(); // triggered when a column is moved this.onColumnMoved = new EventEmitter(); // emit an event when the user has scrolled to the end of a specific column in the board. this.reachedEnd = new EventEmitter(); /* Defines a default enter predicate function for the drag and drop operation. */ this.defaultEnterPredicateFn = () => true; } /** * Emits an event with the clicked item as a parameter. * * @param {any} item - Represents the item that was clicked on the card. */ cardClick(item) { this.onCardClick.next(item); } /** * Used to handle the event when a column is dropped in a drag and drop operation, and it moves the column in the array and * emits an event. * * @param event - represents the drag and drop event that occurred. */ dropColumn(event) { moveItemInArray(event.container.data, event.previousIndex, event.currentIndex); this.onColumnMoved.emit(event); } /** * Used to handle the dropping of a card in a drag and drop operation, either moving the card within the same container or * transferring it to a different container. * * @param event - a generic type that takes three arguments: */ dropCard(event) { if (event.previousContainer === event.container) { moveItemInArray(event.container.data.cards, event.previousIndex, event.currentIndex); } else { transferArrayItem(event.previousContainer.data.cards, event.container.data.cards, event.previousIndex, event.currentIndex); } this.onCardMoved.emit(event); } /** * Checks if the user has scrolled to the end of a specific element and emits an event if so. * * @param {number} index - The index parameter is a number that represents the index of the column being scrolled. * @param {Event} event - The event parameter is an object that represents the scroll event. It contains information about the * scroll event, such as the source element that triggered the event, the amount of scrolling that has occurred, and the dimensions * of the scrollable area. */ onScroll(index, event) { const el = event.target; if (el && el.scrollTop + el.clientHeight >= el.scrollHeight) { this.reachedEnd.emit({ index, data: this.board.columns?.[index] ?? [] }); } } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.0.4", ngImport: i0, type: HubBoardComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); } static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.0.4", type: HubBoardComponent, isStandalone: true, selector: "hub-board, hub-ui-board", inputs: { board: "board", columnSortingDisabled: "columnSortingDisabled" }, outputs: { onCardClick: "onCardClick", onCardMoved: "onCardMoved", onColumnMoved: "onColumnMoved", reachedEnd: "reachedEnd" }, host: { classAttribute: "d-block" }, queries: [{ propertyName: "cardTpt", first: true, predicate: CardTemplateDirective, descendants: true, read: (TemplateRef) }, { propertyName: "columnHeaderTpt", first: true, predicate: BoardColumnHeaderDirective, descendants: true, read: (TemplateRef) }, { propertyName: "columnFooterTpt", first: true, predicate: BoardColumnFooterDirective, descendants: true, read: (TemplateRef) }], ngImport: i0, template: "@if (board) {\n<div\n\tclass=\"hub-board h-100\"\n\tcdkDropList\n\tcdkDropListOrientation=\"horizontal\"\n\t[cdkDropListData]=\"board.columns\"\n\t(cdkDropListDropped)=\"dropColumn($event)\"\n\t[cdkDropListSortingDisabled]=\"columnSortingDisabled\"\n>\n\t<div\n\t\tcdkDropListGroup\n\t\tclass=\"hub-board__columns columns-container d-flex flex-nowrap w-100 h-100 gap-3\"\n\t>\n\t\t@for (column of board.columns; let index = $index; track column) {\n\t\t<div\n\t\t\tclass=\"hub-board__column-container h-100\"\n\t\t\tcdkDrag\n\t\t\t[cdkDragData]=\"column\"\n\t\t\t[cdkDragDisabled]=\"column.disabled\"\n\t\t>\n\t\t\t<div\n\t\t\t\tclass=\"hub-board__column card d-flex flex-column mh-100\"\n\t\t\t\tstyle=\"width: 256px\"\n\t\t\t\t[ngClass]=\"column.classlist\"\n\t\t\t\t[ngStyle]=\"column.style\"\n\t\t\t>\n\t\t\t\t<div\n\t\t\t\t\tclass=\"hub-board__column-header card-header bg-transparent border-bottom-0 d-flex align-items-center\"\n\t\t\t\t>\n\t\t\t\t\t<ng-container\n\t\t\t\t\t\t[ngTemplateOutlet]=\"\n\t\t\t\t\t\t\tcolumnHeaderTpt || defaultColumnHeaderTpt\n\t\t\t\t\t\t\"\n\t\t\t\t\t\t[ngTemplateOutletContext]=\"{ column: column }\"\n\t\t\t\t\t>\n\t\t\t\t\t</ng-container>\n\t\t\t\t</div>\n\t\t\t\t<div\n\t\t\t\t\tclass=\"hub-board__column-body card-body cards overflow-auto d-flex flex-column gap-3\"\n\t\t\t\t\tcdkDropList\n\t\t\t\t\t[cdkDropListData]=\"column\"\n\t\t\t\t\t(cdkDropListDropped)=\"dropCard($event)\"\n\t\t\t\t\t(scroll)=\"onScroll(index, $event)\"\n\t\t\t\t\t[cdkDropListEnterPredicate]=\"\n\t\t\t\t\t\tcolumn.predicate ?? defaultEnterPredicateFn\n\t\t\t\t\t\"\n\t\t\t\t\t[cdkDropListSortingDisabled]=\"column.cardSortingDisabled\"\n\t\t\t\t>\n\t\t\t\t\t@for (card of column.cards; let index = $index; track card)\n\t\t\t\t\t{\n\n\t\t\t\t\t<div\n\t\t\t\t\t\tclass=\"hub-board__card card\"\n\t\t\t\t\t\tcdkDrag\n\t\t\t\t\t\t[cdkDragData]=\"card\"\n\t\t\t\t\t\t[cdkDragDisabled]=\"card.disabled\"\n\t\t\t\t\t\t(click)=\"cardClick(card)\"\n\t\t\t\t\t\t[ngClass]=\"card.classlist\"\n\t\t\t\t\t\t[ngStyle]=\"card.style\"\n\t\t\t\t\t>\n\t\t\t\t\t\t<div class=\"card-body\">\n\t\t\t\t\t\t\t<ng-container\n\t\t\t\t\t\t\t\t[ngTemplateOutlet]=\"cardTpt || defaultCardTpt\"\n\t\t\t\t\t\t\t\t[ngTemplateOutletContext]=\"{\n\t\t\t\t\t\t\t\t\titem: card,\n\t\t\t\t\t\t\t\t\tcolumn\n\t\t\t\t\t\t\t\t}\"\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t</ng-container>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</div>\n\t\t\t\t\t}\n\t\t\t\t</div>\n\n\t\t\t\t@if (columnFooterTpt) {\n\n\t\t\t\t<div\n\t\t\t\t\tclass=\"hub-board__column-footer card-footer bg-transparent border-top-0 d-flex justify-content-end\"\n\t\t\t\t>\n\t\t\t\t\t<ng-container\n\t\t\t\t\t\t[ngTemplateOutlet]=\"columnFooterTpt\"\n\t\t\t\t\t\t[ngTemplateOutletContext]=\"{ column: column }\"\n\t\t\t\t\t>\n\t\t\t\t\t</ng-container>\n\t\t\t\t</div>\n\t\t\t\t}\n\t\t\t</div>\n\t\t</div>\n\t\t}\n\t</div>\n</div>\n}\n\n<ng-template #defaultCardTpt let-item=\"item\">\n\t<h6>{{ item.title }}</h6>\n\t<p class=\"card-text\">{{ item.description }}</p>\n</ng-template>\n\n<ng-template #defaultColumnHeaderTpt let-column=\"column\">\n\t<div class=\"d-flex flex-column\">\n\t\t<h5 class=\"hub-board__column-header-title card-title mb-0\">\n\t\t\t{{ column.title }}\n\t\t</h5>\n\t\t<p class=\"hub-board__column-header-description card-text\">\n\t\t\t{{ column.description }}\n\t\t</p>\n\t</div>\n</ng-template>\n", styles: [":host{display:block;overflow:auto;width:100%;height:100%}.cards{min-height:128px}.cdk-drag-preview{box-sizing:border-box;border-radius:4px;box-shadow:0 5px 5px -3px #0003,0 8px 10px 1px #00000024,0 3px 14px 2px #0000001f}.cdk-drag-placeholder{opacity:0}.cdk-drag-animating{transition:transform .25s cubic-bezier(0,0,.2,1)}.cards.cdk-drop-list-dragging .card:not(.cdk-drag-placeholder){transition:transform .25s cubic-bezier(0,0,.2,1)}\n"], dependencies: [{ kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "ngmodule", type: DragDropModule }, { kind: "directive", type: i1.CdkDropList, selector: "[cdkDropList], cdk-drop-list", inputs: ["cdkDropListConnectedTo", "cdkDropListData", "cdkDropListOrientation", "id", "cdkDropListLockAxis", "cdkDropListDisabled", "cdkDropListSortingDisabled", "cdkDropListEnterPredicate", "cdkDropListSortPredicate", "cdkDropListAutoScrollDisabled", "cdkDropListAutoScrollStep", "cdkDropListElementContainer"], outputs: ["cdkDropListDropped", "cdkDropListEntered", "cdkDropListExited", "cdkDropListSorted"], exportAs: ["cdkDropList"] }, { kind: "directive", type: i1.CdkDropListGroup, selector: "[cdkDropListGroup]", inputs: ["cdkDropListGroupDisabled"], exportAs: ["cdkDropListGroup"] }, { kind: "directive", type: i1.CdkDrag, selector: "[cdkDrag]", inputs: ["cdkDragData", "cdkDragLockAxis", "cdkDragRootElement", "cdkDragBoundary", "cdkDragStartDelay", "cdkDragFreeDragPosition", "cdkDragDisabled", "cdkDragConstrainPosition", "cdkDragPreviewClass", "cdkDragPreviewContainer", "cdkDragScale"], outputs: ["cdkDragStarted", "cdkDragReleased", "cdkDragEnded", "cdkDragEntered", "cdkDragExited", "cdkDragDropped", "cdkDragMoved"], exportAs: ["cdkDrag"] }] }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.0.4", ngImport: i0, type: HubBoardComponent, decorators: [{ type: Component, args: [{ selector: 'hub-board, hub-ui-board', standalone: true, imports: [NgClass, NgStyle, NgTemplateOutlet, DragDropModule], host: { class: 'd-block' }, template: "@if (board) {\n<div\n\tclass=\"hub-board h-100\"\n\tcdkDropList\n\tcdkDropListOrientation=\"horizontal\"\n\t[cdkDropListData]=\"board.columns\"\n\t(cdkDropListDropped)=\"dropColumn($event)\"\n\t[cdkDropListSortingDisabled]=\"columnSortingDisabled\"\n>\n\t<div\n\t\tcdkDropListGroup\n\t\tclass=\"hub-board__columns columns-container d-flex flex-nowrap w-100 h-100 gap-3\"\n\t>\n\t\t@for (column of board.columns; let index = $index; track column) {\n\t\t<div\n\t\t\tclass=\"hub-board__column-container h-100\"\n\t\t\tcdkDrag\n\t\t\t[cdkDragData]=\"column\"\n\t\t\t[cdkDragDisabled]=\"column.disabled\"\n\t\t>\n\t\t\t<div\n\t\t\t\tclass=\"hub-board__column card d-flex flex-column mh-100\"\n\t\t\t\tstyle=\"width: 256px\"\n\t\t\t\t[ngClass]=\"column.classlist\"\n\t\t\t\t[ngStyle]=\"column.style\"\n\t\t\t>\n\t\t\t\t<div\n\t\t\t\t\tclass=\"hub-board__column-header card-header bg-transparent border-bottom-0 d-flex align-items-center\"\n\t\t\t\t>\n\t\t\t\t\t<ng-container\n\t\t\t\t\t\t[ngTemplateOutlet]=\"\n\t\t\t\t\t\t\tcolumnHeaderTpt || defaultColumnHeaderTpt\n\t\t\t\t\t\t\"\n\t\t\t\t\t\t[ngTemplateOutletContext]=\"{ column: column }\"\n\t\t\t\t\t>\n\t\t\t\t\t</ng-container>\n\t\t\t\t</div>\n\t\t\t\t<div\n\t\t\t\t\tclass=\"hub-board__column-body card-body cards overflow-auto d-flex flex-column gap-3\"\n\t\t\t\t\tcdkDropList\n\t\t\t\t\t[cdkDropListData]=\"column\"\n\t\t\t\t\t(cdkDropListDropped)=\"dropCard($event)\"\n\t\t\t\t\t(scroll)=\"onScroll(index, $event)\"\n\t\t\t\t\t[cdkDropListEnterPredicate]=\"\n\t\t\t\t\t\tcolumn.predicate ?? defaultEnterPredicateFn\n\t\t\t\t\t\"\n\t\t\t\t\t[cdkDropListSortingDisabled]=\"column.cardSortingDisabled\"\n\t\t\t\t>\n\t\t\t\t\t@for (card of column.cards; let index = $index; track card)\n\t\t\t\t\t{\n\n\t\t\t\t\t<div\n\t\t\t\t\t\tclass=\"hub-board__card card\"\n\t\t\t\t\t\tcdkDrag\n\t\t\t\t\t\t[cdkDragData]=\"card\"\n\t\t\t\t\t\t[cdkDragDisabled]=\"card.disabled\"\n\t\t\t\t\t\t(click)=\"cardClick(card)\"\n\t\t\t\t\t\t[ngClass]=\"card.classlist\"\n\t\t\t\t\t\t[ngStyle]=\"card.style\"\n\t\t\t\t\t>\n\t\t\t\t\t\t<div class=\"card-body\">\n\t\t\t\t\t\t\t<ng-container\n\t\t\t\t\t\t\t\t[ngTemplateOutlet]=\"cardTpt || defaultCardTpt\"\n\t\t\t\t\t\t\t\t[ngTemplateOutletContext]=\"{\n\t\t\t\t\t\t\t\t\titem: card,\n\t\t\t\t\t\t\t\t\tcolumn\n\t\t\t\t\t\t\t\t}\"\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t</ng-container>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</div>\n\t\t\t\t\t}\n\t\t\t\t</div>\n\n\t\t\t\t@if (columnFooterTpt) {\n\n\t\t\t\t<div\n\t\t\t\t\tclass=\"hub-board__column-footer card-footer bg-transparent border-top-0 d-flex justify-content-end\"\n\t\t\t\t>\n\t\t\t\t\t<ng-container\n\t\t\t\t\t\t[ngTemplateOutlet]=\"columnFooterTpt\"\n\t\t\t\t\t\t[ngTemplateOutletContext]=\"{ column: column }\"\n\t\t\t\t\t>\n\t\t\t\t\t</ng-container>\n\t\t\t\t</div>\n\t\t\t\t}\n\t\t\t</div>\n\t\t</div>\n\t\t}\n\t</div>\n</div>\n}\n\n<ng-template #defaultCardTpt let-item=\"item\">\n\t<h6>{{ item.title }}</h6>\n\t<p class=\"card-text\">{{ item.description }}</p>\n</ng-template>\n\n<ng-template #defaultColumnHeaderTpt let-column=\"column\">\n\t<div class=\"d-flex flex-column\">\n\t\t<h5 class=\"hub-board__column-header-title card-title mb-0\">\n\t\t\t{{ column.title }}\n\t\t</h5>\n\t\t<p class=\"hub-board__column-header-description card-text\">\n\t\t\t{{ column.description }}\n\t\t</p>\n\t</div>\n</ng-template>\n", styles: [":host{display:block;overflow:auto;width:100%;height:100%}.cards{min-height:128px}.cdk-drag-preview{box-sizing:border-box;border-radius:4px;box-shadow:0 5px 5px -3px #0003,0 8px 10px 1px #00000024,0 3px 14px 2px #0000001f}.cdk-drag-placeholder{opacity:0}.cdk-drag-animating{transition:transform .25s cubic-bezier(0,0,.2,1)}.cards.cdk-drop-list-dragging .card:not(.cdk-drag-placeholder){transition:transform .25s cubic-bezier(0,0,.2,1)}\n"] }] }], propDecorators: { board: [{ type: Input, args: [{ required: true }] }], columnSortingDisabled: [{ type: Input }], cardTpt: [{ type: ContentChild, args: [CardTemplateDirective, { read: (TemplateRef) }] }], columnHeaderTpt: [{ type: ContentChild, args: [BoardColumnHeaderDirective, { read: (TemplateRef) }] }], columnFooterTpt: [{ type: ContentChild, args: [BoardColumnFooterDirective, { read: (TemplateRef) }] }], onCardClick: [{ type: Output }], onCardMoved: [{ type: Output }], onColumnMoved: [{ type: Output }], reachedEnd: [{ type: Output }] } }); class BoardModule { static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.0.4", ngImport: i0, type: BoardModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); } static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "19.0.4", ngImport: i0, type: BoardModule, imports: [HubBoardComponent, CardTemplateDirective, BoardColumnHeaderDirective, BoardColumnFooterDirective], exports: [HubBoardComponent, CardTemplateDirective, BoardColumnHeaderDirective, BoardColumnFooterDirective] }); } static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "19.0.4", ngImport: i0, type: BoardModule, imports: [HubBoardComponent] }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.0.4", ngImport: i0, type: BoardModule, decorators: [{ type: NgModule, args: [{ declarations: [], imports: [ HubBoardComponent, CardTemplateDirective, BoardColumnHeaderDirective, BoardColumnFooterDirective ], exports: [ HubBoardComponent, CardTemplateDirective, BoardColumnHeaderDirective, BoardColumnFooterDirective ] }] }] }); class InvertColorPipe { transform(hex, bw) { if (!hex) { return '#000000'; } if (hex.indexOf('#') === 0) { hex = hex.slice(1); } // convert 3-digit hex to 6-digits. if (hex.length === 3) { hex = hex[0] + hex[0] + hex[1] + hex[1] + hex[2] + hex[2]; } if (hex.length !== 6) { throw new Error('Invalid HEX color.'); } let r = parseInt(hex.slice(0, 2), 16); let g = parseInt(hex.slice(2, 4), 16); let b = parseInt(hex.slice(4, 6), 16); if (bw) { // http://stackoverflow.com/a/3943023/112731 return r * 0.299 + g * 0.587 + b * 0.114 > 186 ? '#000000' : '#FFFFFF'; } // invert color components r = (255 - r).toString(16); g = (255 - g).toString(16); b = (255 - b).toString(16); // pad each with zeros and return return '#' + r.padStart(2, 0) + g.padStart(2, 0) + b.padStart(2, 0); } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.0.4", ngImport: i0, type: InvertColorPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe }); } static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "19.0.4", ngImport: i0, type: InvertColorPipe, isStandalone: true, name: "invertColor" }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.0.4", ngImport: i0, type: InvertColorPipe, decorators: [{ type: Pipe, args: [{ name: 'invertColor', standalone: true }] }] }); /* * Public API Surface of board */ // module /** * Generated bundle index. Do not edit. */ export { BoardColumnFooterDirective, BoardColumnHeaderDirective, BoardModule, CardTemplateDirective, HubBoardComponent, InvertColorPipe }; //# sourceMappingURL=ng-hub-ui-board.mjs.map