search envelope-o feed check
Home Unanswered Active Tags New Question
user comment-o

Scheduler - Frozen row not on the top layer

Asked by Anonymous
14 days ago.

I have a resource frozen to the top. When I scroll the events don't go behind that frozen row. See please the attached screenshot.

This is my configuration:
{
    rowHeaderColumns: [
      { text: '', display: "name" },
      { text: '' }
    ],

    durationBarVisible: false,
    heightSpec: 'Parent100Pct',
    hideBorderFor100PctHeight: true,
    locale: this.localeService.localeId,
    cellWidthSpec: 'Auto',
    crosshairType: 'Full',
    timeHeaders: [
      { 'groupBy': "Month", 'format': "MMMM yyyy" },
      { 'groupBy': 'Week' },
      { 'groupBy': 'Day', 'format': 'd' }
    ],
    scale: 'CellDuration',
    cellDuration: 720,
    // https://forums.daypilot.org/question/5571/how-to-change-the-displayed-end-date-of-selected-range
    //eventEndSpec: 'Date',
    showNonBusiness: true,
    businessWeekends: false,
    floatingEvents: true,
    eventHeight: 20,
    headerHeight: 20,
    rowMinHeight: 20,
    eventStackingLineHeight: 100,
    rowMarginTop: 1,
    rowMarginBottom: 4,
    eventMarginBottom: 1,
    eventMarginLeft: 1,
    eventMarginRight: 2,
    eventMovingStartEndEnabled: true,
    eventResizingStartEndEnabled: true,
    timeRangeSelectingStartEndEnabled: true,
    groupConcurrentEvents: false,
    allowEventOverlap: true,
    allowMultiRange: false,
    multiSelectRectangle: 'Free',
    rowHeaderHideIconEnabled: false,
    multiMoveVerticalMode: 'All',
    allowMultiMove: false,
    treeEnabled: false,

    contextMenuResource: new DayPilot.Menu({
      items: [
        {
          text: this.dialogTextService.getTranslation('TitJumpToTimesheet'),
          onClick: (args) => { this.navigateToTimeSheet(args.source.data.id); },
          hidden: false
        },
      ],
      onShow: (args) => {
        const personResource = args.source.data as PersonResource;
        args.menu.items[0].hidden = !this.isActiveAndSubordinatedPerson(personResource);

        if (args.menu.items.all(i => i.hidden)) {
          args.preventDefault();
        }
      }
    }),

    contextMenuSelection: new DayPilot.Menu({
      onShow: (args) => {
        const personId = +args.source.resource;
        const from = new Date(args.source.start.value);
        const to = new Date(args.source.end.value);

        const personResource = this.personResources.find(p => p.id == personId.toString());

        let hasEditPermission = this.globalVariableService.LoggedInUser.hasPermission(PermissionKeys.MODULE_ATTENDANCE)
        let menuItems = new Array<DayPilot.MenuItemData>();

        if (personResource.frozen === undefined) {
          if (personResource && personResource.tags.personRole == PersonRole.OtherEmployee) {
            args.preventDefault();
          }

          if (hasEditPermission) {
            // Hinzufügen
            menuItems.push({
              text: this.dialogTextService.getTranslation('TitAdd'),
              items: this.addCommandSubMenuItems(personResource)
            });

            // Einfügen
            if (this.clipboard[0]) {
              // Separator
              if (menuItems.any()) {
                menuItems.push({ text: '-' });
              }

              menuItems.push({
                text: this.dialogTextService.getTranslation('TitPaste'),
                onClick: (args) => { this.pasteItem(args); }
              });
            }
          }

          // Separator
          if (menuItems.any()) {
            menuItems.push({ text: '-' });
          }

          // Zeitausweis öffnen
          menuItems.push({
            text: this.dialogTextService.getTranslation('TitJumpToTimesheet'),
            onClick: (args) => { this.navigateToTimeSheet(args.source.resource); }
          });
        }
        else {
          // Plankommentar hinzufügen
          menuItems.push({
            text: this.dialogTextService.getTranslation('TitPlanComment'),
            onClick: (args) => { this.addPlanningComment(args.source.resource); }
          });
        }

        args.menu.items = menuItems;

        if (!args.menu.items.any()) {
          args.preventDefault();
        }
      }
    }),

    bubble: new DayPilot.Bubble({
      onLoad: (args) => {
        this.showTooltip(args);
      }
    }),

    /**
     * Events handling settings
     */
    timeRangeSelectedHandling: 'Hold',
    eventMoveHandling: 'Update',
    eventResizeHandling: 'Update',
    eventDeleteHandling: 'Disabled',
    eventClickHandling: 'Select',
    eventHoverHandling: 'Bubble',
    eventDoubleClickHandling: 'Enabled',

    timeRangeRightClickHandling: "ContextMenu",

    /**
     * Events handler
     */

    onAfterRender: (args) => {
      this.busyIndicatorService.hide();
    },

    onBeforeTimeHeaderRender: (args) => {
      if (args.header.level === 1) {
        args.header.html = `${this.dialogTextService.getTranslation('TitWeek')} ${args.header.start.weekNumberISO().padStart(2, '0')}`;
      }

      let start = new Date(args.header.start.value);
      if (args.header.level === 2 && start.isSame(Date.today())) {
        args.header.areas = [
          { start: args.header.start, end: args.header.end, bottom: 0, height: 2, backColor: 'blue' }
        ];
      }
    },

    onBeforeRowHeaderRender: (args) => {
      const personResource = args.row.data as PersonResource;

      if (personResource.frozen !== undefined) {
        return;
      }

      // Status icons
      args.row.columns[1].areas = [];

      const leftOffset = 20
      let left = 60;
      // Same position for top and bottom
      // see https://forums.daypilot.org/question/5577/how-to-center-vertically-active-areas
      const topBottom = 5;

      if (personResource.tags.ignoreWorkProfileChangesToolTipText) {
        args.row.columns[1].areas.push(
          {
            left: left,
            top: topBottom,
            bottom: topBottom,
            style: "display: flex; align-items: center;",
            width: 16,
            html: '<img src="assets/icons/favorites.png" height="16" width="16">',
            toolTip: personResource.tags.ignoreWorkProfileChangesToolTipText
          });

        left = left - leftOffset;
      }

      if (personResource.tags.isIndirectlySubordinatedToolTipText) {
        args.row.columns[1].areas.push(
          {
            left: left,
            top: topBottom,
            bottom: topBottom,
            style: "display: flex; align-items: center;",
            width: 16,
            html: '<img src="assets/icons/subordinate.png" height="16" width="16">',
            toolTip: personResource.tags.isIndirectlySubordinatedToolTipText
          });

        left = left - leftOffset;
      }

      if (personResource.tags.personWillEnterToolTipText) {
        args.row.columns[1].areas.push(
          {
            left: left,
            top: topBottom,
            bottom: topBottom,
            style: "display: flex; align-items: center;",
            width: 16,
            html: '<img src="assets/icons/Enter.png" height="16" width="16">',
            toolTip: personResource.tags.personWillEnterToolTipText
          });

        left = left - leftOffset;
      }

      if (personResource.tags.personLeftCompanyToolTipText) {
        args.row.columns[1].areas.push(
          {
            left: left,
            top: topBottom,
            bottom: topBottom,
            style: "display: flex; align-items: center;",
            width: 16,
            html: '<img src="assets/icons/leave.png" height="16" width="16">',
            toolTip: personResource.tags.personLeftCompanyToolTipText
          });

        left = left - leftOffset;
      }

      if (personResource.tags.calculationStatusToolTipText) {
        args.row.columns[1].areas.push(
          {
            left: left,
            top: topBottom,
            bottom: topBottom,
            style: "display: flex; align-items: center;",
            width: 16,
            html: '<img src="assets/icons/Error.png" height="16" width="16">',
            toolTip: personResource.tags.calculationStatusToolTipText
          });

        left = left - leftOffset;
      }
    },

    onBeforeCellRender: (args) => {
      this.setDayColor(args);
    },

    onBeforeEventRender: (args) => {
      const menuItems = new Array<DayPilot.MenuItemData>();
      const personId = args.data.resource;
      const from = new Date(args.data.start);

      const personResource = this.personResources.find(p => p.id == args.data.resource);

      if (args.data.tags.type == 'Comment' && !this.hasCommentCorrectionPermission(personId)) {
        return;
      }

      let hasEditPermission = this.globalVariableService.LoggedInUser.hasPermission(PermissionKeys.MODULE_ATTENDANCE)

      if (!args.data.isReadonly && hasEditPermission) {
        // Bearbeiten
        if (args.data.tags.type != 'Error' && args.data.tags.type != 'Absence') {
          menuItems.push({
            text: this.dialogTextService.getTranslation('TitEdit'),
            onClick: (args) => {
              let event: DayPilot.Event = args.source;
              let item = event.data as AttendancePlanningEvent;
              this.editItem(item);
            }
          });
        }

        if (args.data.tags.type == 'AdditionalService' || args.data.tags.type == 'Service'
          || args.data.tags.type == 'PlanningBreak') {
          // Kopieren
          menuItems.push({
            text: this.dialogTextService.getTranslation('BtnCopy'),
            onClick: (args) => {
              let event: DayPilot.Event = args.source;
              let item = event.data as AttendancePlanningEvent;
              this.copyItem(args);
            }
          });
        }

        // Löschen
        if (args.data.tags.type != 'Error' && args.data.tags.type != 'Absence') {
          menuItems.push({
            text: this.dialogTextService.getTranslation('BtnDelete'),
            onClick: (args) => {
              let event: DayPilot.Event = args.source;
              let item = event.data as AttendancePlanningEvent;
              this.deleteItem(item);
            }
          });
        }
      }

      // Zeitausweis öffnen
      if (this.isActiveAndSubordinatedPerson(personResource)) {
        // Separator
        if (menuItems.any()) {
          menuItems.push({ text: '-' });
        }

        menuItems.push({
          text: this.dialogTextService.getTranslation('TitJumpToTimesheet'),
          onClick: (args) => { this.navigateToTimeSheet(args.source.resource); }
        });
      }

      if (menuItems.any()) {
        args.data.contextMenu = new DayPilot.Menu({
          items: menuItems
        });
      }
    },

    onEventDoubleClick: (args) => {
      let event = args.e.data as AttendancePlanningEvent;
      if (event.tags.type == 'Absence' || event.tags.type == 'Error') {
        args.preventDefault();
      }
    },

    onEventDoubleClicked: (args) => {
      let event = args.e.data as AttendancePlanningEvent;
      this.editItem(event);
    },

    onTimeRangeRightClick: (args) => {
      const from = new Date(args.start.value);
      if (from.isSameOrBefore(this.clientConfigurationService.blockedDate.toDate())) {
        args.preventDefault();
      }
    },

    onTimeRangeSelecting: (args) => {
      let personResource = this.personResources.find(p => p.id == args.resource);

      if (personResource.frozen === undefined) {
        let person = this.personService.getById(args.resource);
        let day = new Date(args.anchor.value);

        if ((this.currentPerson && this.currentPerson.personId != person.personId) || !day.isSame(this.selectedDay)) {
          this.currentPerson = person;
          this.selectedDay = day;
          this.updateBalanceChart();
        }
      }
    }
  }
