New effects, refactor
This commit is contained in:
parent
4ac45367e5
commit
c0d6bee9c3
14 changed files with 1620 additions and 215 deletions
|
|
@ -6,6 +6,120 @@ export class UIBuilder {
|
|||
constructor(containerElement) {
|
||||
this.container = containerElement;
|
||||
this.controlGroups = new Map(); // category -> { element, controls }
|
||||
this.tooltip = null;
|
||||
this.tooltipTimeout = null;
|
||||
this.setupTooltip();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create and setup the tooltip element
|
||||
*/
|
||||
setupTooltip() {
|
||||
// Wait for DOM to be ready
|
||||
if (document.readyState === 'loading') {
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
this.createTooltipElement();
|
||||
});
|
||||
} else {
|
||||
this.createTooltipElement();
|
||||
}
|
||||
}
|
||||
|
||||
createTooltipElement() {
|
||||
this.tooltip = document.createElement('div');
|
||||
this.tooltip.className = 'control-tooltip';
|
||||
this.tooltip.style.cssText = `
|
||||
position: fixed;
|
||||
background: linear-gradient(135deg, rgba(0, 120, 200, 0.98) 0%, rgba(0, 100, 180, 0.98) 100%);
|
||||
color: #fff;
|
||||
padding: 0.5rem 0.75rem;
|
||||
border-radius: 4px;
|
||||
font-size: 0.8rem;
|
||||
pointer-events: none;
|
||||
z-index: 10000;
|
||||
max-width: 250px;
|
||||
box-shadow: 0 0 20px rgba(0, 150, 255, 0.5), 0 4px 12px rgba(0, 0, 0, 0.4);
|
||||
border: 1px solid rgba(0, 150, 255, 0.6);
|
||||
opacity: 0;
|
||||
transition: opacity 0.15s ease;
|
||||
line-height: 1.4;
|
||||
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.3);
|
||||
`;
|
||||
document.body.appendChild(this.tooltip);
|
||||
}
|
||||
|
||||
/**
|
||||
* Show tooltip for an element
|
||||
*/
|
||||
showTooltip(element, text) {
|
||||
if (!text || !this.tooltip) return;
|
||||
|
||||
clearTimeout(this.tooltipTimeout);
|
||||
|
||||
this.tooltip.textContent = text;
|
||||
this.tooltip.style.opacity = '1';
|
||||
|
||||
// Position tooltip above the element
|
||||
const rect = element.getBoundingClientRect();
|
||||
|
||||
// Set initial position to measure
|
||||
this.tooltip.style.left = '0px';
|
||||
this.tooltip.style.top = '0px';
|
||||
this.tooltip.style.visibility = 'hidden';
|
||||
this.tooltip.style.display = 'block';
|
||||
|
||||
const tooltipRect = this.tooltip.getBoundingClientRect();
|
||||
|
||||
this.tooltip.style.visibility = 'visible';
|
||||
|
||||
let left = rect.left + rect.width / 2 - tooltipRect.width / 2;
|
||||
let top = rect.top - tooltipRect.height - 10;
|
||||
|
||||
// Keep tooltip on screen
|
||||
const padding = 10;
|
||||
if (left < padding) left = padding;
|
||||
if (left + tooltipRect.width > window.innerWidth - padding) {
|
||||
left = window.innerWidth - tooltipRect.width - padding;
|
||||
}
|
||||
if (top < padding) {
|
||||
top = rect.bottom + 10;
|
||||
}
|
||||
|
||||
this.tooltip.style.left = `${left}px`;
|
||||
this.tooltip.style.top = `${top}px`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Hide tooltip
|
||||
*/
|
||||
hideTooltip() {
|
||||
if (!this.tooltip) return;
|
||||
clearTimeout(this.tooltipTimeout);
|
||||
this.tooltipTimeout = setTimeout(() => {
|
||||
this.tooltip.style.opacity = '0';
|
||||
}, 100);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add tooltip handlers to an element
|
||||
*/
|
||||
addTooltipHandlers(element, description) {
|
||||
if (!description) return;
|
||||
|
||||
element.addEventListener('mouseenter', () => {
|
||||
this.showTooltip(element, description);
|
||||
});
|
||||
|
||||
element.addEventListener('mouseleave', () => {
|
||||
this.hideTooltip();
|
||||
});
|
||||
|
||||
element.addEventListener('mousemove', () => {
|
||||
// Update position on mouse move for better following
|
||||
if (this.tooltip && this.tooltip.style.opacity === '1') {
|
||||
this.showTooltip(element, description);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -133,19 +247,19 @@ export class UIBuilder {
|
|||
|
||||
switch (type) {
|
||||
case 'checkbox':
|
||||
return this.createCheckbox(id, label, defaultValue, showWhen);
|
||||
return this.createCheckbox(id, label, defaultValue, showWhen, description);
|
||||
|
||||
case 'range':
|
||||
return this.createRange(id, label, defaultValue, min, max, step, description, showWhen);
|
||||
|
||||
case 'color':
|
||||
return this.createColor(id, label, defaultValue, showWhen);
|
||||
return this.createColor(id, label, defaultValue, showWhen, description);
|
||||
|
||||
case 'select':
|
||||
return this.createSelect(id, label, defaultValue, options, showWhen);
|
||||
return this.createSelect(id, label, defaultValue, options, showWhen, description);
|
||||
|
||||
case 'text':
|
||||
return this.createTextInput(id, label, defaultValue);
|
||||
return this.createTextInput(id, label, defaultValue, showWhen, description);
|
||||
|
||||
default:
|
||||
console.warn(`Unknown control type: ${type}`);
|
||||
|
|
@ -156,7 +270,7 @@ export class UIBuilder {
|
|||
/**
|
||||
* Create a checkbox control
|
||||
*/
|
||||
createCheckbox(id, label, defaultValue, showWhen) {
|
||||
createCheckbox(id, label, defaultValue, showWhen, description) {
|
||||
const wrapper = document.createElement('label');
|
||||
wrapper.className = 'checkbox-label';
|
||||
|
||||
|
|
@ -176,6 +290,9 @@ export class UIBuilder {
|
|||
wrapper.dataset.showWhen = showWhen;
|
||||
}
|
||||
|
||||
// Add tooltip handlers to the label wrapper
|
||||
this.addTooltipHandlers(wrapper, description);
|
||||
|
||||
return wrapper;
|
||||
}
|
||||
|
||||
|
|
@ -188,9 +305,6 @@ export class UIBuilder {
|
|||
const labelEl = document.createElement('label');
|
||||
labelEl.htmlFor = id;
|
||||
labelEl.innerHTML = `${label}: <span id="${id}-value">${defaultValue}</span>`;
|
||||
if (description) {
|
||||
labelEl.title = description;
|
||||
}
|
||||
|
||||
const input = document.createElement('input');
|
||||
input.type = 'range';
|
||||
|
|
@ -218,13 +332,16 @@ export class UIBuilder {
|
|||
container.dataset.showWhen = showWhen;
|
||||
}
|
||||
|
||||
// Add tooltip handlers to the label
|
||||
this.addTooltipHandlers(labelEl, description);
|
||||
|
||||
return container;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a color picker control
|
||||
*/
|
||||
createColor(id, label, defaultValue, showWhen) {
|
||||
createColor(id, label, defaultValue, showWhen, description) {
|
||||
const container = document.createElement('div');
|
||||
|
||||
const labelEl = document.createElement('label');
|
||||
|
|
@ -244,13 +361,16 @@ export class UIBuilder {
|
|||
container.dataset.showWhen = showWhen;
|
||||
}
|
||||
|
||||
// Add tooltip handlers to the label
|
||||
this.addTooltipHandlers(labelEl, description);
|
||||
|
||||
return container;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a select dropdown control
|
||||
*/
|
||||
createSelect(id, label, defaultValue, options, showWhen) {
|
||||
createSelect(id, label, defaultValue, options, showWhen, description) {
|
||||
const container = document.createElement('div');
|
||||
|
||||
const labelEl = document.createElement('label');
|
||||
|
|
@ -278,13 +398,16 @@ export class UIBuilder {
|
|||
container.dataset.showWhen = showWhen;
|
||||
}
|
||||
|
||||
// Add tooltip handlers to the label
|
||||
this.addTooltipHandlers(labelEl, description);
|
||||
|
||||
return container;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a text input control
|
||||
*/
|
||||
createTextInput(id, label, defaultValue) {
|
||||
createTextInput(id, label, defaultValue, showWhen, description) {
|
||||
const container = document.createElement('div');
|
||||
|
||||
const labelEl = document.createElement('label');
|
||||
|
|
@ -300,6 +423,14 @@ export class UIBuilder {
|
|||
container.appendChild(labelEl);
|
||||
container.appendChild(input);
|
||||
|
||||
if (showWhen) {
|
||||
container.style.display = 'none';
|
||||
container.dataset.showWhen = showWhen;
|
||||
}
|
||||
|
||||
// Add tooltip handlers to the label
|
||||
this.addTooltipHandlers(labelEl, description);
|
||||
|
||||
return container;
|
||||
}
|
||||
|
||||
|
|
@ -348,6 +479,14 @@ export class UIBuilder {
|
|||
} else if (controlId && controlId.startsWith('text2-gradient-')) {
|
||||
control.style.display = triggerControl.value === 'gradient' ? 'block' : 'none';
|
||||
}
|
||||
}
|
||||
// For border style controls
|
||||
else if (triggerControlId === 'border-style') {
|
||||
if (controlId === 'border-rainbow-speed') {
|
||||
control.style.display = triggerControl.value === 'rainbow' ? 'block' : 'none';
|
||||
} else if (controlId === 'border-march-speed') {
|
||||
control.style.display = triggerControl.value === 'marching-ants' ? 'block' : 'none';
|
||||
}
|
||||
} else {
|
||||
// Default: show when any value is selected
|
||||
control.style.display = triggerControl.value ? 'block' : 'none';
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue