Bug 1645719 - Create more space for column headers in attendees dialog. r+a=pmorris
authorGeoff Lankow <geoff@darktrojan.net>
Wed, 17 Jun 2020 13:25:08 +0300
changeset 39413 937f289bd94db87a3844084961dec4e6ec44cffe
parent 39412 e431355d0d285ec02da2478c106c00c5f524c1d7
child 39414 6e5152df0c915a190e5c908ace2fa191dc105050
push id402
push userclokep@gmail.com
push dateMon, 29 Jun 2020 20:48:04 +0000
bugs1645719
Bug 1645719 - Create more space for column headers in attendees dialog. r+a=pmorris
calendar/base/content/dialogs/calendar-event-dialog-attendees.css
calendar/base/content/dialogs/calendar-event-dialog-attendees.js
--- a/calendar/base/content/dialogs/calendar-event-dialog-attendees.css
+++ b/calendar/base/content/dialogs/calendar-event-dialog-attendees.css
@@ -84,17 +84,24 @@ calendar-day {
 }
 
 .day-off > .time-off {
   background-color: var(--viewMonthDayOffLabelBackground);
 }
 
 #freebusy-grid-inner {
   overflow: hidden;
-  background-image: url(data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2250%22%20height%3D%2221%22%3E%3Cpath%20d%3D%22M49.5%200V20.5H0%22%20fill%3D%22none%22%20stroke%3D%22threedshadow%22%20stroke-width%3D%221%22%2F%3E%3C%2Fsvg%3E%0A);
+}
+
+#freebusy-grid-inner.twoMinorColumns {
+  background-image: url(data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2260%22%20height%3D%2221%22%20fill%3D%22none%22%20stroke%3D%22threedshadow%22%20stroke-width%3D%221%22%3E%3Cpath%20d%3D%22M59.5%200V20.5H0%22%2F%3E%3Cpath%20d%3D%22M29.5%200V20.5%22%20stroke-opacity%3D%220.44%22%2F%3E%3C%2Fsvg%3E);
+}
+
+#freebusy-grid-inner.threeMinorColumns {
+  background-image: url(data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2290%22%20height%3D%2221%22%20fill%3D%22none%22%20stroke%3D%22threedshadow%22%20stroke-width%3D%221%22%3E%3Cpath%20d%3D%22M89.5%200V20.5H0%22%2F%3E%3Cpath%20d%3D%22M29.5%200V20.5%22%20stroke-opacity%3D%220.44%22%2F%3E%3Cpath%20d%3D%22M59.5%200V20.5%22%20stroke-opacity%3D%220.44%22%2F%3E%3C%2Fsvg%3E);
 }
 
 .freebusy-row {
   position: relative;
   height: 21px;
   box-sizing: border-box;
   padding: 2px 0 3px;
 }
