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
JavaScript
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