UNPKG

ng-hub-ui-board

Version:

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

830 lines (813 loc) 29.8 kB
import * as i0 from '@angular/core'; import { Signal, TemplateRef, ElementRef, PipeTransform } from '@angular/core'; export { moveItemInArray, transferArrayItem } from 'ng-hub-ui-utils'; /** * Represents a card within a board column, containing the core data and behavior. * * @template T The type of custom data attached to the card (defaults to `any`). * @publicApi */ interface BoardCard<T = any> { /** * Unique identifier for the card. */ id?: number; /** * The identifier of the column this card belongs to. */ columnId?: number; /** * The main title displayed on the card. */ title: string; /** * Optional description providing additional details about the card. */ description?: string; /** * Custom data that can be attached to this card, such as metadata, * priority levels, due dates, or any application-specific information. */ data?: T; /** * Optional list of CSS classes to apply to the card for custom styling. */ classlist?: string[]; /** * Custom inline styles for the card, represented as a key-value mapping. */ style?: { [key: string]: any; }; /** * If true, the card is disabled and cannot be interacted with (e.g., dragged or clicked). */ disabled?: boolean; } /** * Represents a drag-and-drop event for board operations. * This interface replaces the CDK's CdkDragDrop to provide a lightweight, * dependency-free alternative. * * @template C The type of the container data. * @template P The type of the previous container data (defaults to C). * @template I The type of the dragged item data (defaults to any). * @publicApi */ interface BoardDragDropEvent<C, P = C, I = any> { /** * The index of the item in its previous container before dragging. */ previousIndex: number; /** * The index where the item was dropped in the current container. */ currentIndex: number; /** * Data associated with the container where the item was dropped. */ container: BoardDropContainer<C>; /** * Data associated with the container from which the item was dragged. */ previousContainer: BoardDropContainer<P>; /** * The dragged item data. */ item: BoardDragItem<I>; /** * Whether the item was dropped in the same container it started in. */ isPointerOverContainer: boolean; /** * The distance the item was dragged. */ distance?: { x: number; y: number; }; /** * The point where the item was dropped. */ dropPoint?: { x: number; y: number; }; } /** * Represents a drop container in the board. * * @template T The type of data associated with the container. * @publicApi */ interface BoardDropContainer<T> { /** * The data associated with this container. */ data: T; /** * The DOM element of the container. */ element?: HTMLElement; } /** * Represents a dragged item in the board. * * @template T The type of data associated with the item. * @publicApi */ interface BoardDragItem<T> { /** * The data associated with this item. */ data: T; /** * The DOM element of the dragged item. */ element?: HTMLElement; } /** * Type alias for card drag-drop events. * @publicApi */ type CardDragDropEvent<T = any> = BoardDragDropEvent<BoardColumn<T>, BoardColumn<T>, BoardCard<T>>; /** * Type alias for column drag-drop events. * @publicApi */ type ColumnDragDropEvent<T = any> = BoardDragDropEvent<BoardColumn<T>[], BoardColumn<T>[], BoardColumn<T>>; /** * Represents a column within a board layout. * * @template T The data type associated with the column (defaults to `any`). * @publicApi */ interface BoardColumn<T = any> { /** * A unique identifier for the column. */ id?: number; /** * The identifier of the board this column belongs to. */ boardId?: number; /** * The title displayed for this column. */ title: string; /** * An optional description of this column. */ description?: string; /** * An array of cards contained within this column. */ cards: BoardCard<T>[]; /** * An optional set of inline styles applied to the column. */ style?: { [key: string]: any; }; /** * A string or array of CSS classes applied to the column. */ classlist?: string[] | string; /** * If true, the column is disabled (e.g., user interactions might be restricted). */ disabled?: boolean; /** * Additional data that can be attached to this column. */ data?: any; /** * If true, sorting cards within this column (via drag-and-drop) is disabled. */ cardSortingDisabled?: boolean; /** * A function to determine whether a dragged item is allowed in this column. * * @param item The dragged item (if any) being tested. * @returns A boolean indicating if the item can be dropped in this column. */ predicate?: (item?: BoardDragItem<T>) => boolean; } /** * Represents a board that can be composed of multiple columns. * * @template T - The type of data handled by each column (defaults to `any`). * @publicApi */ interface Board<T = any> { /** * Unique identifier for the board. */ id?: number; /** * The board's main title. */ title: string; /** * Optional description providing more details about the board. */ description?: string; /** * An array of columns that belong to this board. */ columns?: BoardColumn<T>[]; /** * Optional list of CSS classes to apply to the board. */ classlist?: string[]; /** * Custom inline styles for the board, represented as a key-value mapping. */ style?: { [key: string]: any; }; } /** * Event emitted when a column body is scrolled to its bottom. * * @template T - The type of column data being exposed to consumers (defaults to `any`). * @publicApi */ interface ReachedEndEvent<T = any> { index: number; data: T; } /** * Internal interface for tracking drag state. */ interface DragState { type: 'column' | 'card'; sourceColumnIndex: number; sourceCardIndex?: number; item: BoardColumn | BoardCard; element?: HTMLElement; } /** * Defines how the dragged element behaves visually during drag operations. * - 'ghost': Element becomes semi-transparent but remains visible and occupies space * - 'hide': Element is hidden but still occupies space (invisible placeholder) * - 'collapse': Element is completely hidden and its space is collapsed * @publicApi */ type DragBehavior = 'ghost' | 'hide' | 'collapse'; /** * Standalone Kanban-style board component that provides column-based drag-and-drop, * custom templates and infinite-scroll detection. * * @publicApi */ declare class HubBoardComponent { /** * Reactive input containing the full board definition (columns and cards). */ readonly board: i0.InputSignal<Board<any> | undefined>; /** * Semantic accent applied to the drag/drop placeholder. The built-in values * (`primary` / `success` / `danger` / `warning` / `info`) render with the * design-system tints; any other string is also accepted — the board reads * `--hub-sys-color-<variant>` from the host application, so a custom accent * palette interconnects with no changes to this library. Defaults to `primary`. */ readonly variant: i0.InputSignal<(string & {}) | "primary" | "success" | "danger" | "warning" | "info" | undefined>; /** * Inline accent fed to the board styles: `var(--hub-sys-color-<variant>)` for * the active variant, or `null` to keep the `primary` default. Keeps the * variant set open to any accent token the host application defines. */ readonly groupAccent: Signal<string | null>; /** * Pixel threshold used when determining whether a column has reached scroll end. * Allows for fractional scroll values across different browsers. */ private readonly scrollDetectionPadding; /** * Internal signal to track column updates and force re-renders. */ private readonly _columnsVersion; /** * Derived list of board columns exposed as a signal to the template. * Depends on both the board input and internal version counter to ensure * re-renders after in-place array mutations. */ columns: Signal<Array<BoardColumn>>; /** * When true, column reordering via drag-and-drop is disabled. */ readonly columnSortingDisabled: i0.InputSignal<boolean>; /** * Controls how dragged elements behave visually during drag operations. * - 'ghost': Element becomes semi-transparent (50% opacity) but remains visible * - 'hide': Element is hidden but still occupies its space * - 'collapse': Element is completely hidden and its space is collapsed (default) */ readonly dragBehavior: i0.InputSignal<DragBehavior>; /** * Custom card template supplied via the `cardTpt` structural directive. */ readonly cardTpt: Signal<TemplateRef<any> | undefined>; /** * Custom column header template supplied via the `columnHeaderTpt` structural directive. */ readonly columnHeaderTpt: Signal<TemplateRef<any> | undefined>; /** * Custom column footer template supplied via the `columnFooterTpt` structural directive. */ readonly columnFooterTpt: Signal<TemplateRef<any> | undefined>; /** * Custom card placeholder template supplied via the `cardPlaceholder` structural directive. * Used to customize the appearance of the drop zone when dragging cards. */ readonly cardPlaceholderTpt: Signal<TemplateRef<any> | undefined>; /** * Custom column placeholder template supplied via the `columnPlaceholder` structural directive. * Used to customize the appearance of the drop zone when dragging columns. */ readonly columnPlaceholderTpt: Signal<TemplateRef<any> | undefined>; /** * Custom card drag preview template supplied via the `cardDragPreview` structural directive. * Used to customize the visual element that follows the cursor when dragging cards. * The template receives `card` (the dragged card) and `column` (the source column) as context. */ readonly cardDragPreviewTpt: Signal<TemplateRef<any> | undefined>; /** * Custom column drag preview template supplied via the `columnDragPreview` structural directive. * Used to customize the visual element that follows the cursor when dragging columns. * The template receives `column` (the dragged column) as context. */ readonly columnDragPreviewTpt: Signal<TemplateRef<any> | undefined>; /** * Reference to the hidden container where drag preview elements are rendered. */ readonly dragPreviewContainer: Signal<ElementRef<HTMLElement> | undefined>; /** * Disposer for the currently active custom drag preview, or `null`. */ private dragPreviewDestroy; /** * Emits each time a card is clicked within the board. */ readonly onCardClick: i0.OutputEmitterRef<BoardCard<any>>; /** * Emits when a card has been repositioned, either within the same column or into another column. */ readonly onCardMoved: i0.OutputEmitterRef<CardDragDropEvent<any>>; /** * Emits when columns are reordered through drag-and-drop. */ readonly onColumnMoved: i0.OutputEmitterRef<ColumnDragDropEvent<any>>; /** * Emits when a column body is scrolled to its end, enabling infinite-scroll behaviour. */ readonly reachedEnd: i0.OutputEmitterRef<ReachedEndEvent<any>>; /** * Internal drag state tracking. */ readonly dragState: i0.WritableSignal<DragState | null>; /** * Signal for tracking the currently hovered column index during card drag. */ readonly hoveredColumnIndex: i0.WritableSignal<number | null>; /** * Signal for tracking the drop indicator position within a column. */ readonly dropIndicatorIndex: i0.WritableSignal<number | null>; /** * Signal for tracking the column drop indicator position. */ readonly columnDropIndicatorIndex: i0.WritableSignal<number | null>; /** * Default predicate that allows any card to be dropped into any column. * * @returns Always `true`, indicating that drop operations are permitted. */ defaultEnterPredicateFn: () => boolean; /** * Returns the card currently being dragged, if any. * * @returns The dragged card or null if no card is being dragged. */ get draggedCard(): BoardCard | null; /** * Returns the column currently being dragged, if any. * * @returns The dragged column or null if no column is being dragged. */ get draggedColumn(): BoardColumn | null; /** * Emits the clicked card through {@link onCardClick}. * * @param item - The card that triggered the click event. */ cardClick(item: BoardCard): void; /** * Track function for columns to ensure proper re-rendering. * Uses column id if available, otherwise falls back to index. * * @param index - The index of the column. * @param column - The column object. * @returns A unique identifier for the column. */ trackColumnById(index: number, column: BoardColumn): string | number; /** * Checks if the given column is currently being dragged. * * @param column - The column to check. * @returns Whether the column is being dragged. */ isDraggingColumn(column: BoardColumn): boolean; /** * Checks if the given card is currently being dragged. * * @param card - The card to check. * @returns Whether the card is being dragged. */ isDraggingCard(card: BoardCard): boolean; /** * Handles the drag start event for columns. * * @param event - The native drag event. * @param column - The column being dragged. * @param columnIndex - The index of the column in the board. */ onColumnDragStart(event: DragEvent, column: BoardColumn, columnIndex: number): void; /** * Handles the drag end event for columns. * * @param _event - The native drag event (unused). */ onColumnDragEnd(_event: DragEvent): void; /** * Handles the drag over event for the board container. * Calculates the drop position based on mouse position relative to columns. * * @param event - The native drag event. */ onBoardDragOver(event: DragEvent): void; /** * Handles the drop event for the board container (column reordering). * * @param event - The native drag event. */ onBoardDrop(event: DragEvent): void; /** * Handles the drag start event for cards. * * @param event - The native drag event. * @param card - The card being dragged. * @param columnIndex - The index of the column containing the card. * @param cardIndex - The index of the card within the column. */ onCardDragStart(event: DragEvent, card: BoardCard, columnIndex: number, cardIndex: number): void; /** * Handles the drag end event for cards. * * @param _event - The native drag event (unused). */ onCardDragEnd(_event: DragEvent): void; /** * Handles the drag over event for column bodies (card drop zones). * * @param event - The native drag event. * @param column - The column being dragged over. * @param columnIndex - The index of the column. */ onCardDragOver(event: DragEvent, column: BoardColumn, columnIndex: number): void; /** * Handles the drag leave event for column bodies. * * @param event - The native drag event. * @param columnIndex - The index of the column being left. */ onCardDragLeave(event: DragEvent, columnIndex: number): void; /** * Handles the drop event for cards. * * @param event - The native drag event. * @param column - The target column. * @param columnIndex - The index of the target column. */ onCardDrop(event: DragEvent, column: BoardColumn, columnIndex: number): void; /** * Determines if a column should show the drop indicator. * * @param columnIndex - The index position to check for the drop indicator. * @returns Whether the drop indicator should be shown at this position. */ shouldShowColumnDropIndicator(columnIndex: number): boolean; /** * Determines if a card drop indicator should be shown at a specific position. * * @param columnIndex - The index of the column. * @param cardIndex - The index where the indicator would appear. * @returns Whether the drop indicator should be shown. */ shouldShowCardDropIndicator(columnIndex: number, cardIndex: number): boolean; /** * 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: number, event: Event): void; /** * Creates a custom drag preview element from a template and renders it in a hidden container. * The element must be in the DOM for setDragImage to work properly. * * @param template - The template to render as the drag preview. * @param context - The context to pass to the template. * @returns The native HTML element to use as the drag image, or null if creation failed. */ private createDragPreview; /** * Destroys the current drag preview view and cleans up related resources. The disposer * returned by `createNativeDragImage` removes both the embedded view and the rendered * nodes, so no manual container clearing is required. */ private destroyDragPreview; static ɵfac: i0.ɵɵFactoryDeclaration<HubBoardComponent, never>; static ɵcmp: i0.ɵɵComponentDeclaration<HubBoardComponent, "hub-board, hub-ui-board", never, { "board": { "alias": "board"; "required": false; "isSignal": true; }; "variant": { "alias": "variant"; "required": false; "isSignal": true; }; "columnSortingDisabled": { "alias": "columnSortingDisabled"; "required": false; "isSignal": true; }; "dragBehavior": { "alias": "dragBehavior"; "required": false; "isSignal": true; }; }, { "onCardClick": "onCardClick"; "onCardMoved": "onCardMoved"; "onColumnMoved": "onColumnMoved"; "reachedEnd": "reachedEnd"; }, ["cardTpt", "columnHeaderTpt", "columnFooterTpt", "cardPlaceholderTpt", "columnPlaceholderTpt", "cardDragPreviewTpt", "columnDragPreviewTpt"], never, true, never>; } /** * 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> * ``` */ declare class CardTemplateDirective { templateRef: TemplateRef<unknown>; /** * Creates a new CardTemplateDirective instance. * * @param templateRef - The template reference that contains the custom card layout */ constructor(templateRef: TemplateRef<unknown>); static ɵfac: i0.ɵɵFactoryDeclaration<CardTemplateDirective, never>; static ɵdir: i0.ɵɵDirectiveDeclaration<CardTemplateDirective, "[cardTpt]", never, {}, {}, never, never, true, never>; } /** * 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> * ``` */ declare class BoardColumnHeaderDirective { templateRef: TemplateRef<unknown>; /** * Creates a new BoardColumnHeaderDirective instance. * * @param templateRef - The template reference that contains the custom column header layout */ constructor(templateRef: TemplateRef<unknown>); static ɵfac: i0.ɵɵFactoryDeclaration<BoardColumnHeaderDirective, never>; static ɵdir: i0.ɵɵDirectiveDeclaration<BoardColumnHeaderDirective, "[columnHeaderTpt]", never, {}, {}, never, never, true, never>; } /** * 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> * ``` */ declare class BoardColumnFooterDirective { templateRef: TemplateRef<unknown>; /** * Creates a new BoardColumnFooterDirective instance. * * @param templateRef - The template reference that contains the custom column footer layout */ constructor(templateRef: TemplateRef<unknown>); static ɵfac: i0.ɵɵFactoryDeclaration<BoardColumnFooterDirective, never>; static ɵdir: i0.ɵɵDirectiveDeclaration<BoardColumnFooterDirective, "[columnFooterTpt]", never, {}, {}, never, never, true, never>; } /** * Structural directive used to define a custom placeholder template for cards during drag operations. * * When a card is being dragged, the placeholder shows where the card will be dropped. * By default, an empty space with a dashed border is shown. Use this directive to customize * the placeholder appearance. * * @usageNotes * * ### Basic usage * * ```html * <hub-board [board]="board"> * <ng-template cardPlaceholder let-card="card" let-column="column"> * <div class="my-custom-placeholder"> * <span>Drop "{{ card?.title }}" here</span> * </div> * </ng-template> * </hub-board> * ``` * * ### Context variables * * The template context provides: * - `card`: The card being dragged (may be undefined) * - `column`: The target column where the card will be dropped * * @publicApi */ declare class CardPlaceholderDirective { static ɵfac: i0.ɵɵFactoryDeclaration<CardPlaceholderDirective, never>; static ɵdir: i0.ɵɵDirectiveDeclaration<CardPlaceholderDirective, "[cardPlaceholder]", never, {}, {}, never, never, true, never>; } /** * Structural directive used to define a custom placeholder template for columns during drag operations. * * When a column is being dragged, the placeholder shows where the column will be dropped. * By default, an empty space with a dashed border is shown. Use this directive to customize * the placeholder appearance. * * @usageNotes * * ### Basic usage * * ```html * <hub-board [board]="board"> * <ng-template columnPlaceholder let-column="column"> * <div class="my-custom-placeholder"> * <span>Drop "{{ column?.title }}" here</span> * </div> * </ng-template> * </hub-board> * ``` * * ### Context variables * * The template context provides: * - `column`: The column being dragged (may be undefined) * * @publicApi */ declare class ColumnPlaceholderDirective { static ɵfac: i0.ɵɵFactoryDeclaration<ColumnPlaceholderDirective, never>; static ɵdir: i0.ɵɵDirectiveDeclaration<ColumnPlaceholderDirective, "[columnPlaceholder]", never, {}, {}, never, never, true, never>; } /** * 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 {} * ``` */ declare class BoardModule { static ɵfac: i0.ɵɵFactoryDeclaration<BoardModule, never>; static ɵmod: i0.ɵɵNgModuleDeclaration<BoardModule, never, [typeof HubBoardComponent, typeof CardTemplateDirective, typeof BoardColumnHeaderDirective, typeof BoardColumnFooterDirective, typeof CardPlaceholderDirective, typeof ColumnPlaceholderDirective], [typeof HubBoardComponent, typeof CardTemplateDirective, typeof BoardColumnHeaderDirective, typeof BoardColumnFooterDirective, typeof CardPlaceholderDirective, typeof ColumnPlaceholderDirective]>; static ɵinj: i0.ɵɵInjectorDeclaration<BoardModule>; } /** * Directive that allows customization of the drag preview (drag image) for cards. * * This directive defines a custom template for the visual element that follows * the cursor when dragging a card. The template receives the dragged card and * the source column as context variables. * * @publicApi * * @example * ```html * <hub-board [board]="board"> * <ng-template cardDragPreview let-card="card" let-column="column"> * <div class="custom-drag-preview"> * <i class="bi bi-grip-vertical"></i> * <span>{{ card.title }}</span> * </div> * </ng-template> * </hub-board> * ``` */ declare class CardDragPreviewDirective { templateRef: TemplateRef<unknown>; /** * Creates a new CardDragPreviewDirective instance. * * @param templateRef - The template reference that contains the custom drag preview layout */ constructor(templateRef: TemplateRef<unknown>); static ɵfac: i0.ɵɵFactoryDeclaration<CardDragPreviewDirective, never>; static ɵdir: i0.ɵɵDirectiveDeclaration<CardDragPreviewDirective, "[cardDragPreview]", never, {}, {}, never, never, true, never>; } /** * Directive that allows customization of the drag preview (drag image) for columns. * * This directive defines a custom template for the visual element that follows * the cursor when dragging a column. The template receives the dragged column * as a context variable. * * @publicApi * * @example * ```html * <hub-board [board]="board"> * <ng-template columnDragPreview let-column="column"> * <div class="custom-column-drag-preview"> * <i class="bi bi-kanban"></i> * <span>{{ column.title }}</span> * <span class="badge">{{ column.cards.length }} cards</span> * </div> * </ng-template> * </hub-board> * ``` */ declare class ColumnDragPreviewDirective { templateRef: TemplateRef<unknown>; /** * Creates a new ColumnDragPreviewDirective instance. * * @param templateRef - The template reference that contains the custom drag preview layout */ constructor(templateRef: TemplateRef<unknown>); static ɵfac: i0.ɵɵFactoryDeclaration<ColumnDragPreviewDirective, never>; static ɵdir: i0.ɵɵDirectiveDeclaration<ColumnDragPreviewDirective, "[columnDragPreview]", never, {}, {}, never, never, true, never>; } /** * Converts a hexadecimal color string into its inverted counterpart, offering both * high-contrast black/white and full-spectrum inversion modes. * * @publicApi */ declare class InvertColorPipe implements PipeTransform { /** * 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: string, bw: boolean): string; static ɵfac: i0.ɵɵFactoryDeclaration<InvertColorPipe, never>; static ɵpipe: i0.ɵɵPipeDeclaration<InvertColorPipe, "invertColor", true>; } export { BoardColumnFooterDirective, BoardColumnHeaderDirective, BoardModule, CardDragPreviewDirective, CardPlaceholderDirective, CardTemplateDirective, ColumnDragPreviewDirective, ColumnPlaceholderDirective, HubBoardComponent, InvertColorPipe }; export type { Board, BoardCard, BoardColumn, BoardDragDropEvent, BoardDragItem, BoardDropContainer, CardDragDropEvent, ColumnDragDropEvent, DragBehavior, ReachedEndEvent };