Libib - Custom status indicator style

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

当前为 2025-02-06 提交的版本,查看 最新版本

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 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            1.1.0
// @license            GPL-3.0-or-later; https://raw.githubusercontent.com/JetpackCat-IT/libib-custom-status-style/master/LICENSE
// @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;
        },
      },
      number: {
        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: "number",
            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
      borderHeight: {
        label: "Border height", // Appears next to field
        type: "number", // Makes this setting a select field
        default: 5, // 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");
      let border_height = GM_settings.get("borderHeight");
      // 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" ? `` : `-`
                }${border_height}px;
            }
            .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,
      })
    );
  };
})();