Greasy Fork is available in English.

components

Components library for AniList Edit Multiple Media Simultaneously

Skrip ini tidak untuk dipasang secara langsung. Ini adalah pustaka skrip lain untuk disertakan dengan direktif meta // @require https://update.greasyfork.org/scripts/496874/1387742/components.js

// ==UserScript==
// @name        components
// @license     MIT
// @namespace   rtonne
// @match       https://anilist.co/*
// @version     1.0
// @author      Rtonne
// @description Components library for AniList Edit Multiple Media Simultaneously
// ==/UserScript==

/**
 * Creates a select input.
 * @param {HTMLElement} container A parent element to append the input to.
 * @param {string[]} options The option values for the select input.
 * @returns The select input element.
 */
function createSelectInput(container, options) {
  const input_container_1 = document.createElement("div");
  input_container_1.className = "el-select";
  input_container_1.style.width = "100%";
  container.append(input_container_1);
  const input_container_2 = document.createElement("div");
  input_container_2.className = "el-input el-input--suffix";
  input_container_1.append(input_container_2);
  const input = document.createElement("input");
  input.className = "el-input__inner";
  input.readOnly = true;
  input.autocomplete = "off";
  input_container_2.append(input);
  const input_suffix = document.createElement("span");
  input_suffix.className = "el-input__suffix";
  input_container_2.append(input_suffix);
  const input_suffix_inner = document.createElement("span");
  input_suffix_inner.className = "el-input__suffix-inner";
  input_suffix.append(input_suffix_inner);
  const input_icon = document.createElement("i");
  input_icon.className = "el-select__caret el-input__icon el-icon-arrow-up";
  input_suffix_inner.append(input_icon);

  input.value = options[0];

  const dropdown = document.createElement("div");
  dropdown.className = "el-select-dropdown el-popper";
  dropdown.style.minWidth = "180px";
  dropdown.style.zIndex = "10000";
  dropdown.style.position = "absolute";
  dropdown.style.transition =
    "transform .3s cubic-bezier(.23,1,.32,1),opacity .3s cubic-bezier(.23,1,.32,1)";
  dropdown.style.opacity = "0";
  container.append(dropdown);
  const dropdown_arrow = document.createElement("div");
  dropdown_arrow.className = "popper__arrow";
  dropdown_arrow.style.left = "35px";
  dropdown_arrow.setAttribute("x-arrow", "");
  dropdown.append(dropdown_arrow);
  const dropdown_scrollbar = document.createElement("div");
  dropdown_scrollbar.className = "el-scrollbar";
  dropdown.append(dropdown_scrollbar);
  const dropdown_list_container = document.createElement("div");
  dropdown_list_container.className =
    "el-select-dropdown__wrap el-scrollbar__wrap";
  dropdown_list_container.style.overflow = "auto";
  dropdown_scrollbar.append(dropdown_list_container);
  const dropdown_list = document.createElement("ul");
  dropdown_list.className = "el-scrollbar__view el-select-dropdown__list";
  dropdown_list_container.append(dropdown_list);
  for (const option of options) {
    const dropdown_list_item = document.createElement("li");
    dropdown_list_item.className = "el-select-dropdown__item";
    dropdown_list_item.innerText = option;
    dropdown_list_item.onmousedown = () => {
      input.value = option;
    };
    dropdown_list.append(dropdown_list_item);
  }
  const dropdown_dims = dropdown.getBoundingClientRect();
  const dropdown_arrow_dims = dropdown_arrow.getBoundingClientRect();
  const full_dropdown_height =
    dropdown_dims.height + dropdown_arrow_dims.height;
  dropdown.style.transform = "scaleY(0)";

  function setDropdownPosition() {
    const input_dims = input_container_1.getBoundingClientRect();
    const dropdown_fits_below =
      window.innerHeight - input_dims.bottom >= full_dropdown_height;
    if (dropdown_fits_below) {
      dropdown.style.top = `${window.scrollY + input_dims.bottom}px`;
      dropdown.setAttribute("x-placement", "bottom-start");
      dropdown.style.transformOrigin = "center top";
    } else {
      // Subtract 17px for the margins
      dropdown.style.top = `${
        window.scrollY + input_dims.top - dropdown_dims.height - 17
      }px`;
      dropdown.setAttribute("x-placement", "top-start");
      dropdown.style.transformOrigin = "center bottom";
    }
    dropdown.style.left = `${window.scrollX + input_dims.left}px`;
  }

  new IntersectionObserver(
    () => {
      setDropdownPosition();
    },
    {
      rootMargin: `0px 0px -${dropdown_dims.height}px 0px`,
      threshold: 1,
    }
  ).observe(input_container_1);

  let isFocused = false;
  input_container_1.onclick = (event) => {
    if (isFocused) {
      input.blur();
    } else {
      input.focus();
    }
  };
  input.onmousedown = () => false; // Prevent default

  input.onfocus = () => {
    isFocused = true;
    setDropdownPosition();
    input_container_2.className += " is-focus";
    input_icon.className += " is-reverse";

    for (const item of dropdown_list.children) {
      item.classList.remove("selected");
      if (item.innerText === input.value) {
        item.className += " selected";
      }
    }

    dropdown.style.transform = "scaleY(1)";
    dropdown.style.opacity = "1";
  };

  input.onblur = () => {
    isFocused = false;
    input_container_2.classList.remove("is-focus");
    input_icon.classList.remove("is-reverse");
    dropdown.style.transform = "scaleY(0)";
    dropdown.style.opacity = "0";
  };

  // To prevent the input blurring when clicking the icon
  input_icon.onmousedown = (event) => {
    event.preventDefault();
  };

  return input;
}

