UNPKG

ng-hub-ui-board

Version:

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

392 lines (382 loc) 26.9 kB
import * as i0 from '@angular/core'; import { Directive, input, computed, contentChild, TemplateRef, output, Component, 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'; /** * Directive that allows customization of column footer templates within board columns. * * This directive enables developers to define custom templates for column footers, * perfect for displaying summary information, quick actions, statistics, * or any column-specific controls at the bottom of each column. * * @publicApi * * @example * ```html * <ng-template columnFooterTpt let-column="column"> * <div class="custom-footer"> * <div class="column-summary"> * <span>Total: {{ column.cards.length }}</span> * <span>Priority Items: {{ getPriorityItems(column) }}</span> * </div> * <button (click)="quickAddCard(column)">Quick Add</button> * </div> * </ng-template> * ``` */ class BoardColumnFooterDirective { templateRef; /** * Creates a new BoardColumnFooterDirective instance. * * @param templateRef - The template reference that contains the custom column footer layout */ constructor(templateRef) { this.templateRef = templateRef; } static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.2.3", ngImport: i0, type: BoardColumnFooterDirective, deps: [{ token: i0.TemplateRef }], target: i0.ɵɵFactoryTarget.Directive }); static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.2.3", type: BoardColumnFooterDirective, isStandalone: true, selector: "[columnFooterTpt]", ngImport: i0 }); } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.2.3", ngImport: i0, type: BoardColumnFooterDirective, decorators: [{ type: Directive, args: [{ selector: '[columnFooterTpt]', standalone: true }] }], ctorParameters: () => [{ type: i0.TemplateRef }] }); /** * Directive that allows customization of column header templates within board columns. * * This directive provides the ability to define custom templates for rendering column headers, * giving developers full control over the appearance and functionality of column headers * including titles, descriptions, actions, and metadata display. * * @publicApi * * @example * ```html * <ng-template columnHeaderTpt let-column="column"> * <div class="custom-header"> * <h2>{{ column.title }}</h2> * <span class="card-count">{{ column.cards.length }} items</span> * <button (click)="addCard(column)">Add Card</button> * </div> * </ng-template> * ``` */ class BoardColumnHeaderDirective { templateRef; /** * Creates a new BoardColumnHeaderDirective instance. * * @param templateRef - The template reference that contains the custom column header layout */ constructor(templateRef) { this.templateRef = templateRef; } static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.2.3", ngImport: i0, type: BoardColumnHeaderDirective, deps: [{ token: i0.TemplateRef }], target: i0.ɵɵFactoryTarget.Directive }); static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.2.3", type: BoardColumnHeaderDirective, isStandalone: true, selector: "[columnHeaderTpt]", ngImport: i0 }); } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.2.3", ngImport: i0, type: BoardColumnHeaderDirective, decorators: [{ type: Directive, args: [{ selector: '[columnHeaderTpt]', standalone: true }] }], ctorParameters: () => [{ type: i0.TemplateRef }] }); /** * Directive that allows customization of card templates within board columns. * * This directive is used to define custom templates for rendering board cards. * It provides access to the template reference that can be used by the board component * to render cards with custom layouts and styling. * * @publicApi * * @example * ```html * <ng-template cardTpt let-card="item" let-column="column"> * <div class="custom-card"> * <h3>{{ card.title }}</h3> * <p>{{ card.description }}</p> * </div> * </ng-template> * ``` */ class CardTemplateDirective { templateRef; /** * Creates a new CardTemplateDirective instance. * * @param templateRef - The template reference that contains the custom card layout */ constructor(templateRef) { this.templateRef = templateRef; } static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.2.3", ngImport: i0, type: CardTemplateDirective, deps: [{ token: i0.TemplateRef }], target: i0.ɵɵFactoryTarget.Directive }); static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.2.3", type: CardTemplateDirective, isStandalone: true, selector: "[cardTpt]", ngImport: i0 }); } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.2.3", ngImport: i0, type: CardTemplateDirective, decorators: [{ type: Directive, args: [{ selector: '[cardTpt]', standalone: true }] }], ctorParameters: () => [{ type: i0.TemplateRef }] }); /** * Standalone Kanban-style board component that provides column-based drag-and-drop, * custom templates and infinite-scroll detection. * * @publicApi */ class HubBoardComponent { /** * Reactive input containing the full board definition (columns and cards). */ board = input(...(ngDevMode ? [undefined, { debugName: "board" }] : [])); /** * Pixel threshold used when determining whether a column has reached scroll end. * Allows for fractional scroll values across different browsers. */ scrollDetectionPadding = 1; /** * Derived list of board columns exposed as a signal to the template. */ columns = computed(() => { return this.board()?.columns ?? []; }, ...(ngDevMode ? [{ debugName: "columns" }] : [])); /** * When true, column reordering via drag-and-drop is disabled. */ columnSortingDisabled = input(false, ...(ngDevMode ? [{ debugName: "columnSortingDisabled" }] : [])); /** * Custom card template supplied via the `cardTpt` structural directive. */ cardTpt = contentChild(CardTemplateDirective, ...(ngDevMode ? [{ debugName: "cardTpt", read: (TemplateRef) }] : [{ read: (TemplateRef) }])); /** * Custom column header template supplied via the `columnHeaderTpt` structural directive. */ columnHeaderTpt = contentChild(BoardColumnHeaderDirective, ...(ngDevMode ? [{ debugName: "columnHeaderTpt", read: (TemplateRef) }] : [{ read: (TemplateRef) }])); /** * Custom column footer template supplied via the `columnFooterTpt` structural directive. */ columnFooterTpt = contentChild(BoardColumnFooterDirective, ...(ngDevMode ? [{ debugName: "columnFooterTpt", read: (TemplateRef) }] : [{ read: (TemplateRef) }])); /** * Emits each time a card is clicked within the board. */ onCardClick = output(); /** * Emits when a card has been repositioned, either within the same column or into another column. */ onCardMoved = output(); /** * Emits when columns are reordered through drag-and-drop. */ onColumnMoved = output(); /** * Emits when a column body is scrolled to its end, enabling infinite-scroll behaviour. */ reachedEnd = output(); /** * Default predicate that allows any card to be dropped into any column. * * @returns Always `true`, indicating that drop operations are permitted. */ defaultEnterPredicateFn = () => true; /** * Emits the clicked card through {@link onCardClick}. * * @param item - The card that triggered the click event. */ cardClick(item) { this.onCardClick.emit(item); } /** * Updates column order when a drag-and-drop operation completes and emits the resulting event. * * @param event - Drag-and-drop metadata describing the column movement. */ dropColumn(event) { moveItemInArray(event.container.data, event.previousIndex, event.currentIndex); this.onColumnMoved.emit(event); } /** * Applies card reordering or transfer logic depending on the drag-drop target, * then emits the corresponding drag event metadata. * * @param event - Drag-and-drop metadata describing the card movement. */ dropCard(event) { if (event.previousContainer === event.container) { // Reorder the card within the same column moveItemInArray(event.container.data.cards, event.previousIndex, event.currentIndex); } else { // Transfer the card from one column to another transferArrayItem(event.previousContainer.data.cards, event.container.data.cards, event.previousIndex, event.currentIndex); } this.onCardMoved.emit(event); } /** * Emits {@link reachedEnd} once a column body is scrolled to its bottom. * * @param index - Index of the scrolled column within the board. * @param event - Browser scroll event originating from the column body element. */ onScroll(index, event) { const el = event.target; if (!el) { return; } const scrolledToBottom = el.scrollTop + el.clientHeight >= el.scrollHeight - this.scrollDetectionPadding; if (!scrolledToBottom) { return; } const column = this.board()?.columns?.[index]; if (!column) { return; } this.reachedEnd.emit({ index, data: column }); } static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.2.3", ngImport: i0, type: HubBoardComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.2.3", type: HubBoardComponent, isStandalone: true, selector: "hub-board, hub-ui-board", inputs: { board: { classPropertyName: "board", publicName: "board", isSignal: true, isRequired: false, transformFunction: null }, columnSortingDisabled: { classPropertyName: "columnSortingDisabled", publicName: "columnSortingDisabled", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { onCardClick: "onCardClick", onCardMoved: "onCardMoved", onColumnMoved: "onColumnMoved", reachedEnd: "reachedEnd" }, queries: [{ propertyName: "cardTpt", first: true, predicate: CardTemplateDirective, descendants: true, read: TemplateRef, isSignal: true }, { propertyName: "columnHeaderTpt", first: true, predicate: BoardColumnHeaderDirective, descendants: true, read: TemplateRef, isSignal: true }, { propertyName: "columnFooterTpt", first: true, predicate: BoardColumnFooterDirective, descendants: true, read: TemplateRef, isSignal: true }], ngImport: i0, template: "@if (columns().length) {\n\t<div\n\t\tclass=\"hub-board\"\n\t\tcdkDropList\n\t\tcdkDropListOrientation=\"horizontal\"\n\t\t[cdkDropListData]=\"columns()\"\n\t\t(cdkDropListDropped)=\"dropColumn($event)\"\n\t\t[cdkDropListSortingDisabled]=\"columnSortingDisabled()\"\n\t>\n\t\t<div cdkDropListGroup class=\"hub-board__columns\">\n\t\t\t@for (column of columns(); let index = $index; track column) {\n\t\t\t\t<div\n\t\t\t\t\tclass=\"hub-board__column-container\"\n\t\t\t\t\tcdkDrag\n\t\t\t\t\t[cdkDragData]=\"column\"\n\t\t\t\t\t[cdkDragDisabled]=\"column.disabled\"\n\t\t\t\t>\n\t\t\t\t\t<div\n\t\t\t\t\t\tclass=\"hub-board__column\"\n\t\t\t\t\t\t[ngClass]=\"column.classlist\"\n\t\t\t\t\t\t[ngStyle]=\"column.style\"\n\t\t\t\t\t>\n\t\t\t\t\t\t<div class=\"hub-board__column-header\">\n\t\t\t\t\t\t\t<ng-container\n\t\t\t\t\t\t\t\t[ngTemplateOutlet]=\"\n\t\t\t\t\t\t\t\t\tcolumnHeaderTpt() || defaultColumnHeaderTpt\n\t\t\t\t\t\t\t\t\"\n\t\t\t\t\t\t\t\t[ngTemplateOutletContext]=\"{ column: column }\"\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\t<div\n\t\t\t\t\t\t\tclass=\"hub-board__column-body\"\n\t\t\t\t\t\t\tcdkDropList\n\t\t\t\t\t\t\t[cdkDropListData]=\"column\"\n\t\t\t\t\t\t\t(cdkDropListDropped)=\"dropCard($event)\"\n\t\t\t\t\t\t\t(scroll)=\"onScroll(index, $event)\"\n\t\t\t\t\t\t\t[cdkDropListEnterPredicate]=\"\n\t\t\t\t\t\t\t\tcolumn.predicate ?? defaultEnterPredicateFn\n\t\t\t\t\t\t\t\"\n\t\t\t\t\t\t\t[cdkDropListSortingDisabled]=\"\n\t\t\t\t\t\t\t\tcolumn.cardSortingDisabled\n\t\t\t\t\t\t\t\"\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t@for (\n\t\t\t\t\t\t\t\tcard of column.cards;\n\t\t\t\t\t\t\t\tlet index = $index;\n\t\t\t\t\t\t\t\ttrack card\n\t\t\t\t\t\t\t) {\n\t\t\t\t\t\t\t\t<div\n\t\t\t\t\t\t\t\t\tclass=\"hub-board__card\"\n\t\t\t\t\t\t\t\t\t[class.hub-board__card--disabled]=\"\n\t\t\t\t\t\t\t\t\t\tcard.disabled\n\t\t\t\t\t\t\t\t\t\"\n\t\t\t\t\t\t\t\t\tcdkDrag\n\t\t\t\t\t\t\t\t\t[cdkDragData]=\"card\"\n\t\t\t\t\t\t\t\t\t[cdkDragDisabled]=\"card.disabled\"\n\t\t\t\t\t\t\t\t\t(click)=\"cardClick(card)\"\n\t\t\t\t\t\t\t\t\t(mousedown)=\"\n\t\t\t\t\t\t\t\t\t\tcard.disabled &&\n\t\t\t\t\t\t\t\t\t\t\t$event.stopPropagation()\n\t\t\t\t\t\t\t\t\t\"\n\t\t\t\t\t\t\t\t\t[ngClass]=\"card.classlist\"\n\t\t\t\t\t\t\t\t\t[ngStyle]=\"card.style\"\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t<div class=\"hub-board__card-body\">\n\t\t\t\t\t\t\t\t\t\t<ng-container\n\t\t\t\t\t\t\t\t\t\t\t[ngTemplateOutlet]=\"\n\t\t\t\t\t\t\t\t\t\t\t\tcardTpt() || defaultCardTpt\n\t\t\t\t\t\t\t\t\t\t\t\"\n\t\t\t\t\t\t\t\t\t\t\t[ngTemplateOutletContext]=\"{\n\t\t\t\t\t\t\t\t\t\t\t\titem: card,\n\t\t\t\t\t\t\t\t\t\t\t\tcolumn\n\t\t\t\t\t\t\t\t\t\t\t}\"\n\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t</ng-container>\n\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t</div>\n\n\t\t\t\t\t\t@if (columnFooterTpt()) {\n\t\t\t\t\t\t\t<div class=\"hub-board__column-footer\">\n\t\t\t\t\t\t\t\t<ng-container\n\t\t\t\t\t\t\t\t\t[ngTemplateOutlet]=\"\n\t\t\t\t\t\t\t\t\t\tcolumnFooterTpt() ?? null\n\t\t\t\t\t\t\t\t\t\"\n\t\t\t\t\t\t\t\t\t[ngTemplateOutletContext]=\"{\n\t\t\t\t\t\t\t\t\t\tcolumn: column\n\t\t\t\t\t\t\t\t\t}\"\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t</ng-container>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t}\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t}\n\t\t</div>\n\t</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\">\n\t\t\t{{ column.title }}\n\t\t</h5>\n\t\t<h6 class=\"hub-board__column-header-subtitle\">\n\t\t\t{{ column.description }}\n\t\t</h6>\n\t</div>\n</ng-template>\n", styles: [":host{display:block;overflow:auto;width:100%;height:100%}.hub-board__column-body{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", "cdkDropListHasAnchor"], 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: "20.2.3", ngImport: i0, type: HubBoardComponent, decorators: [{ type: Component, args: [{ selector: 'hub-board, hub-ui-board', standalone: true, imports: [NgClass, NgStyle, NgTemplateOutlet, DragDropModule], template: "@if (columns().length) {\n\t<div\n\t\tclass=\"hub-board\"\n\t\tcdkDropList\n\t\tcdkDropListOrientation=\"horizontal\"\n\t\t[cdkDropListData]=\"columns()\"\n\t\t(cdkDropListDropped)=\"dropColumn($event)\"\n\t\t[cdkDropListSortingDisabled]=\"columnSortingDisabled()\"\n\t>\n\t\t<div cdkDropListGroup class=\"hub-board__columns\">\n\t\t\t@for (column of columns(); let index = $index; track column) {\n\t\t\t\t<div\n\t\t\t\t\tclass=\"hub-board__column-container\"\n\t\t\t\t\tcdkDrag\n\t\t\t\t\t[cdkDragData]=\"column\"\n\t\t\t\t\t[cdkDragDisabled]=\"column.disabled\"\n\t\t\t\t>\n\t\t\t\t\t<div\n\t\t\t\t\t\tclass=\"hub-board__column\"\n\t\t\t\t\t\t[ngClass]=\"column.classlist\"\n\t\t\t\t\t\t[ngStyle]=\"column.style\"\n\t\t\t\t\t>\n\t\t\t\t\t\t<div class=\"hub-board__column-header\">\n\t\t\t\t\t\t\t<ng-container\n\t\t\t\t\t\t\t\t[ngTemplateOutlet]=\"\n\t\t\t\t\t\t\t\t\tcolumnHeaderTpt() || defaultColumnHeaderTpt\n\t\t\t\t\t\t\t\t\"\n\t\t\t\t\t\t\t\t[ngTemplateOutletContext]=\"{ column: column }\"\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\t<div\n\t\t\t\t\t\t\tclass=\"hub-board__column-body\"\n\t\t\t\t\t\t\tcdkDropList\n\t\t\t\t\t\t\t[cdkDropListData]=\"column\"\n\t\t\t\t\t\t\t(cdkDropListDropped)=\"dropCard($event)\"\n\t\t\t\t\t\t\t(scroll)=\"onScroll(index, $event)\"\n\t\t\t\t\t\t\t[cdkDropListEnterPredicate]=\"\n\t\t\t\t\t\t\t\tcolumn.predicate ?? defaultEnterPredicateFn\n\t\t\t\t\t\t\t\"\n\t\t\t\t\t\t\t[cdkDropListSortingDisabled]=\"\n\t\t\t\t\t\t\t\tcolumn.cardSortingDisabled\n\t\t\t\t\t\t\t\"\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t@for (\n\t\t\t\t\t\t\t\tcard of column.cards;\n\t\t\t\t\t\t\t\tlet index = $index;\n\t\t\t\t\t\t\t\ttrack card\n\t\t\t\t\t\t\t) {\n\t\t\t\t\t\t\t\t<div\n\t\t\t\t\t\t\t\t\tclass=\"hub-board__card\"\n\t\t\t\t\t\t\t\t\t[class.hub-board__card--disabled]=\"\n\t\t\t\t\t\t\t\t\t\tcard.disabled\n\t\t\t\t\t\t\t\t\t\"\n\t\t\t\t\t\t\t\t\tcdkDrag\n\t\t\t\t\t\t\t\t\t[cdkDragData]=\"card\"\n\t\t\t\t\t\t\t\t\t[cdkDragDisabled]=\"card.disabled\"\n\t\t\t\t\t\t\t\t\t(click)=\"cardClick(card)\"\n\t\t\t\t\t\t\t\t\t(mousedown)=\"\n\t\t\t\t\t\t\t\t\t\tcard.disabled &&\n\t\t\t\t\t\t\t\t\t\t\t$event.stopPropagation()\n\t\t\t\t\t\t\t\t\t\"\n\t\t\t\t\t\t\t\t\t[ngClass]=\"card.classlist\"\n\t\t\t\t\t\t\t\t\t[ngStyle]=\"card.style\"\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t<div class=\"hub-board__card-body\">\n\t\t\t\t\t\t\t\t\t\t<ng-container\n\t\t\t\t\t\t\t\t\t\t\t[ngTemplateOutlet]=\"\n\t\t\t\t\t\t\t\t\t\t\t\tcardTpt() || defaultCardTpt\n\t\t\t\t\t\t\t\t\t\t\t\"\n\t\t\t\t\t\t\t\t\t\t\t[ngTemplateOutletContext]=\"{\n\t\t\t\t\t\t\t\t\t\t\t\titem: card,\n\t\t\t\t\t\t\t\t\t\t\t\tcolumn\n\t\t\t\t\t\t\t\t\t\t\t}\"\n\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t</ng-container>\n\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t</div>\n\n\t\t\t\t\t\t@if (columnFooterTpt()) {\n\t\t\t\t\t\t\t<div class=\"hub-board__column-footer\">\n\t\t\t\t\t\t\t\t<ng-container\n\t\t\t\t\t\t\t\t\t[ngTemplateOutlet]=\"\n\t\t\t\t\t\t\t\t\t\tcolumnFooterTpt() ?? null\n\t\t\t\t\t\t\t\t\t\"\n\t\t\t\t\t\t\t\t\t[ngTemplateOutletContext]=\"{\n\t\t\t\t\t\t\t\t\t\tcolumn: column\n\t\t\t\t\t\t\t\t\t}\"\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t</ng-container>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t}\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t}\n\t\t</div>\n\t</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\">\n\t\t\t{{ column.title }}\n\t\t</h5>\n\t\t<h6 class=\"hub-board__column-header-subtitle\">\n\t\t\t{{ column.description }}\n\t\t</h6>\n\t</div>\n</ng-template>\n", styles: [":host{display:block;overflow:auto;width:100%;height:100%}.hub-board__column-body{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"] }] }] }); /** * Angular module that provides board functionality with drag-and-drop support. * * This module includes all the necessary components and directives for creating * Kanban-style boards with customizable columns, cards, and templates. * * @deprecated Use standalone components instead. Import individual components and directives directly. * @publicApi * * @example * ```typescript * // Legacy module approach (not recommended) * import { BoardModule } from 'ng-hub-ui-board'; * * @NgModule({ * imports: [BoardModule] * }) * export class AppModule {} * * // Recommended standalone approach * import { HubBoardComponent, CardTemplateDirective } from 'ng-hub-ui-board'; * * @Component({ * standalone: true, * imports: [HubBoardComponent, CardTemplateDirective] * }) * export class MyComponent {} * ``` */ class BoardModule { static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.2.3", ngImport: i0, type: BoardModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); static ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "20.2.3", ngImport: i0, type: BoardModule, imports: [HubBoardComponent, CardTemplateDirective, BoardColumnHeaderDirective, BoardColumnFooterDirective], exports: [HubBoardComponent, CardTemplateDirective, BoardColumnHeaderDirective, BoardColumnFooterDirective] }); static ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "20.2.3", ngImport: i0, type: BoardModule, imports: [HubBoardComponent] }); } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.2.3", ngImport: i0, type: BoardModule, decorators: [{ type: NgModule, args: [{ declarations: [], imports: [ HubBoardComponent, CardTemplateDirective, BoardColumnHeaderDirective, BoardColumnFooterDirective ], exports: [ HubBoardComponent, CardTemplateDirective, BoardColumnHeaderDirective, BoardColumnFooterDirective ] }] }] }); /** * Converts a hexadecimal color string into its inverted counterpart, offering both * high-contrast black/white and full-spectrum inversion modes. * * @publicApi */ class InvertColorPipe { /** * Inverts a HEX color value. * * @param hex - Color expressed as a 3- or 6-digit HEX string with or without a hash prefix. * @param bw - When `true`, returns either black or white based on perceived brightness to maximise contrast. * @returns The inverted color represented as a 6-digit HEX string (always prefixed with `#`). * @throws Error if the provided value cannot be parsed as a valid HEX color. */ transform(hex, bw) { if (!hex) { return '#000000'; } let normalizedHex = hex; if (normalizedHex.indexOf('#') === 0) { normalizedHex = normalizedHex.slice(1); } if (normalizedHex.length === 3) { // Convert shorthand notation (e.g., #abc) to full length (#aabbcc) normalizedHex = normalizedHex[0] + normalizedHex[0] + normalizedHex[1] + normalizedHex[1] + normalizedHex[2] + normalizedHex[2]; } if (normalizedHex.length !== 6) { throw new Error('Invalid HEX color.'); } const r = parseInt(normalizedHex.slice(0, 2), 16); const g = parseInt(normalizedHex.slice(2, 4), 16); const b = parseInt(normalizedHex.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'; } const invertedR = (255 - r).toString(16).padStart(2, '0'); const invertedG = (255 - g).toString(16).padStart(2, '0'); const invertedB = (255 - b).toString(16).padStart(2, '0'); return `#${invertedR}${invertedG}${invertedB}`; } static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.2.3", ngImport: i0, type: InvertColorPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe }); static ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "20.2.3", ngImport: i0, type: InvertColorPipe, isStandalone: true, name: "invertColor" }); } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.2.3", 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