bryntum / support

An issues-only repository for the Bryntum project management component suite which includes powerful Grid, Scheduler, Calendar, Kanban Task Board and Gantt chart components all built in pure JS / CSS / TypeScript
https://www.bryntum.com
54 stars 6 forks source link

Dragged element is not rendered correctly when `constrainDragToTimeline` disabled #10138

Open chuckn0rris opened 3 weeks ago

chuckn0rris commented 3 weeks ago

Test case BryntumDragIssue (1).zip If not runnable with yarn, change version in package.json for yarn from 4.0.4 to 1.2.0 (or whatever you have locally)

Worked when constraints enabled, but doesn't when it disabled. An app attached is on Angular, but it is required to be reviewed by Core developer, not Angular.

Forum post

We are using Brynutm Scheduler 6.0.1 with Angular 17 and have implemented the scheduler with our own renderer component:

const EVENT_COMPONENT_SELECTOR = getComponentSelector(SchedulingBoardEventComponent);

export const eventRendererConfig = {
  eventColor: null,
  eventRenderer: (): string => `<${EVENT_COMPONENT_SELECTOR} />`,
  onRenderEvent: ({ eventRecord, element: parentElement }): void => {
    const data = (eventRecord as SchedulingBoardModel<SchedulingBoardItem>).getData('data');
    const element = findFirstElementOrThrow<SchedulingBoardComponentWithData<SchedulingBoardItem>>(
      EVENT_COMPONENT_SELECTOR,
      parentElement,
    );
    element.data = data;
  },
} satisfies SchedulerConfig;

Everything is working fine, until we deactivate constrainDragToTimeline and set it to false to be able to drag between to connected schedulers.

Our custom renderer component has the following template:

<div *ngIf="data; else dragCreationElement">
  <app-scheduling-board-creation-card
    *ngIf="data.modelType === SchedulingBoardItemTypeEnum.CREATION"
    [subject]="data.subject"
    [color]="data.color"
  />

  <app-work-order-card-scheduling
    *ngIf="data.modelType === SchedulingBoardItemTypeEnum.WORK_ORDER"
    [workOrder]="data"
    [hasNoBackground]="!(showFullyColoredCards$ | async)"
  />
</div>

<ng-template #dragCreationElement>
  <app-scheduling-board-creation-card />
</ng-template>

and once we set constrainDragToTimeline to false and start to drag, it is always rendering the fallback component as if it doesn't get the data. While debugging, we noticed, that the eventRenderer and also the onRenderEvent are not called so we assume the data is not correctly inserted. In the Dom, we can see, that the data is set correctly on the bryntum divs but apparently not forwarded.

Here is a screen shot of the scheduling board while dragging:

Screenshot of the Dom element while dragging: https://drive.google.com/file/d/1RbabIV5pWADdbZDu65wCEIrl1bzPgyJm/view?usp=drive_link

Any idea, what we can do to fix the rendering while dragging?

Thank you!

chuckn0rris commented 1 week ago

pinged https://forum.bryntum.com/viewtopic.php?p=155941#p155941

jsakalos commented 1 week ago

Here is the summary of what is happening here:

  1. If constrainDragToTimeline is false then a clone (a proxy) is created for dragging and the proxy is dragged while the original element is hidden.
  2. The event is rendered by means of a Custom Element, which is defined in the Angular application and created by eventRenderer function.
  3. In the provided application the Custom Element receives Object as an attribute but the CEs, similarly to the HTML tags, can only receive string values as attributes. Although the "object" approach initially works while creating the CEs, there is no way how the resulting markup could be cloned with complex attributes, therefore the content of the event changes while dragging, not receiving the expected data object.
  4. The solution could be either a) spread the data object into individual attributes with string values (preferred because changes can be easily observed and reacted to) or b) serialize data object into JSON string (which could be easier if there's no interaction of the user with the rendered CEs).

The provided demo application can be made working by the following:

  1. Change the SchedulingBoardEventComponent to read the following:
    
    import {Component, Input, OnInit } from '@angular/core';

@Component({ selector: 'app-scheduling-board-event', templateUrl: './scheduling-board-event.component.html', styleUrl: './scheduling-board-event.component.scss', }) export class SchedulingBoardEventComponent implements OnInit { @Input() text: string | undefined;

constructor() {}

ngOnInit() { console.log('SchedulingBoardEventComponent initialized'); } }


Template:

<ng-container *ngIf="text; else dragCreationElement">

{{text}}

<ng-template #dragCreationElement>

Creation Card


2. Change the `eventRenderer` in `app.config.ts` to read the following:
eventRenderer: ({ eventRecord, resourceRecord, renderData }): string => {
    return `<app-scheduling-board-event text="${eventRecord.name}"></app-scheduling-board-event>`;
},


The conclusion is that this is not a bug that can be solved by the Bryntum code but it is a limitaion of Custom Elements which can be worked around easily.

Closing the ticket.
jsakalos commented 1 day ago

I'm re-opening the ticket because the true reason is, as I wrote on Slack to @isglass:

In EventDrag feature we delete original and create a new element when constrainDragToTimeline is false. Normally, it’s not a problem but in Angular, where we use custom elements as renderers it fails to render the proper content while dragging. A user is complaining about that here: https://forum.bryntum.com/viewtopic.php?t=30466

The thing is that if we created the proxy with simple element.cloneNode(true) then the content is preserved; I have tried it and it would resolve the issue.

At least we should investigate whether it is possible to use cloneNode universally. At least we should add an opt-out config option as the user suggests in the forum thread.