--- a/calendar/base/content/dialogs/calendar-event-dialog-attendees.js
+++ b/calendar/base/content/dialogs/calendar-event-dialog-attendees.js
@@ -28,37 +28,47 @@ var freebusyGridInner;
 var displayStartTime;
 var displayEndTime;
 
 var zoom = {
   zoomInButton: null,
   zoomOutButton: null,
   levels: [
     {
-      columnCount: 6,
-      columnDuration: cal.createDuration("PT4H"),
-      multiplier: 300 / 24 / 3600,
-    },
-    {
-      columnCount: 12,
-      columnDuration: cal.createDuration("PT2H"),
-      multiplier: 600 / 24 / 3600,
+      // Total width in pixels of one day.
+      dayWidth: 360,
+      // Number of major columns a day is divided into. Each dividing line is labelled.
+      columnCount: 4,
+      // Duration of each major column.
+      columnDuration: cal.createDuration("PT6H"),
+      // The width in pixels of one column.
+      columnWidth: 90,
+      // The width in pixels of one second.
+      secondWidth: 360 / 24 / 3600,
+      // Which background grid to show.
+      gridClass: "threeMinorColumns",
     },
     {
-      columnCount: 24,
-      columnDuration: cal.createDuration("PT1H"),
-      multiplier: 1200 / 24 / 3600,
+      dayWidth: 720,
+      columnCount: 8,
+      columnDuration: cal.createDuration("PT3H"),
+      columnWidth: 90,
+      secondWidth: 720 / 24 / 3600,
+      gridClass: "threeMinorColumns",
     },
     {
-      columnCount: 48,
-      columnDuration: cal.createDuration("PT30M"),
-      multiplier: 2400 / 24 / 3600,
+      dayWidth: 1440,
+      columnCount: 12,
+      columnDuration: cal.createDuration("PT2H"),
+      columnWidth: 120,
+      secondWidth: 1440 / 24 / 3600,
+      gridClass: "twoMinorColumns",
     },
   ],
-  currentLevel: 0,
+  currentLevel: null,
 
   init() {
     this.zoomInButton = document.getElementById("zoom-in-button");
     this.zoomOutButton = document.getElementById("zoom-out-button");
 
     this.zoomInButton.addEventListener("command", () => this.level++);
     this.zoomOutButton.addEventListener("command", () => this.level--);
   },
@@ -79,27 +89,40 @@ var zoom = {
     this.currentLevel = newZoomLevel;
     displayEndTime = displayStartTime.clone();
 
     emptyGrid();
     for (let attendee of attendeeList.getElementsByTagName("event-attendee")) {
       attendee.clearFreeBusy();
     }
 
+    for (let gridClass of ["twoMinorColumns", "threeMinorColumns"]) {
+      if (this.levels[newZoomLevel].gridClass == gridClass) {
+        freebusyGridInner.classList.add(gridClass);
+      } else {
+        freebusyGridInner.classList.remove(gridClass);
+      }
+    }
     fillGrid();
     eventBar.update(true);
   },
+  get dayWidth() {
+    return this.levels[this.currentLevel].dayWidth;
+  },
   get columnCount() {
     return this.levels[this.currentLevel].columnCount;
   },
   get columnDuration() {
     return this.levels[this.currentLevel].columnDuration;
   },
-  get multiplier() {
-    return this.levels[this.currentLevel].multiplier;
+  get columnWidth() {
+    return this.levels[this.currentLevel].columnWidth;
+  },
+  get secondWidth() {
+    return this.levels[this.currentLevel].secondWidth;
   },
 };
 
 var eventBar = {
   dragDistance: 0,
   dragStartX: null,
   eventBarBottom: "event-bar-bottom",
   eventBarTop: "event-bar-top",
@@ -119,26 +142,27 @@ var eventBar = {
         this.dragStartX = event.clientX;
         let img = document.createElement("img");
         img.src = "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7";
         event.dataTransfer.setDragImage(img, 0, 0);
         event.dataTransfer.effectAllowed = "move";
         break;
       }
       case "dragover": {
-        this.dragDistance = Math.round((event.clientX - this.dragStartX) / 12.5) * 12.5;
+        // Snap dragging movements to half of a minor column width.
+        this.dragDistance = Math.round((event.clientX - this.dragStartX) / 15) * 15;
         this.eventBarTop.style.transform = this.eventBarBottom.style.transform = `translateX(${this.dragDistance}px)`;
         break;
       }
       case "dragend": {
         this.dragStartX = null;
         this.eventBarTop.style.transform = this.eventBarBottom.style.transform = null;
 
         let duration = cal.createDuration();
-        duration.inSeconds = this.dragDistance / zoom.multiplier;
+        duration.inSeconds = this.dragDistance / zoom.secondWidth;
 
         let { startValue, endValue } = dateTimePickerUI;
         startValue.addDuration(duration);
         dateTimePickerUI.startValue = startValue;
         endValue.addDuration(duration);
         dateTimePickerUI.endValue = endValue;
 
         setLeftAndWidth(this.eventBarTop, startValue, endValue);
@@ -458,34 +482,34 @@ function fillGrid() {
   while (
     dayHeaderInner.childElementCount < 5 ||
     dayHeaderOuter.scrollWidth <= dayHeaderOuter.clientWidth
   ) {
     dayHeaderInner.appendChild(document.createXULElement("calendar-day")).date = displayEndTime;
     displayEndTime.addDuration(cal.createDuration("P1D"));
   }
 
-  freebusyGridInner.style.width = dayHeaderInner.childElementCount * zoom.columnCount * 50 + "px";
+  freebusyGridInner.style.width = dayHeaderInner.childElementCount * zoom.dayWidth + "px";
   if (displayEndTime.compare(oldEndTime) > 0) {
     for (let attendee of attendeeList.getElementsByTagName("event-attendee")) {
       attendee.updateFreeBusy(oldEndTime, displayEndTime);
     }
   }
 }
 
 /**
  * Aligns element horizontally on the grid to match the time period it represents.
  *
  * @param {Element} element - The element to align.
  * @param {calIDateTime} startTime - The start time to be represented.
  * @param {calIDateTime} endTime - The end time to be represented.
  */
 function setLeftAndWidth(element, startTime, endTime) {
-  element.style.left = startTime.subtractDate(displayStartTime).inSeconds * zoom.multiplier + "px";
-  element.style.width = endTime.subtractDate(startTime).inSeconds * zoom.multiplier + "px";
+  element.style.left = startTime.subtractDate(displayStartTime).inSeconds * zoom.secondWidth + "px";
+  element.style.width = endTime.subtractDate(startTime).inSeconds * zoom.secondWidth + "px";
 }
 
 // Wrap in a block to prevent leaking to window scope.
 {
   /**
    * Represents a row on the grid for a single attendee. The element itself is the row header, and
    * this class holds reference to any elements on the grid itself that represent the free/busy
    * status for this row's attendee. The free/busy elements are removed automatically if this
@@ -768,34 +792,40 @@ function setLeftAndWidth(element, startT
       let dayLabelContainer = this.appendChild(document.createXULElement("box"));
       dayLabelContainer.setAttribute("pack", "center");
 
       this.dayLabel = dayLabelContainer.appendChild(document.createXULElement("label"));
       this.dayLabel.classList.add("day-label");
 
       let columnContainer = this.appendChild(document.createXULElement("box"));
 
-      columnContainer.appendChild(document.createXULElement("box")).setAttribute("width", "25");
+      // A half-column-wide spacer to align labels with the dividing grid lines.
+      columnContainer
+        .appendChild(document.createXULElement("box"))
+        .setAttribute("width", zoom.columnWidth / 2);
 
       let column = displayEndTime.clone();
       column.isDate = false;
       for (let i = 1; i < zoom.columnCount; i++) {
         column.addDuration(zoom.columnDuration);
 
         let columnBox = columnContainer.appendChild(document.createXULElement("box"));
-        columnBox.setAttribute("width", "50");
+        columnBox.setAttribute("width", zoom.columnWidth);
         columnBox.setAttribute("align", "center");
 
         let columnLabel = columnBox.appendChild(document.createXULElement("label"));
         columnLabel.classList.add("hour-label");
         columnLabel.setAttribute("flex", "1");
         columnLabel.setAttribute("value", cal.dtz.formatter.formatTime(column));
       }
 
-      columnContainer.appendChild(document.createXULElement("box")).setAttribute("width", "24");
+      // A half-column-wide (minus 1px) spacer to align labels with the dividing grid lines.
+      columnContainer
+        .appendChild(document.createXULElement("box"))
+        .setAttribute("width", zoom.columnWidth / 2 - 1);
     }
 
     disconnectedCallback() {
       if (this.dayColumn) {
         this.dayColumn.remove();
       }
     }