Download
126 lines 3.6 KiBJavaScriptModified 2026-08-01 raw
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126
// Right-click settings for a bar widget: its schema-driven settings
// rendered inline in a pop-down, then a separator and links to the bar
// layout editor and the full divebar settings window. Popup mechanics
// live in popup.js and the form rows (with their control CSS) in
// settings-form.js; persistence stays with the caller via
// onChange(key, value).

import { createPopup } from "./popup.js";
import { placeAtX } from "./panel.js";
import { renderSettingsRows, hasFocusedControl } from "./settings-form.js";

const CSS = `
  .dwset {
    min-width: 230px;
    max-width: 360px;
    /* The window is only POPUP_SPACE (320px) tall below the bar; the
       rows scroll instead of getting clipped (same cap as .dmenu). */
    max-height: 290px;
    display: flex;
    flex-direction: column;
    padding: 10px 12px;
    font-size: calc(var(--bar-font) * 0.78);
    color: var(--bar-fg);
  }
  .dwset-title { font-weight: 600; margin: 2px 4px 10px; flex: none; }
  .dwset-rows {
    display: flex;
    flex-direction: column;
    gap: 9px;
    overflow-y: auto;
    min-height: 0;
  }
  .dwset-rows .dform-row { padding: 0 4px; }
  .dwset-sep, .dwset-global { flex: none; }
  .dwset-none { color: var(--bar-fg-dim); padding: 0 4px; }
  .dwset-sep { height: 1px; background: rgba(255, 255, 255, 0.09); margin: 10px 0 4px; }
  .dwset-global {
    display: block;
    width: 100%;
    border: none;
    background: transparent;
    color: var(--bar-fg);
    font: inherit;
    text-align: left;
    padding: 6px 8px;
    border-radius: 8px;
    cursor: pointer;
  }
  .dwset-global:hover { background: var(--chip-bg-hover); }
`;

let stylesInjected = false;

// x (optional): clientX of the opening right-click; the popup drops
// down from there, context-menu style, instead of centering on the
// widget.
export function openWidgetSettings({
  anchor,
  invoke,
  title,
  schemas,
  options,
  onChange,
  onEditLayout,
  onOpenGlobal,
  x,
}) {
  if (!stylesInjected) {
    stylesInjected = true;
    const style = document.createElement("style");
    style.dataset.lib = "widget-settings-popup";
    style.textContent = CSS;
    document.head.appendChild(style);
  }

  let bodyEl = null;
  const popup = createPopup({
    anchor,
    className: "dwset",
    place: x != null ? (el) => placeAtX(el, x) : undefined,
    holdOpen: () => hasFocusedControl(bodyEl),
    render(el) {
      bodyEl = el;
      const titleEl = document.createElement("div");
      titleEl.className = "dwset-title";
      titleEl.textContent = `${title} settings`;
      el.append(titleEl);

      const rows = document.createElement("div");
      rows.className = "dwset-rows";
      if (!schemas.length) {
        const none = document.createElement("div");
        none.className = "dwset-none";
        none.textContent = "No settings for this widget.";
        rows.append(none);
      }
      renderSettingsRows(rows, { schemas, options, invoke, onChange });
      el.append(rows);

      const sep = document.createElement("div");
      sep.className = "dwset-sep";
      el.append(sep);
      if (onEditLayout) {
        const edit = document.createElement("button");
        edit.className = "dwset-global";
        edit.textContent = "Edit bar layout…";
        edit.addEventListener("click", () => {
          popup.close();
          onEditLayout();
        });
        el.append(edit);
      }
      const global = document.createElement("button");
      global.className = "dwset-global";
      global.textContent = "divebar settings…";
      global.addEventListener("click", () => {
        popup.close();
        onOpenGlobal();
      });
      el.append(global);
    },
  });

  popup.open();
  return popup;
}