/**
 * Creates a number input. Step can be 0 to have step=1 and to not limit values to steppable ones.
 * @returns The number input element.
 * @param {HTMLElement} container A parent element to append the input to.
 * @param {number} max_value The maximum value the input can reach.
 * @param {number} step The step value of the input.
 */
function createNumberInput(container, max_value = Infinity, step = 1) {
  const input_container_1 = document.createElement("div");
  input_container_1.className = "el-input-number is-controls-right";
  input_container_1.style.width = "100%";
  container.append(input_container_1);
  const input_decrease_button = document.createElement("span");
  input_decrease_button.className = "el-input-number__decrease is-disabled";
  input_decrease_button.role = "button";
  input_container_1.append(input_decrease_button);
  const input_decrease_button_arrow = document.createElement("i");
  input_decrease_button_arrow.className = "el-icon-arrow-down";
  input_decrease_button.append(input_decrease_button_arrow);
  const input_increase_button = document.createElement("span");
  input_increase_button.className = "el-input-number__increase";
  input_increase_button.role = "button";
  input_container_1.append(input_increase_button);
  const input_increase_button_arrow = document.createElement("i");
  input_increase_button_arrow.className = "el-icon-arrow-up";
  input_increase_button.append(input_increase_button_arrow);
  const input_container_2 = document.createElement("div");
  input_container_2.className = "el-input";
  input_container_1.append(input_container_2);
  const input = document.createElement("input");
  input.className = "el-input__inner";
  input.type = "number";
  input.min = input.value = 0;
  input.max = max_value;
  input.step = step;
  input_container_2.append(input);

  function setButtonDisabledStatus() {
    if (Number(input.value) <= input_min) {
      input_decrease_button.className += " is-disabled";
    } else {
      input_decrease_button.classList.remove("is-disabled");
    }
    if (Number(input.value) >= input_max) {
      input_increase_button.className += " is-disabled";
    } else {
      input_increase_button.classList.remove("is-disabled");
    }
  }
  input_decrease_button.onclick = () => {
    input.stepDown();
    setButtonDisabledStatus();
  };
  input_increase_button.onclick = () => {
    input.stepUp();
    setButtonDisabledStatus();
  };
  input.oninput = setButtonDisabledStatus;

  const input_max = Number(input.max);
  const input_min = Number(input.min);
  function makeValueValid() {
    // https://stackoverflow.com/questions/17369098/simplest-way-of-getting-the-number-of-decimals-in-a-number-in-javascript
    // Step 0 is a special case that should ignore step validation
    if (step !== 0 && Math.floor(Number(input.value)) !== Number(input.value)) {
      let decimalCount;
      if (input.value.indexOf(".") !== -1 && input.value.indexOf("-") !== -1) {
        decimalCount =
          input.value.split(/[.-]/)[1].length + input.value.split("-")[1] || 0;
      } else if (input.value.indexOf(".") !== -1) {
        decimalCount = input.value.split(".")[1].length || 0;
      } else {
        decimalCount = input.value.split("-")[1] || 0;
      }
      // Using Math.round to clean up arithmetic imprecisions
      const remainder =
        Math.round((Number(input.value) % step) * Math.pow(10, decimalCount)) /
        Math.pow(10, decimalCount);
      input.value = Number(input.value) - remainder;
    }
    if (Number(input.value) > input_max) {
      input.value = input_max;
    }
    if (Number(input.value) < input_min) {
      input.value = input_min;
    }
  }
  input.onblur = makeValueValid;
  input.onkeydown = (event) => {
    if (event.key === "Enter") {
      makeValueValid();
    }
  };

  return input;
}

