Premium support for our pure JavaScript UI components


Post by syl »

Hello,

In the standard demo example for PDF export (here), I have noted that the exported pdf format take into account any custom resizing of the left grid (i.e. changing the width of the grid gets applied to the PDF itself).

However, it seems that this does not work when using a tree layout for the grid. I started from the example here.
I applied the following code, resized the left grid as shown in the image, and obtained the enclosed PDF file, which completely ignores the grid resizing.

Can you confirm that this is a defect and can you look into fixing it?

import { DateHelper, AsyncHelper, WidgetHelper, DataGenerator, Scheduler } from '../../build/schedulerpro.module.js';
import shared from '../_shared/shared.module.js';

const headerTpl = ({ currentPage, totalPages }) => `
    <img alt="Company logo" src="resources/bryntum.svg"/>
    <dl>
        <dt>Date: ${DateHelper.format(new Date(), 'll LT')}</dt>
        <dd>${totalPages ? `Page: ${currentPage + 1}/${totalPages}` : ''}</dd>
    </dl>
    `;

const
    footerTpl = () => `<h3>© ${new Date().getFullYear()} Bryntum AB</h3></div>`;

const generateResources  = async(
        resourceCount = resourceCountField.value,
        eventCount    = eventCountField.value
    ) => {
        const
            today                 = DateHelper.clearTime(new Date()),
            mask                  = WidgetHelper.mask(scheduler.element, 'Generating records'),
            colors                = ['cyan', 'green', 'blue'],
            icons                 = ['b-fa-truck-monster', 'b-fa-truck-pickup', 'b-fa-truck', 'b-fa-shuttle-van'],
            names                 = ['Excursion', 'Delivery', 'Rental', 'Tour'],
            resources             = [],
            events                = [],
            assignments           = [],
            dependencies          = [],
            resourceTimeRanges    = [],
            useResourceTimeRanges = !scheduler.features.resourceTimeRanges.disabled,
            useDependencies       = !scheduler.features.dependencies.disabled;

    let schedulerEndDate = today,
        j, step;

    console.time('generate');

    const
        generator   = DataGenerator.generate(resourceCount),
        resourceMap = {};

    while ((step = generator.next()) && !step.done) {
        const res = step.value;

        let parent = resourceMap[res.city];

        if (!parent) {
            resources.push(parent = resourceMap[res.city] = {
                id       : `Parent${Object.keys(resourceMap).length + 1}`,
                name     : res.city,
                expanded : true,
                children : [],
                maxDate  : new Date(2000, 0, 1),
                minDate  : new Date(2200, 0, 1)
            });
        }

        parent.children.push(res);

        for (j = 0; j < eventCount; j++) {
            const
                startDate = DateHelper.add(today, Math.round(Math.random() * (j + 1) * 20), 'days'),
                duration  = Math.round(Math.random() * 9) + 3,
                endDate   = DateHelper.add(startDate, duration, 'days'),
                eventId   = events.length + 1;

            events.push({
                id         : eventId,
                name       : names[parent.children.length % 4] + ' #' + (events.length + 1),
                startDate,
                duration,
                endDate,
                eventColor : colors[parent.children.length % 3],
                iconCls    : `b-fa ${icons[parent.children.length % 4]}`
            });

            assignments.push({
                id       : 'a' + eventId,
                event    : eventId,
                resource : res.id
            });

            if (useDependencies && j > 0) {
                dependencies.push({
                    id   : dependencies.length + 1,
                    from : eventId - 1,
                    to   : eventId
                });
            }

            if (useResourceTimeRanges && j % 2 === 0) {
                resourceTimeRanges.push({
                    id             : resourceTimeRanges.length + 1,
                    resourceId     : res.id,
                    name           : `Range ${resourceTimeRanges.length + 1}`,
                    startDate      : DateHelper.add(startDate, Math.round(Math.random() * 5), 'days'),
                    duration       : Math.round(Math.random() * 5) + 4,
                    timeRangeColor : 'red'
                });
            }

            if (endDate > schedulerEndDate) {
                schedulerEndDate = endDate;
            }

            if (endDate > parent.maxDate) {
                parent.maxDate = endDate;
            }

            if (startDate < parent.minDate) {
                parent.minDate = startDate;
            }
        }

        if (resources.length % 2000 === 0) {
            mask.text = `Generated ${resources.length * eventCount} of ${resourceCount * eventCount} records`;

            await AsyncHelper.animationFrame();
        }
    }

    for (const parent of resources) {
        events.push({
            id         : 'summary-' + parent.id,
            name       : `${parent.name} activities`,
            startDate  : parent.minDate,
            endDate    : parent.maxDate,
            eventColor : 'purple',
            eventStyle : 'plain',
            iconCls    : 'b-fa b-fa-calendar'
        });

        assignments.push({
            id       : 'summary-' + parent.id,
            event    : 'summary-' + parent.id,
            resource : parent.id
        });
    }

    console.timeEnd('generate');

    mask.text = 'Loading data';

    // Give the UI a chance to catch up, to update the mask before proceeding
    await AsyncHelper.sleep(100);

    console.time('data');

    scheduler.suspendRefresh();
    scheduler.endDate = schedulerEndDate;
    scheduler.project = {
        assignmentStore : {
            useRawData : true,
            data       : assignments
        },
        resourceStore : {
            useRawData : true,
            data       : resources
        },
        eventStore : {
            useRawData : true,
            data       : events
        },
        dependencyStore : {
            useRawData : true,
            data       : dependencies
        },
        resourceTimeRangeStore : {
            useRawData : true,
            data       : resourceTimeRanges
        }
    };
    scheduler.resumeRefresh(true);

    await scheduler.project.await('refresh');

    console.timeEnd('data');

    mask.close();
}, toggleCustom    = show => {
    scheduler.widgetMap.rangesButton.hidden = scheduler.widgetMap.dependenciesButton.hidden = resourceCountField.hidden = eventCountField.hidden = !show;
},
applyPreset        = (resources, events) => {
    toggleCustom(false);

    resourceCountField.value = resources;
    eventCountField.value = events;

    generateResources();
},
scheduler          = new Scheduler({
    appendTo              : 'container',
    eventStyle            : 'rounded',
    rowHeight             : 36,
    barMargin             : 2,
    resourceMargin        : 5,
    useInitialAnimation   : false,
    enableEventAnimations : false,
    columns               : [
        { text : 'Id', field : 'id', width : 50, hidden : true },
        { type : 'tree', text : 'Name', field : 'name', width : 200 }
    ],

    features : {
        // Dependencies can be enabled later by the user
        dependencies : {
            disabled : true
        },
        // ResourceTimeRanges can be enabled later by the user
        resourceTimeRanges : {
            disabled : true
        },
        // Turn sticky events off to boost performance with many events on screen simultaneously
        // stickyEvents    : false,
        // Turn off schedule tooltip to boost scrolling performance a bit
        scheduleTooltip : false,
        // Turn the resource grid part of Scheduler into a tree
        tree            : true,
        pdfExport : {
            exportServer: 'https://dev.bryntum.com:8082',
            // Required for font-awesome icons to display correctly
            
            headerTpl,
            footerTpl
        }
    },

    tbar : [
        {
            ref  : 'exportButton',
            type : 'button',
            icon : 'b-fa-file-export',
            text : 'Export',
            onClick() {
                scheduler.features.pdfExport.showExportDialog();
            }
        },

        'Presets',
        {
            type        : 'buttongroup',
            toggleGroup : true,
            items       : [
                {
                    text       : '1K events',
                    pressed    : true,
                    dataConfig : {
                        resources : 200,
                        events    : 5
                    }
                },
                {
                    text       : '5K events',
                    dataConfig : {
                        resources : 1000,
                        events    : 5
                    }
                },
                {
                    text       : '10K events',
                    dataConfig : {
                        resources : 1000,
                        events    : 10
                    }
                },
                {
                    text : 'Custom',
                    ref  : 'customButton',
                    onClick() {
                        toggleCustom(true);
                    }
                }
            ],
            onClick({ source : button }) {
                if (button.dataConfig) {
                    applyPreset(button.dataConfig.resources, button.dataConfig.events);
                }
            }
        },
        '->',
        {
            ref                  : 'resourceCountField',
            type                 : 'number',
            placeholder          : 'Number of resources',
            label                : 'Resources',
            tooltip              : 'Enter number of resource rows to generate and press [ENTER]',
            min                  : 1,
            max                  : 10000,
            width                : 'auto',
            inputWidth           : '5em',
            keyStrokeChangeDelay : 500,
            changeOnSpin         : 500,
            hidden               : true,
            onChange             : ({ userAction }) => userAction && generateResources()
        }, {
            ref                  : 'eventCountField',
            type                 : 'number',
            placeholder          : 'Number of events',
            label                : 'Events',
            tooltip              : 'Enter number of events per resource to generate and press [ENTER]',
            min                  : 1,
            max                  : 100,
            width                : 'auto',
            inputWidth           : '4em',
            keyStrokeChangeDelay : 500,
            changeOnSpin         : 500,
            hidden               : true,
            onChange             : ({ userAction }) => userAction && generateResources()
        }, {
            type        : 'button',
            ref         : 'dependenciesButton',
            toggleable  : true,
            icon        : 'b-fa-square',
            pressedIcon : 'b-fa-check-square',
            text        : 'Dependencies',
            hidden      : true,
            onToggle({ pressed }) {
                scheduler.features.dependencies.disabled = !pressed;

                if (pressed && !scheduler.dependencyStore.count) {
                    generateResources();
                }
            }
        }, {
            type        : 'button',
            ref         : 'rangesButton',
            toggleable  : true,
            icon        : 'b-fa-square',
            pressedIcon : 'b-fa-check-square',
            text        : 'Resource ranges',
            hidden      : true,
            onToggle({ pressed }) {
                scheduler.features.resourceTimeRanges.disabled = !pressed;

                if (pressed && !scheduler.resourceTimeRangeStore.count) {
                    generateResources();
                }
            }
        }
    ]
}),
resourceCountField = scheduler.widgetMap.resourceCountField,
eventCountField    = scheduler.widgetMap.eventCountField;

