import 'bootstrap.css';
import '@mescius/wijmo.styles/wijmo.css';
import './styles.css';
//
import { CollectionView } from '@mescius/wijmo';
import * as wjcScheduler from '@mescius/wijmo.scheduler';
import { getDisplayDate, getEvents, getPriorities, getRegions, getServiceTypes, getSpecialties } from './data';
document.readyState === 'loading' ? document.addEventListener('DOMContentLoaded', init) : init();
function init() {
const scheduler = new wjcScheduler.FlexScheduler('#theScheduler', {
itemsSource: new CollectionView(getEvents()),
resources: [
{
fieldExpr: 'regionId',
label: 'Region',
dataSource: getRegions(),
valueExpr: 'id',
displayExpr: 'name',
backgroundColorExpr: 'color',
foreColorExpr: 'textColor',
},
{
fieldExpr: 'specialtyId',
label: 'Specialty',
dataSource: getSpecialties(),
valueExpr: 'id',
displayExpr: 'name',
},
{
fieldExpr: 'serviceTypeId',
label: 'Service type',
dataSource: getServiceTypes(),
valueExpr: 'id',
displayExpr: 'name',
backgroundColorExpr: 'color',
foreColorExpr: 'textColor',
},
{
fieldExpr: 'priorityId',
label: 'Priority',
dataSource: getPriorities(),
valueExpr: 'id',
displayExpr: 'name',
},
],
definition: getDefinition('columns'),
displayDate: getDisplayDate(),
workHours: [{ start: '08:00', end: '18:00' }],
toolbarViewItems: [{ name: 'Day', key: wjcScheduler.SchedulerBuiltInViewItem.Day }],
});
document.querySelectorAll('[data-grouping-mode]').forEach((button) => {
button.addEventListener('click', () => {
const mode = button.dataset.groupingMode;
scheduler.definition = getDefinition(mode);
scheduler.hostElement.classList.toggle('row-groups-mode', mode === 'rows');
document.querySelectorAll('[data-grouping-mode]').forEach((candidate) => {
const selected = candidate === button;
candidate.setAttribute('aria-pressed', selected.toString());
});
const description = document.querySelector('#groupingDescription');
description.textContent = getModeDescription(mode);
updateBindingOrder(mode);
});
});
}
function updateBindingOrder(mode) {
const rowBinding = document.querySelector('#rowBinding');
const columnBinding = document.querySelector('#columnBinding');
rowBinding.textContent = mode === 'columns' ? 'Rows: Time' : 'Rows: Region > Specialty';
columnBinding.textContent = mode === 'rows' ? 'Columns: Time' : 'Columns: Service type > Priority';
}
function getDefinition(mode) {
const timeBinding = {
type: wjcScheduler.SchedulerAxisBindingType.Time,
unit: wjcScheduler.SchedulerTimeUnit.Hour,
visible: (args) => args.hour >= 8 && args.hour < 18,
};
const rowRegionBinding = { type: wjcScheduler.SchedulerAxisBindingType.Resource, fieldExpr: 'regionId' };
const rowSpecialtyBinding = {
type: wjcScheduler.SchedulerAxisBindingType.Resource,
fieldExpr: 'specialtyId',
};
const columnServiceTypeBinding = {
type: wjcScheduler.SchedulerAxisBindingType.Resource,
fieldExpr: 'serviceTypeId',
};
const columnPriorityBinding = {
type: wjcScheduler.SchedulerAxisBindingType.Resource,
fieldExpr: 'priorityId',
};
return {
key: `multiLevelGrouping-${mode}`,
rows: {
bindings: mode === 'columns'
? [timeBinding]
: mode === 'rows'
? [rowRegionBinding, rowSpecialtyBinding]
: [rowRegionBinding, rowSpecialtyBinding, timeBinding],
},
columns: {
bindings: mode === 'rows' ? [timeBinding] : [columnServiceTypeBinding, columnPriorityBinding],
},
cellSemantic: wjcScheduler.SchedulerCellSemantic.Timed,
};
}
function getModeDescription(mode) {
if (mode === 'rows')
return 'Rows expand by region, then specialty; hours run across columns.';
if (mode === 'both')
return 'Rows use region > specialty; columns use service type > priority.';
return 'Columns expand by service type, then priority.';
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>MESCIUS Wijmo Scheduler Multi-level Grouping</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">
<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="grouping-toolbar">
<div class="grouping-copy">
<strong><i class="bi bi-diagram-3" aria-hidden="true"></i> Regional service board</strong>
<span id="groupingDescription">Columns expand by service type, then priority.</span>
</div>
<div class="grouping-switch" role="group" aria-label="Resource grouping mode">
<button type="button" data-grouping-mode="columns" aria-pressed="true">Column groups</button>
<button type="button" data-grouping-mode="rows" aria-pressed="false">Row groups</button>
<button type="button" data-grouping-mode="both" aria-pressed="false">Rows + columns</button>
</div>
<div class="binding-order" aria-label="Resource binding order">
<span id="rowBinding">Rows: Time</span>
<span id="columnBinding">Columns: Service type > Priority</span>
</div>
</div>
<div id="theScheduler"></div>
</main>
</body>
</html>
function date(hour, minute = 0) {
return new Date(2026, 4, 13, hour, minute);
}
export function getDisplayDate() {
return date(9);
}
export function getRegions() {
return [
{ id: 'east', name: 'East region', color: '#2563eb', textColor: '#ffffff' },
{ id: 'west', name: 'West region', color: '#0f766e', textColor: '#ffffff' },
];
}
export function getSpecialties() {
return [
{ id: 'installation', name: 'Installation' },
{ id: 'support', name: 'Support' },
];
}
export function getServiceTypes() {
return [
{ id: 'onsite', name: 'On-site', color: '#2563eb', textColor: '#ffffff' },
{ id: 'remote', name: 'Remote', color: '#0f766e', textColor: '#ffffff' },
{ id: 'escalation', name: 'Escalation', color: '#b45309', textColor: '#ffffff' },
];
}
export function getPriorities() {
return [
{ id: 'standard', name: 'Standard' },
{ id: 'urgent', name: 'Urgent' },
];
}
export function getEvents() {
return [
{
id: 1,
subject: 'Branch Rollout',
start: date(8, 30),
end: date(10),
regionId: 'east',
specialtyId: 'installation',
serviceTypeId: 'onsite',
priorityId: 'standard',
},
{
id: 2,
subject: 'Connectivity Audit',
start: date(10),
end: date(11, 30),
regionId: 'east',
specialtyId: 'support',
serviceTypeId: 'remote',
priorityId: 'standard',
},
{
id: 3,
subject: 'Site Commissioning',
start: date(11),
end: date(12, 30),
regionId: 'west',
specialtyId: 'installation',
serviceTypeId: 'onsite',
priorityId: 'urgent',
},
{
id: 4,
subject: 'Priority Repair',
start: date(9),
end: date(10, 30),
regionId: 'west',
specialtyId: 'support',
serviceTypeId: 'escalation',
priorityId: 'urgent',
},
{
id: 5,
subject: 'Equipment Upgrade',
start: date(13),
end: date(14, 30),
regionId: 'east',
specialtyId: 'installation',
serviceTypeId: 'onsite',
priorityId: 'urgent',
},
{
id: 6,
subject: 'Customer Handoff',
start: date(14),
end: date(15),
regionId: 'east',
specialtyId: 'support',
serviceTypeId: 'remote',
priorityId: 'standard',
},
{
id: 7,
subject: 'Access Point Setup',
start: date(15),
end: date(16, 30),
regionId: 'west',
specialtyId: 'installation',
serviceTypeId: 'onsite',
priorityId: 'standard',
},
{
id: 8,
subject: 'Service Validation',
start: date(16),
end: date(17),
regionId: 'west',
specialtyId: 'support',
serviceTypeId: 'escalation',
priorityId: 'urgent',
},
];
}
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;
}
.grouping-toolbar {
display: flex;
align-items: center;
justify-content: space-between;
gap: 12px;
min-height: 54px;
margin-bottom: 6px;
padding: 6px 10px;
border: 1px solid #dbe2ea;
background: #f8fafc;
}
.grouping-copy {
display: flex;
flex-direction: column;
gap: 2px;
}
.grouping-copy strong {
display: flex;
align-items: center;
gap: 7px;
}
.grouping-copy span {
color: #64748b;
font-size: 12px;
}
.binding-order {
display: flex;
align-items: center;
gap: 7px;
color: #475569;
font-size: 12px;
}
.grouping-switch {
display: inline-flex;
overflow: hidden;
border: 1px solid #64748b;
border-radius: 4px;
}
.grouping-switch button {
min-height: 32px;
padding: 4px 10px;
border: 0;
border-right: 1px solid #64748b;
background: #ffffff;
color: #334155;
font: inherit;
font-size: 12px;
}
.grouping-switch button:last-child {
border-right: 0;
}
.grouping-switch button[aria-pressed="true"] {
background: #0f6cbd;
color: #ffffff;
}
.grouping-switch button:focus-visible {
outline: 2px solid #0f6cbd;
outline-offset: -3px;
}
.binding-order span {
padding: 4px 8px;
border: 1px solid #cbd5e1;
border-radius: 4px;
background: #ffffff;
font-weight: 600;
}
#theScheduler {
height: calc(100vh - 62px);
min-height: 600px;
}
.row-groups-mode {
width: min(900px, 100%);
height: 380px !important;
min-height: 380px !important;
}
@media (max-width: 640px) {
.row-groups-mode .wj-scheduler-timed-horizontal-header-frame,
.row-groups-mode .wj-scheduler-timed-section {
width: 600px !important;
}
}
@media (max-width: 640px) {
.grouping-toolbar {
align-items: flex-start;
flex-direction: column;
}
.grouping-switch {
width: 100%;
}
.grouping-switch button {
flex: 1 1 0;
}
#theScheduler {
height: 680px;
min-height: 680px;
}
.row-groups-mode {
width: 100%;
height: 430px !important;
min-height: 430px !important;
}
}
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);