/**
 * Creates a date input.
 * @param {HTMLElement} container A parent element to append the input to.
 * @returns The date input element.
 */
function createDateInput(container) {
  const input_container = document.createElement("div");
  input_container.className = "el-input el-input--suffix";
  container.append(input_container);
  const input = document.createElement("input");
  input.className = "el-input__inner";
  input.type = "date";
  input.style.width = "100%";
  input_container.append(input);
  const input_icon_container = document.createElement("span");
  input_icon_container.className = "el-input__suffix";
  input_container.append(input_icon_container);
  const input_icon = document.createElement("i");
  input_icon.className = "el-input__icon el-icon-date";
  input_icon.style.pointerEvents = "auto";
  input_icon_container.append(input_icon);

  input_icon.onclick = () => {
    input.showPicker();
  };

  return input;
}

/**
 * Creates a textarea.
 * @param {HTMLElement} container A parent element to append the input to.
 * @returns The textarea element.
 */
function createTextarea(container) {
  const textarea_container = document.createElement("div");
  textarea_container.className = "el-textarea";
  container.append(textarea_container);
  const textarea = document.createElement("textarea");
  textarea.className = "el-textarea__inner";
  textarea.style.minHeight = "34px";
  textarea_container.append(textarea);

  return textarea;
}

/**
 * Creates a checkbox.
 * @param {HTMLElement} container A parent element to append the checkbox to.
 * @param {string} text The text of the checkbox's label.
 * @returns The checkbox element.
 */
function createCheckbox(container, text) {
  const checkbox_container = document.createElement("div");
  checkbox_container.className = "rtonne-anilist-multiselect-checkbox";
  checkbox_container.style.paddingBottom = "4px";
  container.append(checkbox_container);
  const label = document.createElement("label");
  label.innerText = text;
  label.className = "el-checkbox";
  label.style.fontSize = "1.2rem";
  label.style.fontWeight = "400";
  checkbox_container.append(label);
  const middle_label = document.createElement("label");
  middle_label.className = "el-checkbox__input";
  middle_label.style.marginRight = "4px";
  middle_label.style.verticalAlign = "text-top";
  label.prepend(middle_label);
  const button_label = document.createElement("label");
  button_label.className = "el-checkbox__inner";
  button_label.style.cursor = "pointer";
  middle_label.append(button_label);
  const checkbox = document.createElement("input");
  checkbox.type = "checkbox";
  checkbox.className = "el-checkbox__original";
  button_label.prepend(checkbox);

  checkbox.onchange = () => {
    if (checkbox.checked) {
      label.className += " is-checked";
      middle_label.className += " is-checked";
    } else {
      label.classList.remove("is-checked");
      middle_label.classList.remove("is-checked");
    }
  };

  return checkbox;
}

/**
 * Creates an indeterminate checkbox.
 * When the return checkbox.readOnly or checkbox.indeterminate is true,
 *    it is on a 3rd state where it isn't on nor off.
 * From https://css-tricks.com/indeterminate-checkboxes/.
 * @param {HTMLElement} container A parent element to append the input to.
 * @param {string} text The text of the checkbox's label.
 * @returns The checkbox element.
 */
