UNPKG

@progress/kendo-angular-scheduler

Version:

Kendo UI Scheduler Angular - Outlook or Google-style angular scheduler calendar. Full-featured and customizable embedded scheduling from the creator developers trust for professional UI components.

138 lines (137 loc) 6.21 kB
/**----------------------------------------------------------------------------------------- * Copyright © 2025 Progress Software Corporation. All rights reserved. * Licensed under commercial license. See LICENSE.md in the project root for more information *-------------------------------------------------------------------------------------------*/ import { Directive, Input, Output, EventEmitter, ChangeDetectorRef } from '@angular/core'; import { Subscription } from 'rxjs'; import { intersects, isSameRange, resourcesMatch } from '../utils'; import { SchedulerComponent } from '../../scheduler.component'; import { distinctUntilChanged, filter } from 'rxjs/operators'; import { isChanged } from '@progress/kendo-angular-common'; import * as i0 from "@angular/core"; import * as i1 from "../../scheduler.component"; /** * Represents a directive that manages the built-in slot selection in the Scheduler. * * Add the `kendoSchedulerSlotSelectable` directive to a `<kendo-scheduler>` instance to allow users to select time slots by clicking or dragging. * * The directive keeps track of the selected slot range and emits changes when the selection is updated by user interaction. * * @example * ```html * <kendo-scheduler kendoSchedulerSlotSelectable [(slotSelection)]="selectedSlot"> * </kendo-scheduler> * ``` * * @remarks * Applied to: {@link SchedulerComponent} */ export class SlotSelectableDirective { scheduler; cdr; /** * Represents the currently selected slot range. */ slotSelection; /** * Fires when the user changes the currently selected slot range. */ slotSelectionChange = new EventEmitter(); /** * @hidden * The resources of the cell where the selection started. * When dragging over the slots of a different resource in grouped mode, the ongoing selection will not be affected. */ selectionOriginResources; selectedRange = null; slotSelectionChangeSource = new EventEmitter(); subscriptions = new Subscription(); constructor(scheduler, cdr) { this.scheduler = scheduler; this.cdr = cdr; this.scheduler.selectable = true; this.subscriptions.add(this.slotSelectionChangeSource .pipe(distinctUntilChanged(isSameRange)).subscribe((v) => { this.slotSelectionChange.emit(v); })); const start$ = this.scheduler.slotDragStart; const drag$ = this.scheduler.slotDrag; const end$ = this.scheduler.slotDragEnd; const startSource = start$.pipe(filter(e => !e.isDefaultPrevented())); this.subscriptions.add(startSource.subscribe(e => this.initDragSelect(e))); this.subscriptions.add(drag$.subscribe(e => this.onDrag(e))); this.subscriptions.add(end$.subscribe(() => this.onRelease())); } ngOnInit() { this.scheduler.isSlotSelected = this.isSlotSelected.bind(this); } ngOnChanges(changes) { if (isChanged("slotSelection", changes, false)) { const defaults = { isAllDay: false, resources: this.scheduler?.resources ? this.scheduler.resources.reduce((result, resource) => { result.push(...resource.data); return result; }, []) : [] }; this.selectedRange = Object.assign(defaults, changes['slotSelection'].currentValue); this.cdr.markForCheck(); } } ngOnDestroy() { this.subscriptions.unsubscribe(); } isSlotSelected({ start, end, isAllDay, resources }) { if (!start || !end || !this.selectedRange) { return false; } const match = resourcesMatch(this.selectedRange.resources, resources); if (!match) { return false; // Limit selection to the grouped resource where the drag started. } return this.selectedRange && isAllDay === this.selectedRange.isAllDay && this.isInRange(start, end); } initDragSelect({ start, end, isAllDay, resources }) { this.selectionOriginResources = resources.slice(); this.selectedRange = { start, end, isAllDay, resources: resources.slice() }; this.cdr.markForCheck(); } onDrag({ start, end, resources }) { const match = resourcesMatch(this.selectionOriginResources, resources); if (!match) { return; // Don't change selection when dragging over a different grouped resource's cells. } this.selectedRange.start = start; this.selectedRange.end = end; this.cdr.markForCheck(); } onRelease() { this.selectionOriginResources = null; if (this.selectedRange) { this.slotSelectionChangeSource.emit(this.selectedRange); } } /** * @hidden * Checks if the selected range contains a local date range. */ isInRange(start, end) { if (!this.selectedRange) { return; } return intersects(start, end, this.selectedRange.start, this.selectedRange.end); } static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: SlotSelectableDirective, deps: [{ token: i1.SchedulerComponent }, { token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Directive }); static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "18.2.14", type: SlotSelectableDirective, isStandalone: true, selector: "[kendoSchedulerSlotSelectable]", inputs: { slotSelection: "slotSelection" }, outputs: { slotSelectionChange: "slotSelectionChange" }, usesOnChanges: true, ngImport: i0 }); } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: SlotSelectableDirective, decorators: [{ type: Directive, args: [{ selector: '[kendoSchedulerSlotSelectable]', standalone: true }] }], ctorParameters: () => [{ type: i1.SchedulerComponent }, { type: i0.ChangeDetectorRef }], propDecorators: { slotSelection: [{ type: Input }], slotSelectionChange: [{ type: Output }] } });