@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.
221 lines (220 loc) • 11.1 kB
JavaScript
/**-----------------------------------------------------------------------------------------
* Copyright © 2025 Progress Software Corporation. All rights reserved.
* Licensed under commercial license. See LICENSE.md in the project root for more information
*-------------------------------------------------------------------------------------------*/
import { ChangeDetectorRef, Directive, NgZone } from '@angular/core';
import { Keys } from '@progress/kendo-angular-common';
import { ZonedDate } from '@progress/kendo-date-math';
import { Subscription } from 'rxjs';
import { FocusService } from './focus.service';
import { Modifiers, noModifiers, withModifiers } from '../common/modifiers';
import { groupResources } from '../common/util';
import { SchedulerComponent } from '../scheduler.component';
import { DomEventsService } from '../views/common/dom-events.service';
import { take } from 'rxjs/operators';
import { ViewStateService } from '../views/view-state.service';
import { hasClasses } from '../common/dom-queries';
import { DialogsService } from '../editing/dialogs.service';
import * as i0 from "@angular/core";
import * as i1 from "../scheduler.component";
import * as i2 from "../views/common/dom-events.service";
import * as i3 from "./focus.service";
import * as i4 from "../views/view-state.service";
import * as i5 from "../editing/dialogs.service";
const isContentWrapper = element => hasClasses(element, 'k-scheduler-content');
const CALENDAR_TAG = 'KENDO-CALENDAR-HORIZONTAL';
/**
* @hidden
*/
export class ShortcutsDirective {
scheduler;
domEvents;
focusService;
zone;
changeDetector;
viewState;
dialogsService;
shortcuts = [{
match: e => e.keyCode === Keys.KeyC && noModifiers(e),
action: e => {
const scheduler = this.scheduler;
const hours = new Date().getHours();
const selected = scheduler.selectedDate;
const start = new Date(selected.getFullYear(), selected.getMonth(), selected.getDate(), hours + 1);
const end = new Date(selected.getFullYear(), selected.getMonth(), selected.getDate(), hours + 2);
let firstResource;
if (scheduler.group) {
const resources = scheduler.resources || [];
const group = scheduler.group || {};
const grouped = groupResources(group, resources);
if (grouped.length > 0) {
firstResource = grouped[0].data[0];
}
}
scheduler.create.emit({
start: ZonedDate.fromLocalDate(start, scheduler.timezone).toLocalDate(),
end: ZonedDate.fromLocalDate(end, scheduler.timezone).toLocalDate(),
isAllDay: false,
resources: [firstResource],
originalEvent: e,
sender: scheduler
});
}
}, {
match: e => e.keyCode >= Keys.Digit1 && e.keyCode <= Keys.Digit9 && withModifiers(e, Modifiers.AltKey),
action: e => {
const scheduler = this.scheduler;
const viewIndex = e.keyCode - Keys.Digit0 - 1;
const views = scheduler.views.toArray();
const view = views[viewIndex];
if (view) {
this.zone.run(() => {
const prevented = scheduler.onNavigationAction({ type: 'view-change', view });
if (!prevented) {
this.changeDetector.markForCheck();
this.focusWait();
}
});
}
}
}, {
match: e => e.keyCode === Keys.F10 && noModifiers(e),
action: (e) => {
this.zone.run(() => {
e.preventDefault();
this.scheduler.onNavigationAction({ type: 'focus-toolbar' });
this.focusWait();
});
}
}, {
match: e => e.keyCode === Keys.KeyT && noModifiers(e),
action: () => {
this.zone.run(() => {
this.scheduler.onNavigationAction({ type: 'today' });
this.focusWait();
});
}
}, {
match: e => e.keyCode === Keys.KeyB && noModifiers(e),
action: () => {
this.zone.run(() => {
this.scheduler.onNavigationAction({ type: 'toggle-business-hours' });
this.focusWait();
});
}
}, {
match: (e) => (e.keyCode === Keys.ArrowLeft || e.keyCode === Keys.ArrowRight) && withModifiers(e, Modifiers.ShiftKey),
action: (e) => {
const type = e.keyCode === Keys.ArrowLeft ? 'prev' : 'next';
this.zone.run(() => {
this.scheduler.onNavigationAction({ type });
this.focusWait();
});
}
}, {
match: e => (e.keyCode === Keys.ArrowUp || e.keyCode === Keys.ArrowLeft) && noModifiers(e) && !isContentWrapper(e.target),
action: e => {
//use the MultiViewCalendar navigation for Year View
if (e.target.tagName === CALENDAR_TAG) {
return;
}
// do nothing if the shortcut is executed on an element inside the kendoSchedulerToolbarTemplate
if (this.isInToolbarTemplate(e.target)) {
return;
}
const prevented = this.scheduler.onNavigationAction({ type: 'focus-prev' });
if (!prevented) {
const item = this.focusService.activeItem;
const isFirstEvent = item.containerType === 'content' && item.element.nativeElement.matches(':first-of-type');
const isUpArrow = e.keyCode === Keys.ArrowUp;
// eslint-disable-next-line no-unused-expressions
isFirstEvent && isUpArrow ? this.focusService.focusToolbar() : this.focusService.focusNext({ offset: -1 });
e.preventDefault();
}
}
}, {
match: e => (e.keyCode === Keys.ArrowDown || e.keyCode === Keys.ArrowRight) && noModifiers(e) && !isContentWrapper(e.target),
action: e => {
//use the MultiViewCalendar navigation for Year View
if (e.target.tagName === CALENDAR_TAG) {
return;
}
// do nothing if the shortcut is executed on an element inside the kendoSchedulerToolbarTemplate
if (this.isInToolbarTemplate(e.target)) {
return;
}
const prevented = this.scheduler.onNavigationAction({ type: 'focus-next' });
if (!prevented) {
const isInToolbar = this.focusService.activeItem.containerType === 'toolbar';
const offset = 1;
if (e.keyCode === Keys.ArrowDown && isInToolbar) {
const focusableElementsArray = Array.from(this.focusService.focusableItems);
const firstFocusableContentElementIndex = focusableElementsArray.findIndex(item => item.containerType === 'content');
if (firstFocusableContentElementIndex > -1) {
this.focusService.focusByIndex(firstFocusableContentElementIndex);
e.preventDefault();
return;
}
}
this.focusService.focusNext({ offset });
e.preventDefault();
}
}
}];
taskShortcuts = [{
match: e => (e.keyCode === Keys.Delete || e.keyCode === Keys.Backspace) && noModifiers(e),
action: (e, event) => {
this.viewState.emitEvent('remove', { event: event, dataItem: event.dataItem });
e.preventDefault();
}
}, {
match: e => e.keyCode === Keys.Enter && noModifiers(e),
action: (e, event) => {
this.viewState.emitEvent('eventDblClick', { type: 'dblclick', event: event, originalEvent: e });
e.preventDefault();
}
}];
subs = new Subscription();
constructor(scheduler, domEvents, focusService, zone, changeDetector, viewState, dialogsService) {
this.scheduler = scheduler;
this.domEvents = domEvents;
this.focusService = focusService;
this.zone = zone;
this.changeDetector = changeDetector;
this.viewState = viewState;
this.dialogsService = dialogsService;
this.subs.add(this.domEvents.keydown.subscribe(e => this.onKeydown(e)));
this.subs.add(this.scheduler.eventKeydown.subscribe(e => this.onEventKeydown(e)));
}
onKeydown(e) {
const match = this.shortcuts.find(shortcut => shortcut.match(e));
if (match && !this.dialogsService.isOpen) {
match.action(e);
}
}
onEventKeydown(e) {
const match = this.taskShortcuts.find(shortcut => shortcut.match(e.originalEvent));
if (match && !this.dialogsService.isOpen) {
match.action(e.originalEvent, e.event);
}
}
focusWait() {
this.viewState.layoutEnd.pipe(take(1)).subscribe(() => this.focusService.focus());
}
isInToolbarTemplate(element) {
const isInToolbar = element.closest('.k-scheduler-toolbar');
const isInBuiltInElement = element.closest('.k-toolbar-group') ||
element.closest('.k-scheduler-views') ||
element.closest('.k-views-dropdown');
return isInToolbar && !isInBuiltInElement;
}
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: ShortcutsDirective, deps: [{ token: i1.SchedulerComponent }, { token: i2.DomEventsService }, { token: i3.FocusService }, { token: i0.NgZone }, { token: i0.ChangeDetectorRef }, { token: i4.ViewStateService }, { token: i5.DialogsService }], target: i0.ɵɵFactoryTarget.Directive });
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "16.2.12", type: ShortcutsDirective, isStandalone: true, selector: "kendo-scheduler", ngImport: i0 });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: ShortcutsDirective, decorators: [{
type: Directive,
args: [{
selector: 'kendo-scheduler',
standalone: true
}]
}], ctorParameters: function () { return [{ type: i1.SchedulerComponent }, { type: i2.DomEventsService }, { type: i3.FocusService }, { type: i0.NgZone }, { type: i0.ChangeDetectorRef }, { type: i4.ViewStateService }, { type: i5.DialogsService }]; } });