function createIndeterminateCheckbox(container, text) {
  const checkbox_container = document.createElement("div");
  checkbox_container.className = "rtonne-anilist-multiselect-checkbox";
  checkbox_container.style.paddingBottom = "4px";
  container.append(checkbox_container);
  const label = document.createElement("label");
  label.innerText = text;
  label.className = "el-checkbox";
  label.style.fontSize = "1.2rem";
  label.style.fontWeight = "400";
  checkbox_container.append(label);
  const middle_label = document.createElement("label");
  middle_label.className = "el-checkbox__input";
  middle_label.style.marginRight = "4px";
  middle_label.style.verticalAlign = "text-top";
  label.prepend(middle_label);
  const button_label = document.createElement("label");
  button_label.className = "el-checkbox__inner";
  button_label.style.cursor = "pointer";
  middle_label.append(button_label);
  const checkbox = document.createElement("input");
  checkbox.type = "checkbox";
  checkbox.className = "el-checkbox__original";
  checkbox.readOnly = true;
  checkbox.indeterminate = true;
  button_label.prepend(checkbox);

  checkbox.onclick = () => {
    if (checkbox.readOnly) {
      checkbox.checked = checkbox.readOnly = false;
    } else if (!checkbox.checked) {
      checkbox.readOnly = checkbox.indeterminate = true;
    }
  };

  checkbox.onchange = () => {
    if (checkbox.checked) {
      label.className += " is-checked";
      middle_label.className += " is-checked";
    } else {
      label.classList.remove("is-checked");
      middle_label.classList.remove("is-checked");
    }
  };

  return checkbox;
}

/**
 * Creates a button.
 * @param {HTMLElement} container A parent element to append the input to.
 * @param {string} text The text inside the button.
 * @returns The button element.
 */
function createButton(container, text) {
  const button = document.createElement("button");
  button.innerText = text;
  button.style.backgroundColor = "rgb(var(--color-blue-600))";
  button.style.color = "rgb(var(--color-text-bright))";
  button.style.border = "none";
  button.style.borderRadius = "3px";
  button.style.cursor = "pointer";
  button.style.fontSize = "12px";
  button.style.padding = "9px 15px";
  button.style.transition = ".2s";
  container.append(button);
  return button;
}

/**
 * Creates a cancel button.
 * @param {HTMLElement} container A parent element to append the input to.
 * @param {string} text The text inside the button.
 * @returns The button element.
 */
function createCancelButton(container, text) {
  const button = createButton(container, text);
  button.style.backgroundColor = "rgba(var(--color-background),.8)";
  button.style.color = "rgb(var(--color-text))";
  return button;
}

/**
 * Creates a cancel button with a lighter background.
 * @param {HTMLElement} container A parent element to append the input to.
 * @param {string} text The text inside the button.
 * @returns The button element.
 */
function createCancelLighterButton(container, text) {
  const button = createButton(container, text);
  button.style.backgroundColor = "rgba(var(--color-foreground),.8)";
  button.style.color = "rgb(var(--color-text))";
  return button;
}

/**
 * Creates a danger button.
 * @param {HTMLElement} container A parent element to append the input to.
 * @param {string} text The text inside the button.
 * @returns The button element.
 */
function createDangerButton(container, text) {
  const button = createButton(container, text);
  button.style.backgroundColor = "rgba(var(--color-red),.8)";
  button.style.color = "rgb(var(--color-white))";
  return button;
}

/**
 * Creates a confirmation popup.
 * @param {string} title_text A text/html title for the popup.
 * @param {string} message_text The text/html content of the popup.
 * @returns The confirm button of the popup.
 */
function createConfirmPopup(title_text, message_text) {
  const modal = document.createElement("div");
  modal.className = "v-modal";
  modal.style.zIndex = "99999";
  document.body.append(modal);

  const wrapper = document.createElement("div");
  wrapper.className = "el-message-box__wrapper";
  wrapper.style.zIndex = "100000";
  document.body.append(wrapper);
  const container = document.createElement("div");
  container.className = "el-message-box";
  wrapper.append(container);

  const header = document.createElement("div");
  header.className = "el-message-box__header";
  container.append(header);
  const title = document.createElement("div");
  title.className = "el-message-box__title";
  title.innerHTML = `<span>${title_text}</span>`;
  header.append(title);
  const close_button = document.createElement("button");
  close_button.className = "el-message-box__headerbtn";
  header.append(close_button);
  const close_button_icon = document.createElement("i");
  close_button_icon.className = "el-message-box__close el-icon-close";
  close_button.append(close_button_icon);

  const content = document.createElement("div");
  content.className = "el-message-box__content";
  container.append(content);
  const message = document.createElement("div");
  message.className = "el-message-box__message";
  message.innerHTML = `<p>${message_text}</p>`;
  content.append(message);

  const buttons = document.createElement("div");
  buttons.className = "el-message-box__btns";
  container.append(buttons);
  const cancel_button = createCancelButton(buttons, "Cancel");
  const confirm_button = createButton(buttons, "Confirm");

  wrapper.addEventListener("click", (e) => {
    // e.stopPropagation() doesn't seem to work so this condition is here
    if (e.target !== wrapper) {
      return;
    }
    modal.remove();
    wrapper.remove();
  });
  close_button.onclick = cancel_button.onclick = () => {
    modal.remove();
    wrapper.remove();
  };

  // Used .addEventListener instead of .onclick
  // so either can be used outside this function
  confirm_button.addEventListener("click", () => {
    modal.remove();
    wrapper.remove();
  });

  return confirm_button;
}