Answer posted by Dan Letecky [DayPilot]
14 days ago.

I recommend checking your custom CSS theme:

If you define z-index for the event boxes, they will appear on top of everything. The Scheduler doesn't use z-index for any element.

Comment posted by Anonymous
12 days ago.

Yes, indeed. I set z-index for the event box. Because if I don't do it, then the time range selection covers the events (s. pls. attached vidoe). Strangely enough, in your demo's this doesn't happen and I'm not applying any other CSS.

Comment posted by Dan Letecky [DayPilot]
9 days ago.

The time range selection will appear above events (otherwise you wouldn't be able to see it in a Scheduler with existing events). The background of the selection is semi-transparent so you will still see what it covers.

You can play with the shadow CSS and make the background transparent, with just a border visible (for example).

Comment posted by Dan Letecky [DayPilot]
9 days ago.

And one more thing - the selection shadow is not designed to stay visible for a long time. Its purpose is to highlight the position during drag and drop.

If you want to select a time range and keep it visible (so you can perform additional operations on it) you can create your own implementation of the selection: store it in a global variable and add a background color/active area to the background cells. This technique is used in the following tutorial (to highlight holidays):

https://code.daypilot.org/93068/javascript-scheduler-displaying-holidays

Comment posted by Anonymous
7 days ago.

All this make sense. However, in this demo of you (https://demos.daypilot.org/angular2-scheduler-copy-paste/) the time range selection appears below the events. I'm just wondering, how did you do it. I check the CSS and it doesn't appear, that you're setting the z-index for events explicitly.

Comment posted by Dan Letecky [DayPilot]
7 days ago.

This demo uses an old version where the time range selection was displayed below events. This behavior has changed in 2019.2.3832:

https://javascript.daypilot.org/daypilot-pro-for-javascript-2019-2-3832/

New Reply
This reply is
Attachments:
or drop files here
Your name (optional):