Use modal color picker for classic palette

This commit is contained in:
chris 2025-12-17 17:40:28 -05:00
parent 540acedcab
commit f546662143

View File

@ -588,23 +588,30 @@ function distinctPaletteSlots(palette) {
const isArch = (model.patternName || '').toLowerCase().includes('arch'); const isArch = (model.patternName || '').toLowerCase().includes('arch');
let slideX = 80; let slideX = 80;
let slideY = 0; let slideY = 0;
const idx = typeof cell.balloonIndexInCluster === 'number' ? cell.balloonIndexInCluster : 0;
const spread = idx - 1.5;
if (isArch) { if (isArch) {
// Radial slide outward; preserve layout. // Radial slide outward; preserve layout.
const dist = Math.hypot(c.x, c.y) || 1; const dist = Math.hypot(c.x, c.y) || 1;
const offset = 80; const offset = 80;
slideX = (c.x / dist) * offset; const nx = c.x / dist, ny = c.y / dist;
slideY = (c.y / dist) * offset; slideX = nx * offset;
slideY = ny * offset;
// Slight tangent spread (~5px) to separate balloons without reshaping the quad.
const txDirX = -ny;
const txDirY = nx;
const fan = spread * 10;
slideX += txDirX * fan;
slideY += txDirY * fan;
} }
let tx = c.x + slideX; let tx = c.x + slideX;
let ty = c.y + slideY; let ty = c.y + slideY;
// Keep shape intact; only fan columns slightly. // Keep shape intact; only fan columns slightly.
const idx = typeof cell.balloonIndexInCluster === 'number' ? cell.balloonIndexInCluster : 0;
const spread = idx - 1.5;
if (isArch) { if (isArch) {
// no fan/scale for arches; preserve layout // no fan/scale for arches; preserve layout
} else { } else {
tx += spread * 4; tx += spread * 12;
ty += spread * 2; ty += spread * 10;
} }
const fanScale = 1; const fanScale = 1;
// Nudge the top pair down slightly in columns so they remain easily clickable. // Nudge the top pair down slightly in columns so they remain easily clickable.
@ -1016,7 +1023,7 @@ function distinctPaletteSlots(palette) {
btn.className = 'slot-btn tab-btn'; btn.className = 'slot-btn tab-btn';
btn.dataset.slot = String(i); btn.dataset.slot = String(i);
btn.textContent = `#${i}`; btn.textContent = `#${i}`;
btn.addEventListener('click', () => { activeTarget = String(i); updateUI(); }); btn.addEventListener('click', () => { activeTarget = String(i); updateUI(); openPalettePicker(); });
slotsContainer.appendChild(btn); slotsContainer.appendChild(btn);
} }
} }
@ -1115,53 +1122,55 @@ function distinctPaletteSlots(palette) {
} }
} }
const allPaletteColors = flattenPalette(); swatchGrid.innerHTML = ''; const allPaletteColors = flattenPalette();
(window.PALETTE || []).forEach(group => { swatchGrid.innerHTML = '';
const title = document.createElement('div'); title.className = 'family-title'; title.textContent = group.family; swatchGrid.appendChild(title); swatchGrid.style.display = 'none'; // hide inline list; use modal picker instead
const row = document.createElement('div'); row.className = 'swatch-row';
(group.colors || []).forEach(colorItem => {
const sw = document.createElement('button'); sw.type = 'button'; sw.className = 'swatch'; sw.title = colorItem.name;
sw.setAttribute('aria-label', colorItem.name);
sw.dataset.hex = normHex(colorItem.hex);
if (colorItem.image) sw.dataset.image = colorItem.image;
sw.style.backgroundImage = colorItem.image ? `url("${colorItem.image}")` : 'none'; const openPalettePicker = () => {
sw.style.backgroundColor = colorItem.hex; if (typeof window.openColorPicker !== 'function') return;
sw.style.backgroundSize = '500%'; const items = allPaletteColors.map(c => ({
sw.style.backgroundPosition = 'center'; label: c.name || c.hex,
hex: c.hex,
meta: c,
metaText: c.family || ''
}));
const currentType = document.querySelector('.topper-type-btn[aria-pressed="true"]')?.dataset.type || 'round';
const subtitle = isManual()
? 'Apply to active paint'
: (activeTarget === 'T' ? 'Set topper color' : `Set slot #${activeTarget}`);
window.openColorPicker({
title: 'Choose a color',
subtitle,
items,
onSelect: (item) => {
const meta = item.meta || {};
const selectedColor = { hex: meta.hex || item.hex, image: meta.image || null };
if (activeTarget === 'T') {
if (currentType.startsWith('num-')) {
setNumberTintColor(selectedColor.hex);
setNumberTintOpacity(1);
if (numberTintSlider) numberTintSlider.value = 1;
} else {
setTopperColor(selectedColor);
}
} else if (isManual()) {
manualActiveColorGlobal = window.shared?.setActiveColor?.(selectedColor) || selectedColor;
} else {
const index = parseInt(activeTarget, 10) - 1;
if (index >= 0 && index < MAX_SLOTS) { classicColors[index] = selectedColor; setClassicColors(classicColors); }
}
updateUI(); onColorChange();
if (window.updateExportButtonVisibility) window.updateExportButtonVisibility();
}
});
};
sw.addEventListener('click', () => { topperSwatch.addEventListener('click', () => { activeTarget = 'T'; updateUI(); openPalettePicker(); });
const selectedColor = { hex: colorItem.hex, image: colorItem.image };
const currentType = document.querySelector('.topper-type-btn[aria-pressed="true"]')?.dataset.type || 'round';
if (activeTarget === 'T') {
if (currentType.startsWith('num-')) {
setNumberTintColor(selectedColor.hex);
setNumberTintOpacity(1);
if (numberTintSlider) numberTintSlider.value = 1;
} else {
setTopperColor(selectedColor);
}
} else if (isManual()) {
manualActiveColorGlobal = window.shared?.setActiveColor?.(selectedColor) || selectedColor;
} else {
const index = parseInt(activeTarget, 10) - 1;
if (index >= 0 && index < MAX_SLOTS) { classicColors[index] = selectedColor; setClassicColors(classicColors); }
}
updateUI(); onColorChange();
if (window.updateExportButtonVisibility) window.updateExportButtonVisibility();
});
row.appendChild(sw);
});
swatchGrid.appendChild(row);
});
topperSwatch.addEventListener('click', () => { activeTarget = 'T'; updateUI(); });
activeChip?.addEventListener('click', () => { activeChip?.addEventListener('click', () => {
if (openManualPicker()) return; openPalettePicker();
try { swatchGrid?.scrollIntoView({ behavior: 'smooth', block: 'center' }); } catch {}
}); });
floatingChip?.addEventListener('click', () => { floatingChip?.addEventListener('click', () => {
if (openManualPicker()) return; openPalettePicker();
try { swatchGrid?.scrollIntoView({ behavior: 'smooth', block: 'center' }); } catch {}
}); });
randomizeBtn?.addEventListener('click', () => { randomizeBtn?.addEventListener('click', () => {
const pool = allPaletteColors.slice(); const picks = []; const pool = allPaletteColors.slice(); const picks = [];