Page 1 of 1

Export format issue for left-side grid when using tree layout

Posted: Sun Mar 26, 2023 9:56 pm
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


Re: Export format issue for left-side grid when using tree layout

Posted: Mon Mar 27, 2023 10:48 am
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 611 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 611 times

Maybe more action required to reproduce that?


Re: Export format issue for left-side grid when using tree layout

Posted: Mon Mar 27, 2023 3:07 pm
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


Re: Export format issue for left-side grid when using tree layout

Posted: Mon Mar 27, 2023 5:10 pm
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?


Re: Export format issue for left-side grid when using tree layout

Posted: Mon Mar 27, 2023 5:24 pm
by syl

Hi again,

I sent a recording via PM.


Re: Export format issue for left-side grid when using tree layout

Posted: Tue Mar 28, 2023 11:52 am
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


Re: Export format issue for left-side grid when using tree layout

Posted: Tue Jul 11, 2023 6:42 pm
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


Re: Export format issue for left-side grid when using tree layout

Posted: Tue Jul 11, 2023 6:51 pm
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.


Re: Export format issue for left-side grid when using tree layout

Posted: Mon Nov 13, 2023 1:22 pm
by Maxim Gorkovsky

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