/**
 * Creates an updatable cancel popup.
 * @param {string} initial_title
 * @param {HTMLElement} initial_content
 * @returns The two elements that close the popup with the click event, two functions to update the popup, and a function to close the popup.
 */
function createUpdatableCancelPopup(initial_title, initial_content) {
  const modal = document.createElement("div");
  modal.className = "v-modal";
  modal.style.zIndex = "99999";
  document.body.append(modal);

  const wrapper = document.createElement("div");
  wrapper.className = "el-message-box__wrapper";
  wrapper.style.zIndex = "100000";
  document.body.append(wrapper);
  const container = document.createElement("div");
  container.className = "el-message-box";
  wrapper.append(container);

  const header = document.createElement("div");
  header.className = "el-message-box__header";
  container.append(header);
  const title_element = document.createElement("div");
  title_element.className = "el-message-box__title";
  title_element.innerHTML = `<span>${initial_title}</span>`;
  header.append(title_element);

  const content_element = document.createElement("div");
  content_element.className = "el-message-box__content";
  container.append(content_element);
  const message = document.createElement("div");
  message.className = "el-message-box__message";
  message.replaceChildren(initial_content);
  content_element.append(message);

  const buttons = document.createElement("div");
  buttons.className = "el-message-box__btns";
  container.append(buttons);
  const cancel_button = createCancelButton(buttons, "Cancel");

  function closePopup() {
    modal.remove();
    wrapper.remove();
  }

  // Used .addEventListener instead of .onclick
  // so either can be used outside this function
  wrapper.addEventListener("click", (e) => {
    // e.stopPropagation() doesn't seem to work so this condition is here
    if (e.target !== wrapper) {
      return;
    }
    closePopup();
  });
  cancel_button.addEventListener("click", () => {
    closePopup();
  });

  function changeTitle(title) {
    title_element.innerHTML = `<span>${title}</span>`;
  }

  function changeContent(content) {
    message.replaceChildren(content);
  }

  return {
    popup_wrapper: wrapper,
    popup_cancel_button: cancel_button,
    changePopupTitle: changeTitle,
    changePopupContent: changeContent,
    closePopup: closePopup,
  };
}

/**
 * Creates entry specific content to add to a popup.
 * @param {string} text Text to display to the left of the cover.
 * @param {string} cover The cover to add as a background-image style.
 * @param {number} current_index The index of the current entry being show.
 * @param {number} total The total entries going to be shown.
 * @returns
 */
function createEntryPopupContent(text, cover, current_index, total) {
  const content = document.createElement("div");
  content.style.display = "flex";
  content.style.flexWrap = "wrap";
  content.style.flexDirection = "column";
  content.style.gap = "10px";
  content.style.justifyContent = "center";
  content.style.alignItems = "center";
  content.style.textAlign = "center";
  const content_text = document.createElement("span");
  content_text.innerHTML = text;
  content_text.style.flexGrow = "1";
  content.append(content_text);
  const content_image = document.createElement("div");
  content_image.style.backgroundImage = cover;
  content_image.style.backgroundPosition = "50%";
  content_image.style.backgroundRepeat = "no-repeat";
  content_image.style.backgroundSize = "cover";
  content_image.style.borderRadius = "3px";
  content_image.style.minHeight = "210px";
  content_image.style.minWidth = "150px";
  content.append(content_image);
  const bar = document.createElement("div");
  bar.style.width = "100%";
  bar.style.height = "24px";
  bar.style.display = "flex";
  bar.style.justifyContent = "center";
  bar.style.alignItems = "center";
  bar.style.textAlign = "center";
  bar.style.background = `linear-gradient(90deg, rgb(var(--color-blue)) ${Math.floor(
    (current_index / total) * 100
  )}%, rgb(var(--color-background)) 0)`;
  bar.style.borderRadius = "3px";
  content.append(bar);
  const bar_text = document.createElement("span");
  bar_text.innerText = `${current_index} / ${total}`;
  bar_text.style.boxShadow = `inset 0 0 0 100vw rgb(var(--color-background)),
                              0 0 0 2px rgb(var(--color-background)),
                              0 0 3px 3px rgb(var(--color-background))`;
  bar_text.style.borderRadius = "1px";
  bar.appendChild(bar_text);
  return content;
}

