import 'bootstrap.css';
import '@mescius/wijmo.styles/wijmo.css';
import './styles.css';
//
import * as wjcCore from '@mescius/wijmo';
import * as wjcScheduler from '@mescius/wijmo.scheduler';
import { getDisplayDate, getEvents, getTeams } from './data';
//
const CustomMenuAction = {
FocusBlock: 100,
DuplicateNextDay: 101,
TogglePriority: 102,
};
class BusinessSchedulerMenu extends wjcScheduler.SchedulerMenu {
constructor(element, scheduler) {
super(element, scheduler);
this.targetCell = null;
this.targetItem = null;
this.control = scheduler;
this.itemsSource = [
{ text: 'Add event...', action: wjcScheduler.SchedulerMenuAction.Add, icon: 'bi bi-calendar-plus' },
{ text: 'Create focus block', action: CustomMenuAction.FocusBlock, icon: 'bi bi-bullseye' },
{ text: 'Edit event...', action: wjcScheduler.SchedulerMenuAction.Edit, icon: 'bi bi-pencil-square' },
{ text: 'Duplicate to next day', action: CustomMenuAction.DuplicateNextDay, icon: 'bi bi-copy' },
{ text: 'Toggle priority', action: CustomMenuAction.TogglePriority, icon: 'bi bi-star' },
{ text: 'Delete event', action: wjcScheduler.SchedulerMenuAction.Delete, icon: 'bi bi-trash3' },
{ text: 'Go to today', action: wjcScheduler.SchedulerMenuAction.Today, icon: 'bi bi-calendar2-day' },
];
}
show(position) {
const hit = this.control.hitTest(position);
this.targetCell =
hit.hitTestType === wjcScheduler.SchedulerHitTestType.Cell
? hit.getHitInfo()
: null;
this.targetItem =
hit.hitTestType === wjcScheduler.SchedulerHitTestType.Item
? hit.getHitInfo()
: null;
super.show(position);
}
canExecute(action) {
const actionValue = action;
if (actionValue === CustomMenuAction.FocusBlock)
return !!this.targetCell;
if (actionValue === CustomMenuAction.DuplicateNextDay || actionValue === CustomMenuAction.TogglePriority)
return !!this.targetItem;
return super.canExecute(action);
}
executeContextMenuAction(action) {
var _a;
const actionValue = action;
const sourceItem = (_a = this.targetItem) === null || _a === void 0 ? void 0 : _a.item;
if (actionValue === CustomMenuAction.FocusBlock && this.targetCell) {
const start = this.targetCell.start;
this.control.create({
id: getNextId(this.control),
subject: 'Focus block',
start,
end: new Date(start.getTime() + 30 * 60000),
teamId: this.targetCell.resources.teamId || 'product',
priority: true,
});
return;
}
if (actionValue === CustomMenuAction.DuplicateNextDay && sourceItem) {
this.control.create(Object.assign(Object.assign({}, sourceItem), { id: getNextId(this.control), subject: `${sourceItem.subject} copy`, start: addDays(sourceItem.start, 1), end: addDays(sourceItem.end, 1) }));
return;
}
if (actionValue === CustomMenuAction.TogglePriority && sourceItem) {
this.control.update(sourceItem, { priority: !sourceItem.priority });
return;
}
super.executeContextMenuAction(action);
}
}
document.readyState === 'loading' ? document.addEventListener('DOMContentLoaded', init) : init();
function init() {
const displayDate = getDisplayDate();
const events = new wjcCore.CollectionView(getEvents());
const scheduler = new wjcScheduler.FlexScheduler('#theScheduler', {
itemsSource: events,
resources: [
{
fieldExpr: 'teamId',
label: 'Team',
dataSource: getTeams(),
allowMultiple: false,
valueExpr: 'id',
displayExpr: 'name',
backgroundColorExpr: 'backgroundColor',
foreColorExpr: 'foreColor',
},
],
definition: getWorkWeekDefinition(renderEvent),
displayDate,
workHours: [{ start: '08:00', end: '18:00' }],
toolbarViewItems: [{ name: 'Work Week', key: wjcScheduler.SchedulerBuiltInViewItem.WorkWeek }],
});
const contextMenu = new BusinessSchedulerMenu('#schedulerMenu', scheduler);
contextMenu.dropDownCssClass += ' sample-context-menu';
scheduler.contextMenu = contextMenu;
scheduler.itemChanged.addHandler((_sender, args) => {
const item = args.result && args.result.item;
setStatus(`${getActionText(args.request.action)}${(item === null || item === void 0 ? void 0 : item.subject) ? ` "${item.subject}"` : ''}. ${events.sourceCollection.length} events.`);
});
scheduler.displayDateChanged.addHandler(() => setStatus(`Viewing ${wjcCore.Globalize.format(scheduler.displayDate, 'MMMM d, yyyy')}.`));
document.querySelector('#sampleWeekButton').addEventListener('click', () => {
scheduler.displayDate = displayDate;
scheduler.focus();
});
}
function renderEvent(ctx, element) {
const item = ctx.item;
const icon = document.createElement('i');
icon.className = `bi ${item.priority ? 'bi-star-fill' : 'bi-calendar-event'}`;
icon.setAttribute('aria-hidden', 'true');
element.textContent = '';
element.append(icon, document.createTextNode(item.subject || 'Untitled event'));
element.classList.toggle('priority-event', !!item.priority);
return null;
}
function getNextId(scheduler) {
return scheduler.queryEvents(() => true).reduce((max, item) => Math.max(max, item.id), 0) + 1;
}
function addDays(value, days) {
const result = new Date(value);
result.setDate(result.getDate() + days);
return result;
}
function getWorkWeekDefinition(itemTemplate) {
const definition = wjcScheduler.createSchedulerDefinitionPreset(wjcScheduler.SchedulerBuiltInViewItem.WorkWeek);
return Object.assign(Object.assign({}, definition), { itemTemplate, rows: {
bindings: [
{
type: wjcScheduler.SchedulerAxisBindingType.Time,
unit: wjcScheduler.SchedulerTimeUnit.Hour,
visible: (args) => args.hour >= 8 && args.hour < 18,
},
],
} });
}
function getActionText(action) {
switch (action) {
case wjcScheduler.SchedulerItemChangeAction.Create:
return 'Created';
case wjcScheduler.SchedulerItemChangeAction.Update:
return 'Updated';
case wjcScheduler.SchedulerItemChangeAction.Remove:
return 'Deleted';
default:
return 'Changed';
}
}
function setStatus(message) {
document.querySelector('#menuStatus').textContent = message;
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>MESCIUS Wijmo Scheduler Context Menu</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link href="https://cdn.jsdelivr.net/npm/bootstrap-icons/font/bootstrap-icons.css" rel="stylesheet">
<!-- SystemJS -->
<script src="https://cdn.jsdelivr.net/npm/systemjs@0.21.5/dist/system.src.js" integrity="sha512-skZbMyvYdNoZfLmiGn5ii6KmklM82rYX2uWctBhzaXPxJgiv4XBwJnFGr5k8s+6tE1pcR1nuTKghozJHyzMcoA==" crossorigin="anonymous"></script>
<script src="systemjs.config.js"></script>
<script>
System.import('./src/app');
</script>
</head>
<body>
<main class="container-fluid">
<div class="sample-bar">
<p id="menuStatus" class="menu-status" role="status" aria-live="polite"><i class="bi bi-mouse2" aria-hidden="true"></i> Right-click a slot or event for built-in and business-specific actions.</p>
<button id="sampleWeekButton" class="btn btn-sm btn-outline-secondary" type="button"><i class="bi bi-calendar-week" aria-hidden="true"></i> Sample week</button>
</div>
<div id="theScheduler"></div>
<div id="schedulerMenu" aria-label="Scheduler context menu"></div>
</main>
</body>
</html>
function createDate(day, hour, minute = 0) {
return new Date(2026, 4, day, hour, minute);
}
export function getDisplayDate() {
return createDate(11, 9);
}
export function getTeams() {
return [
{ id: 'product', name: 'Product', backgroundColor: '#1769aa', foreColor: '#ffffff' },
{ id: 'design', name: 'Design', backgroundColor: '#7b3f98', foreColor: '#ffffff' },
{ id: 'success', name: 'Customer Success', backgroundColor: '#087f5b', foreColor: '#ffffff' },
{ id: 'operations', name: 'Operations', backgroundColor: '#a44a19', foreColor: '#ffffff' },
];
}
export function getEvents() {
return [
{
id: 1,
subject: 'Sprint Planning',
start: createDate(11, 9),
end: createDate(11, 10, 30),
teamId: 'product',
priority: true,
},
{ id: 2, subject: 'Design Critique', start: createDate(11, 13), end: createDate(11, 14), teamId: 'design' },
{
id: 3,
subject: 'Customer Workshop',
start: createDate(12, 10),
end: createDate(12, 11, 30),
teamId: 'success',
},
{ id: 4, subject: 'Capacity Review', start: createDate(12, 14), end: createDate(12, 15), teamId: 'operations' },
{ id: 5, subject: 'Roadmap Checkpoint', start: createDate(13, 11), end: createDate(13, 12), teamId: 'product' },
{
id: 6,
subject: 'Research Playback',
start: createDate(14, 9, 30),
end: createDate(14, 10, 30),
teamId: 'design',
},
{ id: 7, subject: 'Renewal Review', start: createDate(14, 14), end: createDate(14, 15), teamId: 'success' },
{
id: 8,
subject: 'Release Readiness',
start: createDate(15, 10),
end: createDate(15, 11, 30),
teamId: 'operations',
},
];
}
body {
color: #1f2937;
font-size: 15px;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif;
user-select: none;
margin: 0;
}
.container-fluid {
box-sizing: border-box;
margin: 0;
padding: 5px;
}
.sample-bar {
display: flex;
min-height: 44px;
margin-bottom: 6px;
padding: 5px 9px;
border: 1px solid #dbe2ea;
border-left: 3px solid #0f6cbd;
background: #f8fafc;
align-items: center;
justify-content: flex-start;
gap: 8px;
}
.menu-status {
min-width: 0;
margin: 0;
color: #334155;
font-size: 14px;
line-height: 20px;
}
#sampleWeekButton {
flex: 0 0 auto;
}
.btn:focus-visible {
border-color: #0f6cbd;
box-shadow: 0 0 0 2px rgb(15 108 189 / 22%);
}
#schedulerMenu {
display: none;
}
#theScheduler {
height: calc(100vh - 50px);
min-height: 560px;
}
.sample-context-menu {
min-width: 220px;
border-color: #94a3b8;
box-shadow: 0 4px 12px rgb(15 23 42 / 18%);
}
.sample-context-menu .bi {
width: 18px;
margin-right: 8px;
color: #526170;
text-align: center;
}
.priority-event {
box-shadow: inset 3px 0 #fbbf24;
}
.priority-event > .bi {
margin-right: 8px;
color: #fde68a;
}
.wj-scheduler-item > .bi {
margin-right: 8px;
}
@media (max-width: 768px) {
.sample-bar {
align-items: flex-start;
}
.menu-status {
overflow-wrap: anywhere;
}
#theScheduler {
height: 790px;
min-height: 680px;
}
}
input,
textarea,
[contenteditable="true"] {
user-select: text;
}
(function (global) {
System.config({
transpiler: 'plugin-babel',
babelOptions: {
es2015: true,
},
meta: {
'*.css': { loader: 'css' },
},
paths: {
// paths serve as alias
'npm:': 'node_modules/',
'cdn:': 'https://cdn.mescius.io/demoapps/packages/wijmojs/5.20262.60-rc/',
'pcdn:': 'https://cdn.jsdelivr.net/npm/',
},
// map tells the System loader where to look for things
map: {
'@mescius/wijmo': 'cdn:@mescius/wijmo/index.js',
'@mescius/wijmo.input': 'cdn:@mescius/wijmo.input/index.js',
'@mescius/wijmo.scheduler': 'cdn:@mescius/wijmo.scheduler/index.js',
'@mescius/wijmo.styles': 'cdn:@mescius/wijmo.styles',
'@mescius/wijmo.cultures': 'cdn:@mescius/wijmo.cultures',
jszip: 'pcdn:jszip@3.8.0/dist/jszip.js',
'bootstrap.css': 'pcdn:bootstrap@5.3.8/dist/css/bootstrap.min.css',
css: 'pcdn:systemjs-plugin-css@0.1.37/css.js',
'plugin-babel': 'pcdn:systemjs-plugin-babel@0.0.25/plugin-babel.js',
'systemjs-babel-build': 'pcdn:systemjs-plugin-babel@0.0.25/systemjs-babel-browser.js',
},
packages: {
src: {
defaultExtension: 'js',
},
node_modules: {
defaultExtension: 'js',
},
},
});
})(this);