Agent-readable demo data

Title
Links with Sticky Parents (JavaScript Scheduler) | DayPilot Pro for JavaScript Demo
Tree
DayPilot JavaScript Demo
Catalog root
Default
Description
Each customer order is a sticky parent. Its links identify the three workshop bookings that fulfil the order. Scroll through the orders: the parent stays visible until the next group arrives. Matching colors identify related bookings.

Inline demo script 1

/* global DayPilot */
    /* eslint-disable no-use-before-define -- Callbacks run after app initialization. */
    const dp = new DayPilot.Scheduler("dp", {
        startDate: "2027-10-17",
        days: 15,
        scale: "Day",
        timeHeaders: [{groupBy: "Month", format: "MMMM yyyy"}, {groupBy: "Day", format: "ddd d"}],
        cellWidthSpec: "Auto",
        cellWidthMin: 65,
        rowHeaderWidth: 215,
        heightSpec: "Fixed",
        height: 370,
        eventHeight: 56,
        rowMarginTop: 2,
        rowMarginBottom: 2,
        durationBarVisible: false,
        treeEnabled: true,
        treePreventParentUsage: false,
        stickyParents: true,
        linkStickyParentsEnabled: true,
        linkShape: "RightAngled",
        timeRangeSelectedHandling: "Disabled",
        eventDeleteHandling: "Disabled",
        onBeforeEventRender: args => {
            const order = app.orders.find(item => item.id === args.data.tags.order);
            const parent = args.data.resource === order.id;
            args.data.backColor = parent ? order.color : order.pale;
            args.data.borderColor = order.color;
            args.data.fontColor = parent ? "#ffffff" : "#20384d";
        },
        onLinkClick: args => {
            const booking = dp.events.find(args.link.data.to);
            const order = app.orders.find(item => item.id === booking.data.tags.order);
            document.querySelector("#relation").textContent = `${booking.text()} fulfils ${order.name}: ${order.product}.`;
        }
    });
    const app = {
        orders: [
            {id: "n204", name: "N204 · North Library", product: "24 reading desks", start: 0, end: 9, color: "#286aa6", pale: "#e4effa",
                jobs: [{name: "Panel cutting", text: "24 desktops", start: 1, end: 3}, {name: "Frame welding", text: "24 frames", start: 3, end: 5}, {name: "Assembly", text: "24 desks", start: 6, end: 9}]},
            {id: "r118", name: "R118 · Riverside Hotel", product: "18 café tables", start: 1, end: 11, color: "#168075", pale: "#e1f2ef",
                jobs: [{name: "Panel cutting", text: "18 tabletops", start: 3, end: 5}, {name: "Metalwork", text: "18 table bases", start: 4, end: 7}, {name: "Assembly", text: "18 café tables", start: 8, end: 11}]},
            {id: "s306", name: "S306 · South School", product: "32 study chairs", start: 2, end: 12, color: "#a66520", pale: "#fbefdf",
                jobs: [{name: "Seat shaping", text: "32 seats", start: 3, end: 5}, {name: "Frame welding", text: "32 chair frames", start: 6, end: 8}, {name: "Upholstery", text: "32 study chairs", start: 9, end: 12}]},
            {id: "w072", name: "W072 · West Gallery", product: "12 display benches", start: 0, end: 13, color: "#7a59a5", pale: "#f0eafa",
                jobs: [{name: "Timber preparation", text: "12 bench tops", start: 2, end: 5}, {name: "Joinery", text: "12 bases", start: 7, end: 10}, {name: "Finishing", text: "12 display benches", start: 10, end: 13}]},
            {id: "e510", name: "E510 · East Clinic", product: "16 storage cabinets", start: 1, end: 12, color: "#587437", pale: "#edf3e4",
                jobs: [{name: "Panel cutting", text: "16 cabinet kits", start: 2, end: 4}, {name: "Case assembly", text: "16 cabinets", start: 5, end: 8}, {name: "Fitting", text: "Doors and hardware", start: 9, end: 12}]},
            {id: "c091", name: "C091 · City Hall", product: "8 reception units", start: 2, end: 13, color: "#9b5570", pale: "#f7e9ef",
                jobs: [{name: "Timber preparation", text: "8 unit kits", start: 3, end: 6}, {name: "Joinery", text: "8 reception units", start: 7, end: 10}, {name: "Finishing", text: "Finish and inspect", start: 11, end: 13}]}
        ],
        init() {
            this.buildData();
            dp.init();
            dp.update({resources: this.resources, events: this.events, links: this.links});
            document.querySelector("#shape").addEventListener("change", ev => dp.update({linkShape: ev.target.value}));
            document.querySelector("#colors").addEventListener("change", ev => dp.update({links: this.links.map(link => ({...link, color: ev.target.checked ? link.color : "#697787"}))}));
            document.querySelector("#follow").addEventListener("change", ev => dp.update({linkStickyParentsEnabled: ev.target.checked}));
        },
        at(days) {
            return new DayPilot.Date("2027-10-18").addDays(days);
        },
        buildData() {
            this.resources = [];
            this.events = [];
            this.links = [];
            this.orders.forEach(order => {
                const children = order.jobs.map((job, index) => ({id: `${order.id}-${index}`, name: job.name}));
                this.resources.push({id: order.id, name: order.name, expanded: true, children});
                this.events.push({id: order.id, resource: order.id, text: `${order.name} · ${order.product}`, start: this.at(order.start), end: this.at(order.end), tags: {order: order.id}});
                order.jobs.forEach((job, index) => {
                    const id = `${order.id}-${index}`;
                    this.events.push({id, resource: id, text: job.text, start: this.at(job.start), end: this.at(job.end), tags: {order: order.id}});
                    this.links.push({id: `fulfil-${id}`, from: order.id, to: id, type: "StartToStart", color: order.color, width: 2});
                });
            });
        }
    };
    app.init();