/**
* UI Components for SageUtils Cache Browser
* Handles creation of UI elements and styling
*/
/**
* Common button styling
*/
export const BUTTON_STYLES = {
base: `
margin-top: 8px;
padding: 6px 12px;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 12px;
`,
refresh: '#4CAF50',
pull: '#2196F3',
edit: '#FF9800',
scan: '#9C27B0',
report: '#673AB7'
};
/**
* Common input styling
*/
export const INPUT_STYLES = {
select: `
width: 100%;
padding: 8px;
background: #333;
color: #fff;
border: 1px solid #555;
border-radius: 4px;
font-size: 12px;
margin-bottom: 10px;
`,
input: `
width: 100%;
padding: 8px;
background: #333;
color: #fff;
border: 1px solid #555;
border-radius: 4px;
font-size: 12px;
margin-bottom: 10px;
box-sizing: border-box;
`,
label: `
display: block;
margin-bottom: 5px;
color: #ffffff;
font-size: 13px;
font-weight: bold;
`
};
/**
* Create a styled button element
* @param {string} text - Button text
* @param {string} color - Background color
* @param {boolean} disabled - Whether button is disabled
* @returns {HTMLButtonElement} - Created button element
*/
export function createButton(text, color, disabled = false) {
const button = document.createElement('button');
button.textContent = text;
button.style.cssText = BUTTON_STYLES.base + `background: ${color};`;
if (disabled) {
button.disabled = true;
button.style.opacity = '0.5';
}
return button;
}
/**
* Create a styled select dropdown
* @param {Array} options - Array of {value, text} option objects
* @param {string} defaultValue - Default selected value
* @returns {HTMLSelectElement} - Created select element
*/
export function createSelect(options, defaultValue = null) {
const select = document.createElement('select');
select.style.cssText = INPUT_STYLES.select;
options.forEach(option => {
const optionElement = document.createElement('option');
optionElement.value = option.value;
optionElement.textContent = option.text;
if (defaultValue && option.value === defaultValue) {
optionElement.selected = true;
}
select.appendChild(optionElement);
});
return select;
}
/**
* Create a styled input element
* @param {string} type - Input type
* @param {string} placeholder - Placeholder text
* @returns {HTMLInputElement} - Created input element
*/
export function createInput(type = 'text', placeholder = '') {
const input = document.createElement('input');
input.type = type;
input.placeholder = placeholder;
input.style.cssText = INPUT_STYLES.input;
return input;
}
/**
* Create a styled label element
* @param {string} text - Label text
* @param {string} htmlFor - Associated input ID
* @returns {HTMLLabelElement} - Created label element
*/
export function createLabel(text, htmlFor = null) {
const label = document.createElement('label');
label.textContent = text;
label.style.cssText = INPUT_STYLES.label;
if (htmlFor) {
label.htmlFor = htmlFor;
}
return label;
}
/**
* Create a container div with specified styling
* @param {string} marginBottom - Bottom margin
* @returns {HTMLDivElement} - Created container element
*/
export function createContainer(marginBottom = '10px') {
const container = document.createElement('div');
container.style.marginBottom = marginBottom;
return container;
}
/**
* Create progress bar component
* @returns {Object} - Object containing progress elements and update function
*/
export function createProgressBar() {
const container = document.createElement('div');
container.style.cssText = `
margin-top: 10px;
display: none;
`;
const label = document.createElement('div');
label.style.cssText = `
color: #fff;
font-size: 12px;
margin-bottom: 5px;
`;
label.textContent = 'Scanning models...';
const outer = document.createElement('div');
outer.style.cssText = `
width: 100%;
height: 20px;
background: #333;
border: 1px solid #555;
border-radius: 4px;
overflow: hidden;
`;
const inner = document.createElement('div');
inner.style.cssText = `
height: 100%;
background: linear-gradient(90deg, #9C27B0, #E91E63);
width: 0%;
transition: width 0.3s ease;
position: relative;
`;
const text = document.createElement('div');
text.style.cssText = `
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
display: flex;
align-items: center;
justify-content: center;
color: #fff;
font-size: 11px;
font-weight: bold;
text-shadow: 1px 1px 2px rgba(0,0,0,0.7);
`;
text.textContent = '0%';
inner.appendChild(text);
outer.appendChild(inner);
container.appendChild(label);
container.appendChild(outer);
return {
container,
label,
outer,
inner,
text,
show() {
container.style.display = 'block';
},
hide() {
container.style.display = 'none';
},
update(percentage, labelText) {
inner.style.width = `${percentage}%`;
text.textContent = `${percentage}%`;
if (labelText) {
label.textContent = labelText;
}
}
};
}
/**
* Create dropdown CSS styles and add to document
*/
export function addDropdownStyles() {
const style = document.createElement('style');
style.textContent = `
.cache-dropdown-item {
padding: 6px 10px;
cursor: pointer;
position: relative;
border-bottom: 1px solid #444;
font-size: 11px;
}
.cache-dropdown-item:hover {
background: #444;
}
.cache-dropdown-item.folder {
background: #383838;
font-weight: bold;
}
.cache-dropdown-item.folder:hover {
background: #484848;
}
.cache-dropdown-item.folder::after {
content: "▶";
float: right;
}
.cache-dropdown-submenu {
position: fixed;
background: #333;
border: 1px solid #555;
border-radius: 4px;
min-width: 250px;
max-height: 300px;
overflow-y: auto;
z-index: 1001;
display: none;
box-shadow: 2px 2px 8px rgba(0,0,0,0.3);
}
.cache-dropdown-item.file {
color: #e0e0e0;
}
.cache-dropdown-item.selected {
background: #4CAF50;
}
`;
document.head.appendChild(style);
}
/**
* Create custom dropdown component
* @param {string} placeholder - Placeholder text
* @returns {Object} - Object containing dropdown elements and methods
*/
export function createCustomDropdown(placeholder = 'Select an option...') {
const container = document.createElement('div');
container.style.cssText = `
position: relative;
width: 100%;
`;
const button = document.createElement('div');
button.style.cssText = `
width: 100%;
padding: 8px;
background: #333;
color: #fff;
border: 1px solid #555;
border-radius: 4px;
font-size: 12px;
cursor: pointer;
display: flex;
justify-content: space-between;
align-items: center;
user-select: none;
`;
button.innerHTML = `${placeholder}▼`;
const menu = document.createElement('div');
menu.style.cssText = `
position: fixed;
background: #333;
border: 1px solid #555;
border-top: none;
border-radius: 0 0 4px 4px;
max-height: 600px;
overflow-y: auto;
z-index: 1000;
display: none;
box-shadow: 2px 2px 8px rgba(0,0,0,0.3);
`;
container.appendChild(button);
document.body.appendChild(menu); // Append to body to avoid container constraints
let isOpen = false;
return {
container,
button,
menu,
get isOpen() { return isOpen; },
set isOpen(value) { isOpen = value; },
show() {
const rect = button.getBoundingClientRect();
menu.style.left = `${rect.left}px`;
menu.style.top = `${rect.bottom}px`;
menu.style.width = `${rect.width}px`;
menu.style.display = 'block';
button.innerHTML = button.innerHTML.replace('▼', '▲');
isOpen = true;
},
hide() {
menu.style.display = 'none';
button.innerHTML = button.innerHTML.replace('▲', '▼');
isOpen = false;
},
clear() {
menu.innerHTML = '';
},
setPlaceholder(text) {
if (!isOpen) {
button.innerHTML = `${text}▼`;
}
}
};
}
/**
* Create NSFW toggle component
* @param {string} id - Element ID
* @returns {Object} - Object containing checkbox and label elements
*/
export function createNsfwToggle(id = 'nsfw-toggle') {
const container = document.createElement('div');
container.style.cssText = `
margin-bottom: 10px;
display: flex;
align-items: center;
gap: 8px;
`;
const checkbox = document.createElement('input');
checkbox.type = 'checkbox';
checkbox.id = id;
checkbox.style.cssText = `
margin: 0;
transform: scale(1.2);
`;
const label = document.createElement('label');
label.htmlFor = id;
label.textContent = 'Show NSFW Images';
label.style.cssText = `
color: #ffffff;
font-size: 13px;
font-weight: bold;
cursor: pointer;
user-select: none;
`;
container.appendChild(checkbox);
container.appendChild(label);
return { container, checkbox, label };
}