Libib - Custom status indicator style

Set a custom color and style for libib.com item status indicator

目前為 2025-02-05 提交的版本,檢視 最新版本

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

You will need to install an extension such as Tampermonkey to install this script.

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==UserScript==
// @name               Libib - Custom status indicator style
// @name:it            Libib - Stile indicatore stato personalizzato
// @description        Set a custom color and style for libib.com item status indicator
// @description:it     Modifica i colori e lo stile dell'indicatore dello stato di un oggetto di libib.com
// @author             JetpackCat
// @namespace          https://github.com/JetpackCat-IT/libib-custom-status-style
// @supportURL         https://github.com/JetpackCat-IT/libib-custom-status-style/issues
// @icon               https://github.com/JetpackCat-IT/libib-custom-status-style/raw/v1.0.0/img/icon_64.png
// @version            0.0.1pre
// @license            GPL-3.0
// @match              https://www.libib.com/library
// @icon               https://www.libib.com/img/favicon.png
// @run-at             document-idle
// @require            https://openuserjs.org/src/libs/sizzle/GM_config.js
// @grant              GM_getValue
// @grant              GM_setValue
// @grant              GM.getValue
// @grant              GM.setValue

// ==/UserScript==

(function () {
  "use strict";

  // Get libib sidebar menu. The settings button will be added to the sidebar
  const libib_sidebar_menu = document.getElementById("primary-menu");

  // Create the element, it needs to be an <a> tag inside an <li> tag
  const settings_button_a = document.createElement("a");
  settings_button_a.appendChild(
    document.createTextNode("Libib status settings")
  );

  // Create <li> element and insert <a> element inside
  const settings_button_li = document.createElement("li");
  settings_button_li.appendChild(settings_button_a);

  // Assign click event handler to open the menu settings
  settings_button_li.addEventListener("click", function () {
    gmc.open();
  });

  // Add <li> element to the sidebar
  libib_sidebar_menu.appendChild(settings_button_li);

  // Create a container for the configuration elements
  const config_container = document.createElement("div");
  document.body.appendChild(config_container);

  // Adapt container background color and shadow based on libib theme (dark/light)
  const is_dark_scheme = document.body.classList.contains("dark");
  let background_color = "#fefefe";
  let shadow_color = "#838383";

  if (is_dark_scheme) {
    background_color = "#1b1b1b";
    shadow_color = "#e7e7e7";
  }
  const config_panel_css = `#libib_status_config{padding: 20px !important; background-color: ${background_color}; box-shadow: 0px 0px 9px 3px ${shadow_color}}; `;

  let gmc = new GM_config({
    id: "libib_status_config", // The id used for this instance of GM_config
    title: "Script Settings", // Panel Title
    types: {
      // Create color input type
      color: {
        default: null,
        toNode: function () {
          var field = this.settings,
            value = this.value,
            id = this.id,
            create = this.create,
            slash = null,
            retNode = create("div", {
              className: "config_var",
              id: this.configId + "_" + id + "_var",
              title: field.title || "",
            });

          // Create the field lable
          retNode.appendChild(
            create("label", {
              innerHTML: field.label,
              id: this.configId + "_" + id + "_field_label",
              for: this.configId + "_field_" + id,
              className: "field_label",
            })
          );
          // Create the actual input element
          var props = {
            id: this.configId + "_field_" + id,
            type: "color",
            value: value ?? "",
          };
          // Actually create and append the input element
          retNode.appendChild(create("input", props));
          return retNode;
        },
        toValue: function () {
          let input = document.getElementById(
            `${this.configId}_field_${this.id}`
          );
          return input.value;
        },
        reset: function () {
          let input = document.getElementById(
            `${this.configId}_field_${this.id}`
          );
          input.value = this.default;
        },
      },
    },
    // Fields object
    fields: {
      // This is the id of the field
      type: {
        label: "Indicator type", // Appears next to field
        type: "radio", // Makes this setting a radio field
        options: ["Triangle", "Border"], // Default = triangle
        default: "Triangle", // Default value if user doesn't change it
      },
      // This is the id of the field
      trianglePosition: {
        label: "Triangle position", // Appears next to field
        type: "select", // Makes this setting a select field
        options: ["Top left", "Top right", "Bottom left", "Bottom right"],
        default: "Top left", // Default value if user doesn't change it
      },
      // This is the id of the field
      borderPosition: {
        label: "Border position", // Appears next to field
        type: "select", // Makes this setting a select field
        options: ["Top", "Bottom"],
        default: "Top", // Default value if user doesn't change it
      },
      // This is the id of the field
      colorNotBegun: {
        label: '"Not begun" Color', // Appears next to field
        type: "color", // Makes this setting a text field
        default: "#ffffff", // Default value if user doesn't change it
      },
      // This is the id of the field
      colorCompleted: {
        label: '"Completed" Color', // Appears next to field
        type: "color", // Makes this setting a text field
        default: "#76eb99", // Default value if user doesn't change it
      },
      // This is the id of the field
      colorProgress: {
        label: '"In progress" Color', // Appears next to field
        type: "color", // Makes this setting a text field
        default: "#ffec8a", // Default value if user doesn't change it
      },
      // This is the id of the field
      colorAbandoned: {
        label: '"Abandoned" Color', // Appears next to field
        type: "color", // Makes this setting a text field
        default: "#ff7a7a", // Default value if user doesn't change it
      },
    },
    css: config_panel_css,
    frame: config_container,
    // Callback functions object
    events: {
      init: function () {
        let css = generateCSS(this);
        setCustomStyle(css);
      },
      save: function () {
        let css = generateCSS(this);
        setCustomStyle(css);
        this.close();
      },
    },
  });

  const generateCSS = function (GM_settings) {
    if (GM_settings == null) GM_settings = gmc;

    const not_begun_color = GM_settings.get("colorNotBegun");
    const completed_color = GM_settings.get("colorCompleted");
    const in_progress_color = GM_settings.get("colorProgress");
    const abandoned_color = GM_settings.get("colorAbandoned");

    let css_style = "";
    // Make libib buttons still clickable
    css_style += `
        .quick-edit-link{
            z-index: 10;
        }
        .batch-select{
            z-index: 10;
        }
        `;
    // Set the save, close and reset buttons color to white id dark mode
    css_style += `
        body.dark #libib_status_config_resetLink,body.dark #libib_status_config_saveBtn,body.dark #libib_status_config_closeBtn{
        color:white!important
        }`;

    // Triangle style
    if (GM_settings.get("type") == "Triangle") {
      let triangle_position = GM_settings.get("trianglePosition");
      if (triangle_position == "Top left") {
        css_style += `
            .cover .completed.cover-wrapper::after {
                border-left-color: ${completed_color};
                border-top-color: ${completed_color};
            }
            .cover .in-progress.cover-wrapper::after {
                border-left-color: ${in_progress_color};
                border-top-color: ${in_progress_color};
            }
            .cover .abandoned.cover-wrapper::after {
                border-left-color: ${abandoned_color};
                border-top-color: ${abandoned_color};
            }
            .cover .not-begun.cover-wrapper::after {
                border-left-color: ${not_begun_color};
                border-top-color: ${not_begun_color};
            }
            `;
      } else if (triangle_position == "Top right") {
        css_style += `
                .cover .cover-wrapper::after{
                right: 0;
                left: auto;
                }
            .cover .completed.cover-wrapper::after {
                border-left-color: transparent;
                border-right-color: ${completed_color};
                border-top-color: ${completed_color};
            }
            .cover .in-progress.cover-wrapper::after {
                border-left-color: transparent;
                border-right-color: ${in_progress_color};
                border-top-color: ${in_progress_color};
            }
            .cover .abandoned.cover-wrapper::after {
                border-left-color: transparent;
                border-right-color: ${abandoned_color};
                border-top-color: ${abandoned_color};
            }
            .cover .not-begun.cover-wrapper::after {
                border-left-color: transparent;
                border-right-color: ${not_begun_color};
                border-top-color: ${not_begun_color};
            }
            `;
      } else if (triangle_position == "Bottom left") {
        css_style += `
                .cover .cover-wrapper::after{
                bottom: 0;
                top: auto;
                }
            .cover .completed.cover-wrapper::after {
                border-top-color: transparent;
                border-left-color: ${completed_color};
                border-bottom-color: ${completed_color};
            }
            .cover .in-progress.cover-wrapper::after {
                border-top-color: transparent;
                border-left-color: ${in_progress_color};
                border-bottom-color: ${in_progress_color};
            }
            .cover .abandoned.cover-wrapper::after {
                border-top-color: transparent;
                border-left-color: ${abandoned_color};
                border-bottom-color: ${abandoned_color};
            }
            .cover .not-begun.cover-wrapper::after {
                border-top-color: transparent;
                border-left-color: ${not_begun_color};
                border-bottom-color: ${not_begun_color};
            }
            `;
      } else if (triangle_position == "Bottom right") {
        css_style += `
                .cover .cover-wrapper::after{
                bottom: 0;
                top: auto;
                left: auto;
                right: 0;
                }
            .cover .completed.cover-wrapper::after {
                border-top-color: transparent;
                border-left-color: transparent;
                border-right-color: ${completed_color};
                border-bottom-color: ${completed_color};
            }
            .cover .in-progress.cover-wrapper::after {
                border-top-color: transparent;
                border-left-color: transparent;
                border-right-color: ${in_progress_color};
                border-bottom-color: ${in_progress_color};
            }
            .cover .abandoned.cover-wrapper::after {
                border-top-color: transparent;
                border-left-color: transparent;
                border-right-color: ${abandoned_color};
                border-bottom-color: ${abandoned_color};
            }
            .cover .not-begun.cover-wrapper::after {
                border-top-color: transparent;
                border-left-color: transparent;
                border-right-color: ${not_begun_color};
                border-bottom-color: ${not_begun_color};
            }
            `;
      }
    } else if (GM_settings.get("type") == "Border") {
      let border_position = GM_settings.get("borderPosition");
      // The box-shadow prevents the click on the item, so it needs to be hidden on hover
      css_style += `
            .cover-wrapper {
                --shadow-y: ${border_position == "Top" ? "5px" : "-5px"};
            }
            .cover-wrapper:hover::after {
                display:none!important;
                --shadow-y: 0px;
                transition: all 0.25s;
                transition-behavior: allow-discrete;
             }`;

      css_style += `
            .cover .cover-wrapper::before, .cover .cover-wrapper::after {
                width: 100%;
                height: 100%;
                border-radius: 4px;
                display: block;
                border: none;
                z-index: 0;
            }
            .cover .completed.cover-wrapper::after {
                box-shadow: inset 0px var(--shadow-y) ${completed_color};
            }
            .cover .in-progress.cover-wrapper::after {
                box-shadow: inset 0px var(--shadow-y) ${in_progress_color};
            }
            .cover .abandoned.cover-wrapper::after {
                box-shadow: inset 0px var(--shadow-y) ${abandoned_color};
            }
            .cover .not-begun.cover-wrapper::after {
                box-shadow: inset 0px var(--shadow-y) ${not_begun_color};
            }
            `;
    }
    return css_style;
  };

  const setCustomStyle = function (css) {
    // Remove existing style if present
    const existingStyle = document.getElementById(
      "libib-custom-status-indicator-style"
    );
    if (existingStyle != null) {
      existingStyle.remove();
    }

    // Add style tag to document
    document.head.append(
      Object.assign(document.createElement("style"), {
        type: "text/css",
        id: "libib-custom-status-indicator-style",
        textContent: css,
      })
    );
  };
})();