/**
 * Creates an error popup.
 * @param {string} text The message/content of the popup.
 * @returns {Promise<boolean>} A promise that returns if the user asked to cancel.
 */
function createErrorPopup(text) {
  const modal = document.createElement("div");
  modal.className = "v-modal";
  modal.style.zIndex = "99999";
  document.body.append(modal);

  const wrapper = document.createElement("div");
  wrapper.className = "el-message-box__wrapper";
  wrapper.style.zIndex = "100000";
  document.body.append(wrapper);
  const container = document.createElement("div");
  container.className = "el-message-box";
  wrapper.append(container);

  const header = document.createElement("div");
  header.className = "el-message-box__header";
  container.append(header);
  const title = document.createElement("div");
  title.className = "el-message-box__title";
  title.innerHTML = `<span>ERROR<span>`;
  header.append(title);
  const close_button = document.createElement("button");
  close_button.className = "el-message-box__headerbtn";
  header.append(close_button);
  const close_button_icon = document.createElement("i");
  close_button_icon.className = "el-message-box__close el-icon-close";
  close_button.append(close_button_icon);

  const content = document.createElement("div");
  content.className = "el-message-box__content";
  container.append(content);
  const message = document.createElement("div");
  message.className = "el-message-box__message";
  message.innerHTML = `<p>${text}</p>`;
  content.append(message);

  const buttons = document.createElement("div");
  buttons.className = "el-message-box__btns";
  container.append(buttons);
  const cancel_button = createDangerButton(buttons, "Cancel");
  const retry_button = createButton(buttons, "Retry");

  return new Promise((resolve) => {
    wrapper.onclick = (e) => {
      // e.stopPropagation() doesn't seem to work so this condition is here
      if (e.target !== wrapper) {
        return;
      }
      modal.remove();
      wrapper.remove();
      resolve(true);
    };
    close_button.onclick = cancel_button.onclick = () => {
      modal.remove();
      wrapper.remove();
      resolve(true);
    };

    retry_button.onclick = () => {
      modal.remove();
      wrapper.remove();
      resolve(false);
    };
  });
}

/**
 * Creates a popup.
 * @param {string} title_text A text/html title for the popup.
 * @param {string} message_text The text/html content of the popup.
 * @returns {Promise<void>}
 */
function createPopup(title_text, message_text) {
  const modal = document.createElement("div");
  modal.className = "v-modal";
  modal.style.zIndex = "99999";
  document.body.append(modal);

  const wrapper = document.createElement("div");
  wrapper.className = "el-message-box__wrapper";
  wrapper.style.zIndex = "100000";
  document.body.append(wrapper);
  const container = document.createElement("div");
  container.className = "el-message-box";
  wrapper.append(container);

  const header = document.createElement("div");
  header.className = "el-message-box__header";
  container.append(header);
  const title = document.createElement("div");
  title.className = "el-message-box__title";
  title.innerHTML = `<span>${title_text}</span>`;
  header.append(title);
  const close_button = document.createElement("button");
  close_button.className = "el-message-box__headerbtn";
  header.append(close_button);
  const close_button_icon = document.createElement("i");
  close_button_icon.className = "el-message-box__close el-icon-close";
  close_button.append(close_button_icon);

  const content = document.createElement("div");
  content.className = "el-message-box__content";
  container.append(content);
  const message = document.createElement("div");
  message.className = "el-message-box__message";
  message.innerHTML = `<p>${message_text}</p>`;
  content.append(message);

  const buttons = document.createElement("div");
  buttons.className = "el-message-box__btns";
  container.append(buttons);
  const ok_button = createButton(buttons, "Ok");

  return new Promise((resolve) => {
    wrapper.onclick = (e) => {
      // e.stopPropagation() doesn't seem to work so this condition is here
      if (e.target !== wrapper) {
        return;
      }
      modal.remove();
      wrapper.remove();
      resolve();
    };
    close_button.onclick = ok_button.onclick = () => {
      modal.remove();
      wrapper.remove();
      resolve();
    };
  });
}