applyPreset(200, 5);

Thanks
Sylvain

Attachments
ExportView-VisibleScheduleAndRows.pdf
Resulting PDF
(67.1 KiB) Downloaded 24 times
View after resizing
View after resizing
HTMLView.png (118 KiB) Viewed 389 times

Post by alex.l »

Hi, I do see resizing of the column using code you posted.
Here is pdf before resize

Screenshot 2023-03-27 at 11.46.06.png
Screenshot 2023-03-27 at 11.46.06.png (50.7 KiB) Viewed 366 times

And after resize

Screenshot 2023-03-27 at 11.46.10.png
Screenshot 2023-03-27 at 11.46.10.png (40.78 KiB) Viewed 366 times

Maybe more action required to reproduce that?

All the best,
Alex


Post by syl »

Hi Alex,

Thanks for looking into.
I tried to get your results but I really couldn't. I tried to change the export options Schedule Range and Rows, but I'm always getting the same grid width in the exported PDF.
The overall format changes depending on the options I use (as expected) but the grid is always sized to fit the content of the grid, while in the website, I'm leaving plenty of blank space by increase the width.
And I'm not doing any other steps than what I mentioned above... :/

I'm using Chrome if that makes any difference.

Now, just checking, on another ticket, there was a mismatch between the behavior of the examples on the website and the behavior in your internal dev environment.
Can I ask if you tried locally or on the website directly, just in case?

Sylvain


Post by alex.l »

Hi Sylvain,

I tried on website examples with the code provided by you. Do I need to do something else to reproduce? Could you please make a video with all steps you did to repro that, maybe I did something wrong?

All the best,
Alex


Post by syl »

Hi again,

I sent a recording via PM.


Post by alex.l »

Thank you for extra information, I've reproduced that and opened a ticket for that https://github.com/bryntum/support/issues/6475

All the best,
Alex


Post by syl »

Hi Alex,

Just checking in to see if there is any update on this issue. It has started to pop up on our client side by now.

Best,
Sylvain


Post by alex.l »

Hi Sylvain,

No news for now, unfortunately, I will let product management know about your request and we will try to rise a priority.

All the best,
Alex


Post by Maxim Gorkovsky »

This issue was fixed by this ticket: https://github.com/bryntum/support/issues/7695 Have you tried release 5.6.0?


Post Reply