Plain is headed towards 1.0! Subscribe for development updates →

  1// Make this available to the JS console for the user
  2const plainToolbar = {
  3  hide: function () {
  4    // Hide by inserting a style so it doesn't flash on page load
  5    const style = document.createElement("style");
  6    style.innerHTML = "#plaintoolbar { display: none; }";
  7    document.getElementsByTagName("head")[0].appendChild(style);
  8    this.stylesheet = style;
  9  },
 10  show: function () {
 11    localStorage.removeItem("plaintoolbar.hidden_until");
 12    if (this.stylesheet) {
 13      this.stylesheet.remove();
 14    }
 15  },
 16  shouldHide: () => {
 17    const hiddenUntil = localStorage.getItem("plaintoolbar.hidden_until");
 18    if (hiddenUntil) {
 19      if (Date.now() < hiddenUntil) {
 20        return true;
 21      }
 22      localStorage.removeItem("plaintoolbar.hidden_until");
 23      return false;
 24    }
 25    return false;
 26  },
 27  hideUntil: function (until) {
 28    localStorage.setItem("plaintoolbar.hidden_until", until);
 29    this.hide();
 30  },
 31  toggleExpand: function () {
 32    this.expanded = !this.expanded;
 33    document.querySelector("#plaintoolbar-details").classList.toggle("hidden");
 34    localStorage.setItem("plaintoolbar.expanded", this.expanded ? "1" : "0");
 35  },
 36  expand: function () {
 37    this.expanded = true;
 38    document.querySelector("#plaintoolbar-details").classList.remove("hidden");
 39    localStorage.setItem("plaintoolbar.expanded", "1");
 40  },
 41  collapse: function () {
 42    this.expanded = false;
 43    document.querySelector("#plaintoolbar-details").classList.add("hidden");
 44    localStorage.setItem("plaintoolbar.expanded", "0");
 45  },
 46  expandTemporary: function () {
 47    this.expanded = true;
 48    document.querySelector("#plaintoolbar-details").classList.remove("hidden");
 49  },
 50  showTab: function (tabName) {
 51    this.expand();
 52
 53    const toolbar = document.querySelector("#plaintoolbar");
 54    const tab = toolbar.querySelector(`div[data-toolbar-tab=${tabName}]`);
 55
 56    // If the tab doesn't exist for some reason, quit
 57    if (!tab) {
 58      console.warn(`Toolbar tab ${tabName} does not exist`);
 59      return;
 60    }
 61
 62    // Hide all children in the tab parent
 63    for (let i = 0; i < tab.parentNode.children.length; i++) {
 64      const child = tab.parentNode.children[i];
 65      if (child !== tab) {
 66        child.style.display = "none";
 67      }
 68    }
 69
 70    tab.style.display = "block";
 71
 72    for (const tab of toolbar.querySelectorAll("button[data-toolbar-tab]")) {
 73      if (tab.dataset.toolbarTab === tabName) {
 74        tab.setAttribute("data-active", true);
 75      } else {
 76        tab.removeAttribute("data-active");
 77      }
 78    }
 79    localStorage.setItem("plaintoolbar.tab", tabName);
 80  },
 81};
 82
 83// Render it hidden immediately if the user has hidden it before
 84if (plainToolbar.shouldHide()) {
 85  plainToolbar.hide();
 86}
 87
 88window.addEventListener("load", () => {
 89  // Restore expanded/collapsed state
 90  const state = localStorage.getItem("plaintoolbar.expanded");
 91  if (state === "1") {
 92    plainToolbar.expand();
 93    // Restore last active tab
 94    const lastTab = localStorage.getItem("plaintoolbar.tab");
 95    if (lastTab) {
 96      plainToolbar.showTab(lastTab);
 97    }
 98  } else if (state === "0") {
 99    plainToolbar.collapse();
100  }
101  const toolbar = document.querySelector("#plaintoolbar");
102  const hasException = toolbar.querySelector('[data-toolbar-tab="Exception"]');
103
104  if (hasException) {
105    plainToolbar.show();
106    if (!plainToolbar.expanded) {
107      plainToolbar.expandTemporary();
108    }
109  }
110
111  for (const tab of toolbar.querySelectorAll("button[data-toolbar-tab]")) {
112    tab.addEventListener("click", () => {
113      plainToolbar.showTab(tab.dataset.toolbarTab);
114    });
115  }
116
117  for (const btn of toolbar.querySelectorAll("[data-plaintoolbar-hide]")) {
118    btn.addEventListener("click", () => {
119      plainToolbar.hide();
120    });
121  }
122
123  for (const btn of toolbar.querySelectorAll("[data-plaintoolbar-hideuntil]")) {
124    btn.addEventListener("click", () => {
125      console.log("Hiding admin toolbar for 1 hour");
126      plainToolbar.hideUntil(Date.now() + 3600000);
127    });
128  }
129
130  for (const btn of toolbar.querySelectorAll("[data-plaintoolbar-expand]")) {
131    btn.addEventListener("click", () => {
132      plainToolbar.toggleExpand();
133    });
134  }
135
136  // Enable manual resize of the expanded toolbar via drag handle
137  const details = document.getElementById("plaintoolbar-details");
138  if (details) {
139    const handle = details.querySelector("[data-resizer]");
140    const content = handle.nextElementSibling;
141    let isDragging = false;
142    let startY = 0;
143    let startHeight = 0;
144    if (handle && content) {
145      // Initial cursor
146      handle.style.cursor = "grab";
147      // Start dragging
148      handle.addEventListener("mousedown", (e) => {
149        isDragging = true;
150        startY = e.clientY;
151        startHeight = content.offsetHeight;
152        handle.style.cursor = "grabbing";
153        // Prevent text selection while dragging
154        document.body.style.userSelect = "none";
155        e.preventDefault();
156      });
157      // Handle dragging
158      document.addEventListener("mousemove", (e) => {
159        if (!isDragging) return;
160        const delta = e.clientY - startY;
161        // Calculate new height: dragging up increases height
162        let newHeight = startHeight - delta;
163        // Clamp between reasonable bounds
164        const minHeight = 50;
165        const maxHeight = window.innerHeight - 100;
166        newHeight = Math.max(minHeight, Math.min(maxHeight, newHeight));
167        content.style.height = `${newHeight}px`;
168      });
169      // End dragging
170      document.addEventListener("mouseup", () => {
171        if (isDragging) {
172          isDragging = false;
173          handle.style.cursor = "grab";
174          document.body.style.userSelect = "";
175        }
176      });
177    }
178  }
179});