diff --git a/apps/demos/Demos/Scheduler/ContextMenu/Angular/app/app.component.html b/apps/demos/Demos/Scheduler/ContextMenu/Angular/app/app.component.html index 93cbf6453a15..efa3ce860356 100644 --- a/apps/demos/Demos/Scheduler/ContextMenu/Angular/app/app.component.html +++ b/apps/demos/Demos/Scheduler/ContextMenu/Angular/app/app.component.html @@ -19,17 +19,20 @@ icon="conferenceroomoutline" > +
- @if(e.color) { -
- } -
{{ e.text }}
+
+ @if(e.color) { +
+ } + {{ e.text }} +
diff --git a/apps/demos/Demos/Scheduler/ContextMenu/Angular/app/app.component.ts b/apps/demos/Demos/Scheduler/ContextMenu/Angular/app/app.component.ts index e9fd6bb38d71..dbb0f9ffb696 100644 --- a/apps/demos/Demos/Scheduler/ContextMenu/Angular/app/app.component.ts +++ b/apps/demos/Demos/Scheduler/ContextMenu/Angular/app/app.component.ts @@ -13,9 +13,6 @@ if (!/localhost/.test(document.location.host)) { enableProdMode(); } -const appointmentClassName = '.dx-scheduler-appointment'; -const cellClassName = '.dx-scheduler-date-table-cell'; - let modulePrefix = ''; // @ts-ignore if (window && window.config?.packageConfigPaths) { @@ -42,60 +39,80 @@ export class AppComponent { resourcesData: Resource[]; - groups: string[]; + groups: string[] = []; crossScrollingEnabled = false; - contextMenuItems = []; - - disabled = true; - - target: string = appointmentClassName; + contextMenuItems: ContextMenuItem[] = []; constructor(service: Service) { this.resourcesData = service.getResources(); this.appointmentsData = service.getAppointments(); } - onAppointmentContextMenu({ appointmentData, targetedAppointmentData }: DxSchedulerTypes.AppointmentContextMenuEvent) { + onContextMenuItemClick(e: DxContextMenuTypes.ItemClickEvent) { + e.itemData.onItemClick(e); + } + + onAppointmentContextMenu(e: DxSchedulerTypes.AppointmentContextMenuEvent) { + const items = this.getAppointmentContextMenuItems(e); + this.contextMenuItems = items; + } + + onCellContextMenu(e: DxSchedulerTypes.CellContextMenuEvent) { + const items = this.getCellContextMenuItems(e); + this.contextMenuItems = items; + } + + onContextMenuHiding() { + this.contextMenuItems = []; + } + + getAppointmentContextMenuItems(e: DxSchedulerTypes.AppointmentContextMenuEvent): ContextMenuItem[] { + const { + appointmentData: appointment, + targetedAppointmentData: targetedAppointment, + } = e; const scheduler = this.scheduler.instance; - const resourceItems = this.resourcesData - .map((item) => ({ - ...item, - onItemClick: ({ itemData }: DxContextMenuTypes.ItemClickEvent) => scheduler.updateAppointment(appointmentData, { - ...appointmentData, - ...{ roomId: [itemData.id] }, - }), - })); - this.target = appointmentClassName; - this.disabled = false; - this.contextMenuItems = [ + + return [ { text: 'Open', - onItemClick: () => scheduler.showAppointmentPopup(appointmentData), + onItemClick: () => scheduler.showAppointmentPopup(appointment), }, { text: 'Delete', - onItemClick: () => scheduler.deleteAppointment(appointmentData), + onItemClick: () => scheduler.deleteAppointment(appointment), }, { text: 'Repeat Weekly', beginGroup: true, - onItemClick: () => scheduler.updateAppointment(appointmentData, { - startDate: targetedAppointmentData.startDate, + onItemClick: () => scheduler.updateAppointment(appointment, { + ...appointment, + startDate: targetedAppointment.startDate, recurrenceRule: 'FREQ=WEEKLY', }), }, - { text: 'Set Room', beginGroup: true, disabled: true }, - ...resourceItems, + { + text: 'Set Room', + beginGroup: true, + disabled: true, + }, + ...this.resourcesData.map((item) => ({ + ...item, + onItemClick: ({ itemData }: DxContextMenuTypes.ItemClickEvent) => scheduler.updateAppointment(appointment, { + ...appointment, + roomId: [itemData.id], + }), + })), ]; } - onCellContextMenu({ cellData }: DxSchedulerTypes.CellContextMenuEvent) { + getCellContextMenuItems(e: DxSchedulerTypes.CellContextMenuEvent): ContextMenuItem[] { + const { cellData } = e; const scheduler = this.scheduler.instance; - this.target = cellClassName; - this.disabled = false; - this.contextMenuItems = [ + + return [ { text: 'New Appointment', onItemClick: () => scheduler.showAppointmentPopup( @@ -117,9 +134,9 @@ export class AppComponent { text: 'Group by Room/Ungroup', beginGroup: true, onItemClick: () => { - if (this.groups) { + if (this.groups.length) { this.crossScrollingEnabled = false; - this.groups = null; + this.groups = []; } else { this.crossScrollingEnabled = true; this.groups = ['roomId']; @@ -134,10 +151,6 @@ export class AppComponent { }, ]; } - - onContextMenuItemClick(e: DxContextMenuTypes.ItemClickEvent) { - e.itemData.onItemClick(e); - } } bootstrapApplication(AppComponent, { diff --git a/apps/demos/Demos/Scheduler/ContextMenu/Angular/app/app.service.ts b/apps/demos/Demos/Scheduler/ContextMenu/Angular/app/app.service.ts index 58ab97226925..c65695a0db3d 100644 --- a/apps/demos/Demos/Scheduler/ContextMenu/Angular/app/app.service.ts +++ b/apps/demos/Demos/Scheduler/ContextMenu/Angular/app/app.service.ts @@ -16,7 +16,7 @@ export interface Resource { color: string; } -export type ContextMenuItem = DxContextMenuTypes.Item & Resource & { +export type ContextMenuItem = DxContextMenuTypes.Item & Partial & { onItemClick?: (e: DxContextMenuTypes.ItemClickEvent) => void }; diff --git a/apps/demos/Demos/Scheduler/ContextMenu/React/App.tsx b/apps/demos/Demos/Scheduler/ContextMenu/React/App.tsx index eaa9085a94db..da48c214a4e8 100644 --- a/apps/demos/Demos/Scheduler/ContextMenu/React/App.tsx +++ b/apps/demos/Demos/Scheduler/ContextMenu/React/App.tsx @@ -1,19 +1,16 @@ import React, { useCallback, useRef, useState } from 'react'; import { Scheduler, Resource } from 'devextreme-react/scheduler'; -import type { SchedulerTypes, SchedulerRef } from 'devextreme-react/scheduler'; +import type { SchedulerRef, SchedulerTypes } from 'devextreme-react/scheduler'; import { ContextMenu } from 'devextreme-react/context-menu'; import type { ContextMenuTypes } from 'devextreme-react/context-menu'; -import AppointmentMenuTemplate from './AppointmentTemplate.tsx'; +import ItemTemplate from './itemTemplate.tsx'; import { data, resourcesData } from './data.ts'; import type { ContextMenuItem } from './types'; const views: SchedulerTypes.ViewType[] = ['day', 'month']; -const appointmentClassName = '.dx-scheduler-appointment'; -const cellClassName = '.dx-scheduler-date-table-cell'; - const onContextMenuItemClick = (e: ContextMenuTypes.ItemClickEvent) => { e.itemData?.onItemClick?.(e); }; @@ -22,39 +19,28 @@ const App = () => { const schedulerRef = useRef(null); const [currentDate, setCurrentDate] = useState(new Date(2020, 10, 25)); const [contextMenuItems, setContextMenuItems] = useState([]); - const [target, setTarget] = useState(appointmentClassName); - const [disabled, setDisabled] = useState(true); - const [groups, setGroups] = useState(undefined); + const [groups, setGroups] = useState([]); const [crossScrollingEnabled, setCrossScrollingEnabled] = useState(false); - const onAppointmentContextMenu = useCallback((event: SchedulerTypes.AppointmentContextMenuEvent) => { - const { appointmentData, targetedAppointmentData } = event; - const scheduler = schedulerRef.current?.instance(); - - const resourceItems: ContextMenuItem[] = resourcesData.map((item) => ({ - ...item, - onItemClick: (e: ContextMenuTypes.ItemClickEvent) => scheduler?.updateAppointment(appointmentData, { - ...appointmentData, - ...{ roomId: [e.itemData?.id] }, - }), - })); + const getAppointmentContextMenuItems = useCallback((e: SchedulerTypes.AppointmentContextMenuEvent) => { + const { appointmentData: appointment, targetedAppointmentData: targetedAppointment } = e; + const scheduler = e.component; - setTarget(appointmentClassName); - setDisabled(false); - setContextMenuItems([ + return [ { text: 'Open', - onItemClick: () => scheduler?.showAppointmentPopup(appointmentData), + onItemClick: () => scheduler?.showAppointmentPopup(appointment), }, { text: 'Delete', - onItemClick: () => scheduler?.deleteAppointment(appointmentData), + onItemClick: () => scheduler?.deleteAppointment(appointment), }, { text: 'Repeat Weekly', beginGroup: true, - onItemClick: () => scheduler?.updateAppointment(appointmentData, { - startDate: targetedAppointmentData?.startDate, + onItemClick: () => scheduler?.updateAppointment(appointment, { + ...appointment, + startDate: targetedAppointment?.startDate, recurrenceRule: 'FREQ=WEEKLY', }), }, @@ -63,40 +49,50 @@ const App = () => { beginGroup: true, disabled: true, }, - ...resourceItems, - ]); + ...resourcesData.map((item) => ({ + ...item, + onItemClick: (clickEvent: ContextMenuTypes.ItemClickEvent) => { + scheduler?.updateAppointment(appointment, { + ...appointment, + roomId: [clickEvent.itemData?.id], + }); + }, + })), + ]; }, []); - const onCellContextMenu = useCallback((e: SchedulerTypes.CellContextMenuEvent) => { - const scheduler = schedulerRef.current?.instance(); + const getCellContextMenuItems = useCallback((e: SchedulerTypes.CellContextMenuEvent) => { + const scheduler = e.component; - setTarget(cellClassName); - setDisabled(false); - setContextMenuItems([ + return [ { text: 'New Appointment', - onItemClick: () => scheduler?.showAppointmentPopup( - { startDate: e.cellData.startDateUTC }, - true, - ), + onItemClick: () => { + scheduler?.showAppointmentPopup( + { startDate: e.cellData.startDateUTC }, + true, + ); + }, }, { text: 'New Recurring Appointment', - onItemClick: () => scheduler?.showAppointmentPopup( - { - startDate: e.cellData.startDateUTC, - recurrenceRule: 'FREQ=DAILY', - }, - true, - ), + onItemClick: () => { + scheduler?.showAppointmentPopup( + { + startDate: e.cellData.startDateUTC, + recurrenceRule: 'FREQ=DAILY', + }, + true, + ); + }, }, { text: 'Group by Room/Ungroup', beginGroup: true, onItemClick: () => { - if (groups) { + if (groups.length) { setCrossScrollingEnabled(false); - setGroups(undefined); + setGroups([]); } else { setCrossScrollingEnabled(true); setGroups(['roomId']); @@ -109,9 +105,23 @@ const App = () => { setCurrentDate(new Date()); }, }, - ]); + ]; }, [groups]); + const onAppointmentContextMenu = useCallback((e: SchedulerTypes.AppointmentContextMenuEvent) => { + const items = getAppointmentContextMenuItems(e); + setContextMenuItems(items); + }, [getAppointmentContextMenuItems]); + + const onCellContextMenu = useCallback((e: SchedulerTypes.CellContextMenuEvent) => { + const items = getCellContextMenuItems(e); + setContextMenuItems(items); + }, [getCellContextMenuItems]); + + const onContextMenuHiding = useCallback(() => { + setContextMenuItems([]); + }, []); + return ( <> { ); diff --git a/apps/demos/Demos/Scheduler/ContextMenu/React/index.html b/apps/demos/Demos/Scheduler/ContextMenu/React/index.html index cfb9b3aa6769..ee451f8288ff 100644 --- a/apps/demos/Demos/Scheduler/ContextMenu/React/index.html +++ b/apps/demos/Demos/Scheduler/ContextMenu/React/index.html @@ -6,6 +6,7 @@ + diff --git a/apps/demos/Demos/Scheduler/ContextMenu/React/AppointmentTemplate.tsx b/apps/demos/Demos/Scheduler/ContextMenu/React/itemTemplate.tsx similarity index 59% rename from apps/demos/Demos/Scheduler/ContextMenu/React/AppointmentTemplate.tsx rename to apps/demos/Demos/Scheduler/ContextMenu/React/itemTemplate.tsx index f4278dc0da98..ce704b4a2f01 100644 --- a/apps/demos/Demos/Scheduler/ContextMenu/React/AppointmentTemplate.tsx +++ b/apps/demos/Demos/Scheduler/ContextMenu/React/itemTemplate.tsx @@ -1,17 +1,17 @@ import React from 'react'; -type AppointmentMenuTemplateProps = { +type ItemTemplateProps = { data: { color: string; text: string; } }; -const AppointmentMenuTemplate = (props: AppointmentMenuTemplateProps) => ( +const ItemTemplate = (props: ItemTemplateProps) => (
{props.data.color &&
} {props.data.text}
); -export default AppointmentMenuTemplate; +export default ItemTemplate; diff --git a/apps/demos/Demos/Scheduler/ContextMenu/React/styles.css b/apps/demos/Demos/Scheduler/ContextMenu/React/styles.css index 7d52d89364e2..41fb881541dd 100644 --- a/apps/demos/Demos/Scheduler/ContextMenu/React/styles.css +++ b/apps/demos/Demos/Scheduler/ContextMenu/React/styles.css @@ -1,13 +1,3 @@ -.dx-menu-item-content span { - margin-right: 5px; -} - -.dx-menu-item-has-submenu .dx-icon-spinright { - position: absolute; - top: 7px; - right: 2px; -} - .item-badge { text-align: center; float: left; diff --git a/apps/demos/Demos/Scheduler/ContextMenu/React/types.ts b/apps/demos/Demos/Scheduler/ContextMenu/React/types.ts index 854fba22f8c6..f526dc20a468 100644 --- a/apps/demos/Demos/Scheduler/ContextMenu/React/types.ts +++ b/apps/demos/Demos/Scheduler/ContextMenu/React/types.ts @@ -9,4 +9,4 @@ export type Resource = { color?: string; }; -export type ContextMenuItem = ContextMenuTypes.Item & Resource & { onItemClick?: (e: ContextMenuTypes.ItemClickEvent) => void }; +export type ContextMenuItem = ContextMenuTypes.Item & Partial & { onItemClick?: (e: ContextMenuTypes.ItemClickEvent) => void }; diff --git a/apps/demos/Demos/Scheduler/ContextMenu/ReactJs/App.js b/apps/demos/Demos/Scheduler/ContextMenu/ReactJs/App.js index 2052f22053ec..3603761cf4ce 100644 --- a/apps/demos/Demos/Scheduler/ContextMenu/ReactJs/App.js +++ b/apps/demos/Demos/Scheduler/ContextMenu/ReactJs/App.js @@ -1,12 +1,10 @@ import React, { useCallback, useRef, useState } from 'react'; import { Scheduler, Resource } from 'devextreme-react/scheduler'; import { ContextMenu } from 'devextreme-react/context-menu'; -import AppointmentMenuTemplate from './AppointmentTemplate.js'; +import ItemTemplate from './itemTemplate.js'; import { data, resourcesData } from './data.js'; const views = ['day', 'month']; -const appointmentClassName = '.dx-scheduler-appointment'; -const cellClassName = '.dx-scheduler-date-table-cell'; const onContextMenuItemClick = (e) => { e.itemData?.onItemClick?.(e); }; @@ -14,38 +12,27 @@ const App = () => { const schedulerRef = useRef(null); const [currentDate, setCurrentDate] = useState(new Date(2020, 10, 25)); const [contextMenuItems, setContextMenuItems] = useState([]); - const [target, setTarget] = useState(appointmentClassName); - const [disabled, setDisabled] = useState(true); - const [groups, setGroups] = useState(undefined); + const [groups, setGroups] = useState([]); const [crossScrollingEnabled, setCrossScrollingEnabled] = useState(false); - const onAppointmentContextMenu = useCallback((event) => { - const { appointmentData, targetedAppointmentData } = event; - const scheduler = schedulerRef.current?.instance(); - const resourceItems = resourcesData.map((item) => ({ - ...item, - onItemClick: (e) => - scheduler?.updateAppointment(appointmentData, { - ...appointmentData, - ...{ roomId: [e.itemData?.id] }, - }), - })); - setTarget(appointmentClassName); - setDisabled(false); - setContextMenuItems([ + const getAppointmentContextMenuItems = useCallback((e) => { + const { appointmentData: appointment, targetedAppointmentData: targetedAppointment } = e; + const scheduler = e.component; + return [ { text: 'Open', - onItemClick: () => scheduler?.showAppointmentPopup(appointmentData), + onItemClick: () => scheduler?.showAppointmentPopup(appointment), }, { text: 'Delete', - onItemClick: () => scheduler?.deleteAppointment(appointmentData), + onItemClick: () => scheduler?.deleteAppointment(appointment), }, { text: 'Repeat Weekly', beginGroup: true, onItemClick: () => - scheduler?.updateAppointment(appointmentData, { - startDate: targetedAppointmentData?.startDate, + scheduler?.updateAppointment(appointment, { + ...appointment, + startDate: targetedAppointment?.startDate, recurrenceRule: 'FREQ=WEEKLY', }), }, @@ -54,38 +41,46 @@ const App = () => { beginGroup: true, disabled: true, }, - ...resourceItems, - ]); + ...resourcesData.map((item) => ({ + ...item, + onItemClick: (clickEvent) => { + scheduler?.updateAppointment(appointment, { + ...appointment, + roomId: [clickEvent.itemData?.id], + }); + }, + })), + ]; }, []); - const onCellContextMenu = useCallback( + const getCellContextMenuItems = useCallback( (e) => { - const scheduler = schedulerRef.current?.instance(); - setTarget(cellClassName); - setDisabled(false); - setContextMenuItems([ + const scheduler = e.component; + return [ { text: 'New Appointment', - onItemClick: () => - scheduler?.showAppointmentPopup({ startDate: e.cellData.startDateUTC }, true), + onItemClick: () => { + scheduler?.showAppointmentPopup({ startDate: e.cellData.startDateUTC }, true); + }, }, { text: 'New Recurring Appointment', - onItemClick: () => + onItemClick: () => { scheduler?.showAppointmentPopup( { startDate: e.cellData.startDateUTC, recurrenceRule: 'FREQ=DAILY', }, true, - ), + ); + }, }, { text: 'Group by Room/Ungroup', beginGroup: true, onItemClick: () => { - if (groups) { + if (groups.length) { setCrossScrollingEnabled(false); - setGroups(undefined); + setGroups([]); } else { setCrossScrollingEnabled(true); setGroups(['roomId']); @@ -98,10 +93,27 @@ const App = () => { setCurrentDate(new Date()); }, }, - ]); + ]; }, [groups], ); + const onAppointmentContextMenu = useCallback( + (e) => { + const items = getAppointmentContextMenuItems(e); + setContextMenuItems(items); + }, + [getAppointmentContextMenuItems], + ); + const onCellContextMenu = useCallback( + (e) => { + const items = getCellContextMenuItems(e); + setContextMenuItems(items); + }, + [getCellContextMenuItems], + ); + const onContextMenuHiding = useCallback(() => { + setContextMenuItems([]); + }, []); return ( <> { ); diff --git a/apps/demos/Demos/Scheduler/ContextMenu/ReactJs/index.html b/apps/demos/Demos/Scheduler/ContextMenu/ReactJs/index.html index d6d83f735a76..db31b0fd60c6 100644 --- a/apps/demos/Demos/Scheduler/ContextMenu/ReactJs/index.html +++ b/apps/demos/Demos/Scheduler/ContextMenu/ReactJs/index.html @@ -19,6 +19,11 @@ type="text/css" href="../../../../node_modules/devextreme-dist/css/dx.light.css" /> + diff --git a/apps/demos/Demos/Scheduler/ContextMenu/ReactJs/AppointmentTemplate.js b/apps/demos/Demos/Scheduler/ContextMenu/ReactJs/itemTemplate.js similarity index 71% rename from apps/demos/Demos/Scheduler/ContextMenu/ReactJs/AppointmentTemplate.js rename to apps/demos/Demos/Scheduler/ContextMenu/ReactJs/itemTemplate.js index ff6707af3299..31eaeb9417d8 100644 --- a/apps/demos/Demos/Scheduler/ContextMenu/ReactJs/AppointmentTemplate.js +++ b/apps/demos/Demos/Scheduler/ContextMenu/ReactJs/itemTemplate.js @@ -1,6 +1,6 @@ import React from 'react'; -const AppointmentMenuTemplate = (props) => ( +const ItemTemplate = (props) => (
{props.data.color && (
( {props.data.text}
); -export default AppointmentMenuTemplate; +export default ItemTemplate; diff --git a/apps/demos/Demos/Scheduler/ContextMenu/ReactJs/styles.css b/apps/demos/Demos/Scheduler/ContextMenu/ReactJs/styles.css index 7d52d89364e2..41fb881541dd 100644 --- a/apps/demos/Demos/Scheduler/ContextMenu/ReactJs/styles.css +++ b/apps/demos/Demos/Scheduler/ContextMenu/ReactJs/styles.css @@ -1,13 +1,3 @@ -.dx-menu-item-content span { - margin-right: 5px; -} - -.dx-menu-item-has-submenu .dx-icon-spinright { - position: absolute; - top: 7px; - right: 2px; -} - .item-badge { text-align: center; float: left; diff --git a/apps/demos/Demos/Scheduler/ContextMenu/Vue/App.vue b/apps/demos/Demos/Scheduler/ContextMenu/Vue/App.vue index ed78632e61ec..144de8b2a47e 100644 --- a/apps/demos/Demos/Scheduler/ContextMenu/Vue/App.vue +++ b/apps/demos/Demos/Scheduler/ContextMenu/Vue/App.vue @@ -1,7 +1,6 @@