mirror of
https://github.com/easydiffusion/easydiffusion.git
synced 2025-08-14 18:25:42 +02:00
Image Modifier UI Overhaul
Additionally also fixed body overflowing when on mobile. This is unrelated to the overhaul.
This commit is contained in:
@ -1,14 +1,21 @@
|
||||
let activeTags = []
|
||||
let modifiers = []
|
||||
let customModifiersGroupElement = undefined
|
||||
let customModifiersInitialContent
|
||||
let customModifiersInitialContent = ""
|
||||
let modifierPanelFreezed = false
|
||||
|
||||
let modifiersMainContainer = document.querySelector("#editor-modifiers")
|
||||
let modifierDropdown = document.querySelector("#image-modifier-dropdown")
|
||||
let editorModifiersContainer = document.querySelector("#editor-modifiers")
|
||||
let editorModifierEntries = document.querySelector("#editor-modifiers-entries")
|
||||
let editorModifierTagsList = document.querySelector("#editor-inputs-tags-list")
|
||||
let editorTagsContainer = document.querySelector("#editor-inputs-tags-container")
|
||||
let modifierCardSizeSlider = document.querySelector("#modifier-card-size-slider")
|
||||
let previewImageField = document.querySelector("#preview-image")
|
||||
let modifierSettingsBtn = document.querySelector("#modifier-settings-btn")
|
||||
let modifiersContainerSizeBtn = document.querySelector("#modifiers-container-size-btn")
|
||||
let modifiersCloseBtn = document.querySelector("#modifiers-close-button")
|
||||
let modifiersCollapsiblesBtn = document.querySelector("#modifiers-action-collapsibles-btn")
|
||||
let modifierSettingsOverlay = document.querySelector("#modifier-settings-config")
|
||||
let customModifiersTextBox = document.querySelector("#custom-modifiers-input")
|
||||
let customModifierEntriesToolbar = document.querySelector("#editor-modifiers-entries-toolbar")
|
||||
@ -18,31 +25,31 @@ const activeCardClass = "modifier-card-active"
|
||||
const CUSTOM_MODIFIERS_KEY = "customModifiers"
|
||||
|
||||
function createModifierCard(name, previews, removeBy) {
|
||||
const modifierCard = document.createElement("div")
|
||||
let style = previewImageField.value
|
||||
let styleIndex = style == "portrait" ? 0 : 1
|
||||
let cardPreviewImageType = previewImageField.value
|
||||
|
||||
const modifierCard = document.createElement("div")
|
||||
modifierCard.className = "modifier-card"
|
||||
modifierCard.innerHTML = `
|
||||
<div class="modifier-card-overlay"></div>
|
||||
<div class="modifier-card-image-container">
|
||||
<div class="modifier-card-image-overlay">+</div>
|
||||
<p class="modifier-card-error-label"></p>
|
||||
<p class="modifier-card-error-label">No Image</p>
|
||||
<img onerror="this.remove()" alt="Modifier Image" class="modifier-card-image">
|
||||
</div>
|
||||
<div class="modifier-card-container">
|
||||
<div class="modifier-card-label"><p></p></div>
|
||||
<div class="modifier-card-label">
|
||||
<span class="long-label hidden"></span>
|
||||
<p class="regular-label"></p>
|
||||
</div>
|
||||
</div>`
|
||||
|
||||
const image = modifierCard.querySelector(".modifier-card-image")
|
||||
const errorText = modifierCard.querySelector(".modifier-card-error-label")
|
||||
const label = modifierCard.querySelector(".modifier-card-label")
|
||||
|
||||
errorText.innerText = "No Image"
|
||||
const longLabel = modifierCard.querySelector(".modifier-card-label span.long-label")
|
||||
const regularLabel = modifierCard.querySelector(".modifier-card-label p.regular-label")
|
||||
|
||||
if (typeof previews == "object") {
|
||||
image.src = previews[styleIndex] // portrait
|
||||
image.setAttribute("preview-type", style)
|
||||
image.src = previews[cardPreviewImageType == "portrait" ? 0 : 1] // 0 index is portrait, 1 landscape
|
||||
image.setAttribute("preview-type", cardPreviewImageType)
|
||||
} else {
|
||||
image.remove()
|
||||
}
|
||||
@ -50,24 +57,32 @@ function createModifierCard(name, previews, removeBy) {
|
||||
const maxLabelLength = 30
|
||||
const cardLabel = removeBy ? name.replace("by ", "") : name
|
||||
|
||||
if (cardLabel.length <= maxLabelLength) {
|
||||
label.querySelector("p").innerText = cardLabel
|
||||
} else {
|
||||
const tooltipText = document.createElement("span")
|
||||
tooltipText.className = "tooltip-text"
|
||||
tooltipText.innerText = name
|
||||
|
||||
label.classList.add("tooltip")
|
||||
label.appendChild(tooltipText)
|
||||
|
||||
label.querySelector("p").innerText = cardLabel.substring(0, maxLabelLength) + "..."
|
||||
function getFormattedLabel(length) {
|
||||
if (cardLabel?.length <= length) {
|
||||
return cardLabel
|
||||
} else {
|
||||
return cardLabel.substring(0, length) + "..."
|
||||
}
|
||||
}
|
||||
label.querySelector("p").dataset.fullName = name // preserve the full name
|
||||
|
||||
modifierCard.dataset.fullName = name // preserve the full name
|
||||
regularLabel.dataset.fullName = name // preserve the full name, legacy support for older plugins
|
||||
|
||||
longLabel.innerText = getFormattedLabel(maxLabelLength * 2)
|
||||
regularLabel.innerText = getFormattedLabel(maxLabelLength)
|
||||
|
||||
if (cardLabel.length > maxLabelLength) {
|
||||
modifierCard.classList.add("support-long-label")
|
||||
|
||||
if (cardLabel.length > maxLabelLength * 2) {
|
||||
modifierCard.title = `"${name}"`
|
||||
}
|
||||
}
|
||||
|
||||
return modifierCard
|
||||
}
|
||||
|
||||
function createModifierGroup(modifierGroup, initiallyExpanded, removeBy) {
|
||||
function createModifierGroup(modifierGroup, isInitiallyOpen, removeBy) {
|
||||
const title = modifierGroup.category
|
||||
const modifiers = modifierGroup.modifiers
|
||||
|
||||
@ -78,8 +93,8 @@ function createModifierGroup(modifierGroup, initiallyExpanded, removeBy) {
|
||||
const modifiersEl = document.createElement("div")
|
||||
modifiersEl.classList.add("collapsible-content", "editor-modifiers-leaf")
|
||||
|
||||
if (initiallyExpanded === true) {
|
||||
titleEl.className += " active"
|
||||
if (isInitiallyOpen === true) {
|
||||
titleEl.classList.add("active")
|
||||
}
|
||||
|
||||
modifiers.forEach((modObj) => {
|
||||
@ -126,7 +141,7 @@ function createModifierGroup(modifierGroup, initiallyExpanded, removeBy) {
|
||||
e.appendChild(titleEl)
|
||||
e.appendChild(modifiersEl)
|
||||
|
||||
editorModifierEntries.insertBefore(e, customModifierEntriesToolbar.nextSibling)
|
||||
editorModifierEntries.prepend(e)
|
||||
|
||||
return e
|
||||
}
|
||||
@ -149,7 +164,10 @@ async function loadModifiers() {
|
||||
res.reverse()
|
||||
|
||||
res.forEach((modifierGroup, idx) => {
|
||||
createModifierGroup(modifierGroup, idx === res.length - 1, modifierGroup === "Artist" ? true : false) // only remove "By " for artists
|
||||
const isInitiallyOpen = false // idx === res.length - 1
|
||||
const removeBy = modifierGroup === "Artist" ? true : false // only remove "By " for artists
|
||||
|
||||
createModifierGroup(modifierGroup, isInitiallyOpen, removeBy)
|
||||
})
|
||||
|
||||
createCollapsibles(editorModifierEntries)
|
||||
@ -169,7 +187,7 @@ function refreshModifiersState(newTags, inactiveTags) {
|
||||
.querySelector("#editor-modifiers")
|
||||
.querySelectorAll(".modifier-card")
|
||||
.forEach((modifierCard) => {
|
||||
const modifierName = modifierCard.querySelector(".modifier-card-label p").dataset.fullName // pick the full modifier name
|
||||
const modifierName = modifierCard.dataset.fullName // pick the full modifier name
|
||||
if (activeTags.map((x) => x.name).includes(modifierName)) {
|
||||
modifierCard.classList.remove(activeCardClass)
|
||||
modifierCard.querySelector(".modifier-card-image-overlay").innerText = "+"
|
||||
@ -184,8 +202,9 @@ function refreshModifiersState(newTags, inactiveTags) {
|
||||
.querySelector("#editor-modifiers")
|
||||
.querySelectorAll(".modifier-card")
|
||||
.forEach((modifierCard) => {
|
||||
const modifierName = modifierCard.querySelector(".modifier-card-label p").dataset.fullName
|
||||
const modifierName = modifierCard.dataset.fullName
|
||||
const shortModifierName = modifierCard.querySelector(".modifier-card-label p").innerText
|
||||
|
||||
if (trimModifiers(tag) == trimModifiers(modifierName)) {
|
||||
// add modifier to active array
|
||||
if (!activeTags.map((x) => x.name).includes(tag)) {
|
||||
@ -242,10 +261,10 @@ function refreshInactiveTags(inactiveTags) {
|
||||
}
|
||||
|
||||
// update cards
|
||||
let overlays = document.querySelector("#editor-inputs-tags-list").querySelectorAll(".modifier-card-overlay")
|
||||
let overlays = editorModifierTagsList.querySelectorAll(".modifier-card-overlay")
|
||||
overlays.forEach((i) => {
|
||||
let modifierName = i.parentElement.getElementsByClassName("modifier-card-label")[0].getElementsByTagName("p")[0]
|
||||
.dataset.fullName
|
||||
let modifierName = i.parentElement.dataset.fullName
|
||||
|
||||
if (inactiveTags?.find((element) => trimModifiers(element) === modifierName) !== undefined) {
|
||||
i.parentElement.classList.add("modifier-toggle-inactive")
|
||||
}
|
||||
@ -262,6 +281,12 @@ function refreshTagsList(inactiveTags) {
|
||||
editorTagsContainer.style.display = "block"
|
||||
}
|
||||
|
||||
if(activeTags.length > 15) {
|
||||
editorModifierTagsList.style["overflow-y"] = "auto"
|
||||
} else {
|
||||
editorModifierTagsList.style["overflow-y"] = "unset"
|
||||
}
|
||||
|
||||
activeTags.forEach((tag, index) => {
|
||||
tag.element.querySelector(".modifier-card-image-overlay").innerText = "-"
|
||||
tag.element.classList.add("modifier-card-tiny")
|
||||
@ -285,48 +310,42 @@ function refreshTagsList(inactiveTags) {
|
||||
|
||||
let brk = document.createElement("br")
|
||||
brk.style.clear = "both"
|
||||
|
||||
editorModifierTagsList.appendChild(brk)
|
||||
|
||||
refreshInactiveTags(inactiveTags)
|
||||
|
||||
document.dispatchEvent(new Event("refreshImageModifiers")) // notify plugins that the image tags have been refreshed
|
||||
}
|
||||
|
||||
function toggleCardState(modifierName, makeActive) {
|
||||
document
|
||||
.querySelector("#editor-modifiers")
|
||||
.querySelectorAll(".modifier-card")
|
||||
.forEach((card) => {
|
||||
const name = card.querySelector(".modifier-card-label").innerText
|
||||
if (
|
||||
trimModifiers(modifierName) == trimModifiers(name) ||
|
||||
trimModifiers(modifierName) == "by " + trimModifiers(name)
|
||||
) {
|
||||
if (makeActive) {
|
||||
card.classList.add(activeCardClass)
|
||||
card.querySelector(".modifier-card-image-overlay").innerText = "-"
|
||||
} else {
|
||||
card.classList.remove(activeCardClass)
|
||||
card.querySelector(".modifier-card-image-overlay").innerText = "+"
|
||||
}
|
||||
}
|
||||
})
|
||||
const cards = [...document.querySelectorAll("#editor-modifiers .modifier-card")]
|
||||
.filter(cardElem => trimModifiers(cardElem.dataset.fullName) == trimModifiers(modifierName))
|
||||
|
||||
const cardExists = typeof cards == "object" && cards?.length > 0
|
||||
|
||||
if (cardExists) {
|
||||
const card = cards[0]
|
||||
|
||||
if (makeActive) {
|
||||
card.classList.add(activeCardClass)
|
||||
card.querySelector(".modifier-card-image-overlay").innerText = "-"
|
||||
} else {
|
||||
card.classList.remove(activeCardClass)
|
||||
card.querySelector(".modifier-card-image-overlay").innerText = "+"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function changePreviewImages(val) {
|
||||
const previewImages = document.querySelectorAll(".modifier-card-image-container img")
|
||||
|
||||
let previewArr = []
|
||||
|
||||
modifiers.map((x) => x.modifiers).forEach((x) => previewArr.push(...x.map((m) => m.previews)))
|
||||
|
||||
previewArr = previewArr.map((x) => {
|
||||
let obj = {}
|
||||
|
||||
x.forEach((preview) => {
|
||||
const previewArr = modifiers.flatMap((x) => x.modifiers.map((m) => m.previews))
|
||||
.map((x) => x.reduce((obj, preview) => {
|
||||
obj[preview.name] = preview.path
|
||||
})
|
||||
|
||||
return obj
|
||||
})
|
||||
return obj
|
||||
}, {}))
|
||||
|
||||
previewImages.forEach((previewImage) => {
|
||||
const currentPreviewType = previewImage.getAttribute("preview-type")
|
||||
@ -369,17 +388,70 @@ function resizeModifierCards(val) {
|
||||
})
|
||||
}
|
||||
|
||||
function saveCustomModifiers() {
|
||||
localStorage.setItem(CUSTOM_MODIFIERS_KEY, customModifiersTextBox.value.trim())
|
||||
|
||||
loadCustomModifiers()
|
||||
}
|
||||
|
||||
function loadCustomModifiers() {
|
||||
PLUGINS["MODIFIERS_LOAD"].forEach((fn) => fn.loader.call())
|
||||
}
|
||||
|
||||
function showModifierContainer() {
|
||||
document.addEventListener("click", checkIfClickedOutsideDropdownElem)
|
||||
|
||||
modifierDropdown.dataset.active = true
|
||||
editorModifiersContainer.classList.add("active")
|
||||
}
|
||||
|
||||
function hideModifierContainer() {
|
||||
document.removeEventListener("click", checkIfClickedOutsideDropdownElem)
|
||||
|
||||
modifierDropdown.dataset.active = false
|
||||
editorModifiersContainer.classList.remove("active")
|
||||
}
|
||||
|
||||
function checkIfClickedOutsideDropdownElem(e) {
|
||||
const clickedElement = e.target
|
||||
|
||||
const clickedInsideSpecificElems = [modifierDropdown, editorModifiersContainer, modifierSettingsOverlay].some((div) =>
|
||||
div && (div.contains(clickedElement) || div === clickedElement))
|
||||
|
||||
if (!clickedInsideSpecificElems && !modifierPanelFreezed) {
|
||||
hideModifierContainer()
|
||||
}
|
||||
}
|
||||
|
||||
function collapseAllModifierCategory() {
|
||||
const collapsibleElems = editorModifierEntries.querySelectorAll(".modifier-category .collapsible"); // needs to have ";"
|
||||
|
||||
[...collapsibleElems].forEach((elem) => {
|
||||
const isActive = elem.classList.contains("active")
|
||||
|
||||
if(isActive) {
|
||||
elem?.click()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function expandAllModifierCategory() {
|
||||
const collapsibleElems = editorModifierEntries.querySelectorAll(".modifier-category .collapsible"); // needs to have ";"
|
||||
|
||||
[...collapsibleElems].forEach((elem) => {
|
||||
const isActive = elem.classList.contains("active")
|
||||
|
||||
if (!isActive) {
|
||||
elem?.click()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
customModifiersTextBox.addEventListener("change", saveCustomModifiers)
|
||||
|
||||
modifierCardSizeSlider.onchange = () => resizeModifierCards(modifierCardSizeSlider.value)
|
||||
previewImageField.onchange = () => changePreviewImages(previewImageField.value)
|
||||
|
||||
modifierSettingsBtn.addEventListener("click", function(e) {
|
||||
modifierSettingsOverlay.classList.add("active")
|
||||
customModifiersTextBox.setSelectionRange(0, 0)
|
||||
customModifiersTextBox.focus()
|
||||
customModifiersInitialContent = customModifiersTextBox.value // preserve the initial content
|
||||
e.stopPropagation()
|
||||
})
|
||||
|
||||
modifierSettingsOverlay.addEventListener("keydown", function(e) {
|
||||
switch (e.key) {
|
||||
case "Escape": // Escape to cancel
|
||||
@ -397,14 +469,93 @@ modifierSettingsOverlay.addEventListener("keydown", function(e) {
|
||||
}
|
||||
})
|
||||
|
||||
function saveCustomModifiers() {
|
||||
localStorage.setItem(CUSTOM_MODIFIERS_KEY, customModifiersTextBox.value.trim())
|
||||
modifierDropdown.addEventListener("click", e => {
|
||||
const targetElem = e.target
|
||||
const isDropdownActive = targetElem.dataset.active == "true" ? true : false
|
||||
|
||||
loadCustomModifiers()
|
||||
}
|
||||
if (!isDropdownActive)
|
||||
showModifierContainer()
|
||||
else
|
||||
hideModifierContainer()
|
||||
})
|
||||
|
||||
function loadCustomModifiers() {
|
||||
PLUGINS["MODIFIERS_LOAD"].forEach((fn) => fn.loader.call())
|
||||
}
|
||||
let collapsiblesBtnState = false
|
||||
|
||||
customModifiersTextBox.addEventListener("change", saveCustomModifiers)
|
||||
modifiersCollapsiblesBtn.addEventListener("click", (e) => {
|
||||
const btnElem = modifiersCollapsiblesBtn
|
||||
|
||||
const collapseText = "Collapse Categories"
|
||||
const expandText = "Expand Categories"
|
||||
|
||||
const collapseIconClasses = ["fa-solid", "fa-square-minus"]
|
||||
const expandIconClasses = ["fa-solid", "fa-square-plus"]
|
||||
|
||||
const iconElem = btnElem.querySelector(".modifiers-action-icon")
|
||||
const textElem = btnElem.querySelector(".modifiers-action-text")
|
||||
|
||||
if (collapsiblesBtnState) {
|
||||
collapseAllModifierCategory()
|
||||
|
||||
collapsiblesBtnState = false
|
||||
|
||||
collapseIconClasses.forEach((c) => iconElem.classList.remove(c))
|
||||
expandIconClasses.forEach((c) => iconElem.classList.add(c))
|
||||
|
||||
textElem.innerText = expandText
|
||||
} else {
|
||||
expandAllModifierCategory()
|
||||
|
||||
collapsiblesBtnState = true
|
||||
|
||||
expandIconClasses.forEach((c) => iconElem.classList.remove(c))
|
||||
collapseIconClasses.forEach((c) => iconElem.classList.add(c))
|
||||
|
||||
textElem.innerText = collapseText
|
||||
}
|
||||
})
|
||||
|
||||
let containerSizeBtnState = false
|
||||
|
||||
modifiersContainerSizeBtn.addEventListener("click", (e) => {
|
||||
const btnElem = modifiersContainerSizeBtn
|
||||
|
||||
const maximizeIconClasses = ["fa-solid", "fa-expand"]
|
||||
const revertIconClasses = ["fa-solid", "fa-compress"]
|
||||
|
||||
modifiersMainContainer.classList.toggle("modifiers-maximized")
|
||||
|
||||
if(containerSizeBtnState) {
|
||||
revertIconClasses.forEach((c) => btnElem.classList.remove(c))
|
||||
maximizeIconClasses.forEach((c) => btnElem.classList.add(c))
|
||||
|
||||
containerSizeBtnState = false
|
||||
} else {
|
||||
maximizeIconClasses.forEach((c) => btnElem.classList.remove(c))
|
||||
revertIconClasses.forEach((c) => btnElem.classList.add(c))
|
||||
|
||||
containerSizeBtnState = true
|
||||
}
|
||||
})
|
||||
|
||||
modifierSettingsBtn.addEventListener("click", (e) => {
|
||||
modifierSettingsOverlay.classList.add("active")
|
||||
customModifiersTextBox.setSelectionRange(0, 0)
|
||||
customModifiersTextBox.focus()
|
||||
customModifiersInitialContent = customModifiersTextBox.value // preserve the initial content
|
||||
e.stopPropagation()
|
||||
})
|
||||
|
||||
modifiersCloseBtn.addEventListener("click", (e) => {
|
||||
hideModifierContainer()
|
||||
})
|
||||
|
||||
// prevents the modifier panel closing at the same time as the settings overlay
|
||||
new MutationObserver(() => {
|
||||
const isActive = modifierSettingsOverlay.classList.contains("active")
|
||||
|
||||
if (!isActive) {
|
||||
modifierPanelFreezed = true
|
||||
|
||||
setTimeout(() => modifierPanelFreezed = false, 25)
|
||||
}
|
||||
}).observe(modifierSettingsOverlay, { attributes: true })
|
Reference in New Issue
Block a user