devextreme
Version:
JavaScript/TypeScript Component Suite for Responsive Web Development
266 lines (265 loc) • 12 kB
JavaScript
/**
* DevExtreme (esm/__internal/scheduler/appointments_new/appointments.js)
* Version: 25.2.7
* Build date: Tue May 05 2026
*
* Copyright (c) 2012 - 2026 Developer Express Inc. ALL RIGHTS RESERVED
* Read about DevExtreme licensing here: https://js.devexpress.com/Licensing/
*/
import registerComponent from "../../../core/component_registrator";
import $ from "../../../core/renderer";
import {
domAdapter
} from "../../core/m_dom_adapter";
import {
EmptyTemplate
} from "../../core/templates/m_empty_template";
import DOMComponent from "../../core/widget/dom_component";
import {
AgendaAppointmentView
} from "./appointment/agenda_appointment";
import {
GridAppointmentView
} from "./appointment/grid_appointment";
import {
AppointmentCollector
} from "./appointment_collector";
import {
AppointmentsFocusController
} from "./appointments.focus_controller";
import {
APPOINTMENTS_CONTAINER_CLASS
} from "./const";
import {
getTargetedAppointment
} from "./utils/get_targeted_appointment";
import {
getViewModelDiff
} from "./utils/get_view_model_diff";
import {
isAgendaAppointmentViewModel,
isCollectorViewModel as isAppointmentCollectorViewModel,
isGridAppointmentViewModel
} from "./utils/type_helpers";
export class Appointments extends DOMComponent {
constructor() {
super(...arguments);
this.viewItemBySortedIndex = {};
this.viewItems = []
}
getViewItemByIndex(index) {
return this.viewItems[index]
}
getViewItemBySortedIndex(sortedIndex) {
return this.viewItemBySortedIndex[sortedIndex]
}
get $allDayContainer() {
return this.option().$allDayContainer
}
get $commonContainer() {
return this.$element()
}
_init() {
super._init();
this.focusController = new AppointmentsFocusController(this);
this._templateManager.addDefaultTemplates({
appointment: new EmptyTemplate,
appointmentCollector: new EmptyTemplate
});
if ("item" === this.option().appointmentTemplate) {
this.option("appointmentTemplate", "appointment")
}
}
_initMarkup() {
super._initMarkup();
this.$element().addClass(APPOINTMENTS_CONTAINER_CLASS)
}
_getDefaultOptions() {
return Object.assign({}, super._getDefaultOptions(), {
tabIndex: 0,
viewModel: [],
$allDayContainer: null,
appointmentTemplate: "appointment",
appointmentCollectorTemplate: "appointmentCollector",
onAppointmentRendered: () => {}
})
}
_optionChanged(args) {
switch (args.name) {
case "items":
this.option("viewModel", args.value);
break;
case "viewModel": {
if ("agenda" === this.option().currentView) {
this.renderViewModel(args.value);
break
}
const diff = this.getViewModelDiff(args.previousValue ?? [], args.value ?? []);
this.renderViewModelDiff(diff);
break
}
case "appointmentCollectorTemplate":
case "appointmentTemplate":
if ("appointmentTemplate" === args.name && "item" === args.value) {
this.option("appointmentTemplate", "appointment");
break
}
this.renderViewModel(this.option().viewModel);
break;
case "tabIndex":
this.focusController.resetTabIndex()
}
}
updateResizableArea() {}
moveAppointmentBack() {}
focus() {}
_renderAppointmentTemplate() {}
getViewModelDiff(oldViewModel, newViewModel) {
const isPreviousAgenda = oldViewModel.length && isAgendaAppointmentViewModel(oldViewModel[0]);
const normalizedOldViewModel = isPreviousAgenda ? [] : oldViewModel;
return getViewModelDiff(normalizedOldViewModel, newViewModel, this.option().getAppointmentDataSource())
}
renderViewModel() {
var _this$$allDayContaine, _this$$allDayContaine2;
let viewModel = arguments.length > 0 && void 0 !== arguments[0] ? arguments[0] : [];
const allDayFragment = domAdapter.createDocumentFragment();
const commonFragment = domAdapter.createDocumentFragment();
this.viewItemBySortedIndex = {};
null === (_this$$allDayContaine = this.$allDayContainer) || void 0 === _this$$allDayContaine || _this$$allDayContaine.empty();
this.$commonContainer.empty();
viewModel.forEach((viewModelItem, index) => {
const container = "agenda" === this.option().currentView || !viewModelItem.allDay ? commonFragment : allDayFragment;
const viewItem = this.renderViewItem(container, viewModelItem, index);
this.viewItemBySortedIndex[viewModelItem.sortedIndex] = viewItem
});
this.viewItems = Object.values(this.viewItemBySortedIndex);
null === (_this$$allDayContaine2 = this.$allDayContainer) || void 0 === _this$$allDayContaine2 || _this$$allDayContaine2.get(0).appendChild(allDayFragment);
this.$commonContainer.get(0).appendChild(commonFragment);
this.focusController.resetTabIndex()
}
renderViewModelDiff(viewModelDiff) {
var _this$$allDayContaine4;
const allDayFragment = domAdapter.createDocumentFragment();
const commonFragment = domAdapter.createDocumentFragment();
const newViewItemBySortedIndex = {};
const isRepaintAll = viewModelDiff.every(item => Boolean(item.needToAdd ?? item.needToRemove));
if (isRepaintAll) {
var _this$$allDayContaine3;
null === (_this$$allDayContaine3 = this.$allDayContainer) || void 0 === _this$$allDayContaine3 || _this$$allDayContaine3.empty();
this.$commonContainer.empty()
}
viewModelDiff.forEach((diffItem, index) => {
const {
allDay: allDay,
sortedIndex: sortedIndex
} = diffItem.item;
const lookupIndex = diffItem.oldSortedIndex ?? sortedIndex;
const viewItem = this.viewItemBySortedIndex[lookupIndex];
switch (true) {
case diffItem.needToRemove:
if (isRepaintAll) {
break
}
viewItem.$element().remove();
break;
case diffItem.needToAdd: {
const fragment = allDay ? allDayFragment : commonFragment;
const newViewItem = this.renderViewItem(fragment, diffItem.item, index);
newViewItemBySortedIndex[sortedIndex] = newViewItem;
break
}
default:
if (diffItem.needToResize) {
viewItem.resize({
height: diffItem.item.height,
width: diffItem.item.width,
top: diffItem.item.top,
left: diffItem.item.left
})
}
viewItem.option("sortedIndex", sortedIndex);
newViewItemBySortedIndex[sortedIndex] = viewItem
}
});
this.viewItemBySortedIndex = newViewItemBySortedIndex;
this.viewItems = Object.values(this.viewItemBySortedIndex);
null === (_this$$allDayContaine4 = this.$allDayContainer) || void 0 === _this$$allDayContaine4 || _this$$allDayContaine4.get(0).appendChild(allDayFragment);
this.$commonContainer.get(0).appendChild(commonFragment);
this.focusController.resetTabIndex()
}
renderViewItem(fragment, appointmentViewModel, index) {
const $element = $("<div>");
fragment.appendChild($element.get(0));
const targetedAppointmentData = this.getTargetedAppointmentData(appointmentViewModel);
const baseViewItemConfig = {
tabIndex: -1,
sortedIndex: appointmentViewModel.sortedIndex,
onFocusIn: this.focusController.onViewItemFocusIn.bind(this.focusController),
onFocusOut: this.focusController.onViewItemFocusOut.bind(this.focusController),
onClick: this.focusController.onViewItemClick.bind(this.focusController),
onKeyDown: this.focusController.onViewItemKeyDown.bind(this.focusController)
};
if (isAppointmentCollectorViewModel(appointmentViewModel)) {
return this._createComponent($element, AppointmentCollector, Object.assign({}, baseViewItemConfig, {
appointmentsData: appointmentViewModel.items.map(item => item.itemData),
isCompact: appointmentViewModel.isCompact,
geometry: {
height: appointmentViewModel.height,
width: appointmentViewModel.width,
top: appointmentViewModel.top,
left: appointmentViewModel.left
},
targetedAppointmentData: targetedAppointmentData,
appointmentCollectorTemplate: this._getTemplateByOption("appointmentCollectorTemplate")
}))
}
const baseAppointmentViewConfig = Object.assign({}, baseViewItemConfig, {
index: index,
appointmentTemplate: this._getTemplateByOption("appointmentTemplate"),
appointmentData: appointmentViewModel.itemData,
targetedAppointmentData: targetedAppointmentData,
onRendered: this.option().onAppointmentRendered,
getResourceColor: this.getResourceColor.bind(this, appointmentViewModel),
getDataAccessor: this.option().getDataAccessor
});
if (isGridAppointmentViewModel(appointmentViewModel)) {
return this._createComponent($element, GridAppointmentView, Object.assign({}, baseAppointmentViewConfig, {
geometry: {
height: appointmentViewModel.height,
width: appointmentViewModel.width,
top: appointmentViewModel.top,
left: appointmentViewModel.left
},
modifiers: {
empty: appointmentViewModel.empty
}
}))
}
return this._createComponent($element, AgendaAppointmentView, Object.assign({}, baseAppointmentViewConfig, {
modifiers: {
isLastInGroup: appointmentViewModel.isLastInGroup
},
geometry: {
height: appointmentViewModel.height,
width: appointmentViewModel.width
},
getResourcesValues: this.getResourcesValues.bind(this)
}))
}
getTargetedAppointmentData(appointmentViewModel) {
const normalizedAppointmentViewModel = isAppointmentCollectorViewModel(appointmentViewModel) ? appointmentViewModel.items[0] : appointmentViewModel;
return getTargetedAppointment(normalizedAppointmentViewModel, this.option().getDataAccessor(), this.option().getResourceManager())
}
getResourceColor(appointmentViewModel) {
const resourceManager = this.option().getResourceManager();
return resourceManager.getAppointmentColor({
itemData: appointmentViewModel.itemData,
groupIndex: appointmentViewModel.groupIndex
})
}
getResourcesValues(appointmentData) {
const resourceManager = this.option().getResourceManager();
return resourceManager.getAppointmentResourcesValues(appointmentData)
}
}
registerComponent("dxSchedulerNewAppointments", Appointments);