Use modal picker for organic and wall palettes

This commit is contained in:
chris 2025-12-17 17:50:32 -05:00
parent f546662143
commit 99d943643b
2 changed files with 41 additions and 157 deletions

View File

@ -143,6 +143,13 @@
const garlandAccentChip = document.getElementById('garland-accent-chip');
const garlandAccentClearBtn = document.getElementById('garland-accent-clear');
const garlandControls = document.getElementById('garland-controls');
// Optional dropdowns (may not be present in current layout)
const garlandColorMain1Sel = document.getElementById('garland-color-main-1');
const garlandColorMain2Sel = document.getElementById('garland-color-main-2');
const garlandColorMain3Sel = document.getElementById('garland-color-main-3');
const garlandColorMain4Sel = document.getElementById('garland-color-main-4');
const garlandColorAccentSel = document.getElementById('garland-color-accent');
const updateGarlandSwatches = () => {}; // stub for layouts without dropdown swatches
const sizePresetGroup = document.getElementById('size-preset-group');
const toggleShineBtn = null;
@ -1112,45 +1119,25 @@
function renderAllowedPalette() {
if (!paletteBox) return;
paletteBox.innerHTML = '';
(window.PALETTE || []).forEach(group => {
const title = document.createElement('div');
title.className = 'family-title';
title.textContent = group.family;
paletteBox.appendChild(title);
const row = document.createElement('div');
row.className = 'swatch-row';
(group.colors || []).forEach(c => {
const idx =
FLAT_COLORS.find(fc => fc.name === c.name && fc.hex === c.hex && fc.family === group.family)?._idx
?? HEX_TO_FIRST_IDX.get(normalizeHex(c.hex));
const sw = document.createElement('button');
sw.type = 'button';
sw.className = 'swatch';
sw.setAttribute('aria-label', c.name);
if (c.image) {
const meta = FLAT_COLORS[idx] || {};
sw.style.backgroundImage = `url("${c.image}")`;
sw.style.backgroundSize = `${100 * SWATCH_TEXTURE_ZOOM}%`;
sw.style.backgroundPosition = `${(meta.imageFocus?.x ?? 0.5) * 100}% ${(meta.imageFocus?.y ?? 0.5) * 100}%`;
} else {
sw.style.backgroundColor = c.hex;
}
if (idx === selectedColorIdx) sw.classList.add('active');
sw.title = c.name;
sw.addEventListener('click', () => {
selectedColorIdx = idx ?? 0;
renderAllowedPalette();
const btn = document.createElement('button');
btn.type = 'button';
btn.className = 'btn-dark w-full';
btn.textContent = 'Choose color';
btn.addEventListener('click', () => {
if (!window.openColorPicker) return;
window.openColorPicker({
title: 'Choose active color',
subtitle: 'Applies to drawing and path tools',
items: (FLAT_COLORS || []).map((c, idx) => ({ label: c.name || c.hex, metaText: c.family || '', idx })),
onSelect: (item) => {
if (!Number.isInteger(item.idx)) return;
selectedColorIdx = item.idx;
updateCurrentColorChip();
persist();
});
row.appendChild(sw);
}
});
paletteBox.appendChild(row);
});
paletteBox.appendChild(btn);
}
function getUsedColors() {
@ -1212,8 +1199,6 @@
onSelect: (item) => {
if (!Number.isInteger(item.idx)) return;
selectedColorIdx = item.idx;
renderAllowedPalette();
renderUsedPalette();
updateCurrentColorChip();
persist();
}
@ -1238,58 +1223,8 @@
function renderUsedPalette() {
if (!usedPaletteBox) return;
usedPaletteBox.innerHTML = '';
const used = getUsedColors();
if (used.length === 0) {
usedPaletteBox.innerHTML = '<div class="hint">No colors yet.</div>';
if (replaceFromSel) replaceFromSel.innerHTML = '';
return;
}
const row = document.createElement('div');
row.className = 'swatch-row';
used.forEach(item => {
const sw = document.createElement('button');
sw.type = 'button';
sw.className = 'swatch';
const name = item.name || NAME_BY_HEX.get(item.hex) || item.hex;
sw.setAttribute('aria-label', `${name} - Count: ${item.count}`);
if (item.image) {
const meta = FLAT_COLORS[HEX_TO_FIRST_IDX.get(item.hex)] || {};
sw.style.backgroundImage = `url("${item.image}")`;
sw.style.backgroundSize = `${100 * SWATCH_TEXTURE_ZOOM}%`;
sw.style.backgroundPosition = `${(meta.imageFocus?.x ?? 0.5) * 100}% ${(meta.imageFocus?.y ?? 0.5) * 100}%`;
} else {
sw.style.backgroundColor = item.hex;
}
if (normalizeHex(FLAT_COLORS[selectedColorIdx]?.hex) === item.hex) sw.classList.add('active');
sw.title = `${name}${item.count}`;
sw.addEventListener('click', () => {
selectedColorIdx = HEX_TO_FIRST_IDX.get(item.hex) ?? 0;
renderAllowedPalette();
renderUsedPalette();
});
const badge = document.createElement('div');
badge.className = 'badge';
badge.textContent = String(item.count);
sw.appendChild(badge);
row.appendChild(sw);
});
usedPaletteBox.appendChild(row);
// fill "replace from"
if (replaceFromSel) {
replaceFromSel.innerHTML = '';
used.forEach(item => {
const opt = document.createElement('option');
const name = item.name || NAME_BY_HEX.get(item.hex) || item.hex;
opt.value = item.hex;
opt.textContent = `${name} (${item.count})`;
replaceFromSel.appendChild(opt);
});
updateReplaceChips();
}
usedPaletteBox.innerHTML = '<div class="hint">Palette opens in modal.</div>';
if (replaceFromSel) replaceFromSel.innerHTML = '';
}
// ====== Balloon Ops & Data/Export ======

85
wall.js
View File

@ -623,36 +623,7 @@
function renderWallUsedPalette() {
if (!wallUsedPaletteEl) return;
const used = wallUsedColors();
wallUsedPaletteEl.innerHTML = '';
if (!used.length) {
wallUsedPaletteEl.innerHTML = '<div class="text-xs text-gray-500">No colors yet.</div>';
populateWallReplaceSelects();
updateWallReplacePreview();
return;
}
const row = document.createElement('div');
row.className = 'swatch-row';
used.forEach(item => {
const sw = document.createElement('button');
sw.type = 'button';
sw.className = 'swatch';
if (item.image) {
sw.style.backgroundImage = `url("${item.image}")`;
sw.style.backgroundSize = `${100 * 2.5}%`;
} else {
sw.style.backgroundColor = item.hex;
}
sw.title = `${item.name || item.hex} (${item.count})`;
sw.addEventListener('click', () => {
if (!Number.isInteger(item.idx)) return;
setActiveColor(normalizeColorIdx(item.idx));
renderWallPalette();
renderWallUsedPalette();
});
row.appendChild(sw);
});
wallUsedPaletteEl.appendChild(row);
wallUsedPaletteEl.innerHTML = '<div class="text-xs text-gray-500">Palette opens in modal.</div>';
populateWallReplaceSelects();
updateWallReplacePreview();
}
@ -1003,47 +974,25 @@
if (!wallPaletteEl) return;
wallPaletteEl.innerHTML = '';
populateWallReplaceSelects();
(window.PALETTE || []).forEach(group => {
const title = document.createElement('div');
title.className = 'family-title';
title.textContent = group.family;
wallPaletteEl.appendChild(title);
const row = document.createElement('div');
row.className = 'swatch-row';
(group.colors || []).forEach(c => {
const normHex = (c.hex || '').toLowerCase();
let idx = FLAT_COLORS.findIndex(fc => fc.name === c.name && fc.hex === c.hex && fc.family === group.family);
if (idx < 0 && window.shared?.HEX_TO_FIRST_IDX?.has(normHex)) {
idx = window.shared.HEX_TO_FIRST_IDX.get(normHex);
const btn = document.createElement('button');
btn.type = 'button';
btn.className = 'btn-dark w-full';
btn.textContent = 'Choose color';
btn.addEventListener('click', () => {
if (!window.openColorPicker) return;
window.openColorPicker({
title: 'Choose active wall color',
subtitle: 'Applies to wall fill tools',
items: (FLAT_COLORS || []).map((c, idx) => ({ label: c.name || c.hex, metaText: c.family || '', idx })),
onSelect: (item) => {
if (!Number.isInteger(item.idx)) return;
setActiveColor(item.idx);
updateWallActiveChip(getActiveWallColorIdx());
updateWallReplacePreview();
}
idx = normalizeColorIdx(idx);
const sw = document.createElement('button');
sw.type = 'button';
sw.className = 'swatch';
if (c.image) {
const meta = FLAT_COLORS[idx] || {};
sw.style.backgroundImage = `url("${c.image}")`;
sw.style.backgroundSize = `${100 * 2.5}%`;
sw.style.backgroundPosition = `${(meta.imageFocus?.x ?? 0.5) * 100}% ${(meta.imageFocus?.y ?? 0.5) * 100}%`;
} else {
sw.style.backgroundColor = c.hex;
}
if (idx === selectedColorIdx) sw.classList.add('active');
sw.title = c.name;
sw.addEventListener('click', () => {
setActiveColor(idx);
window.organic?.updateCurrentColorChip?.(selectedColorIdx);
// Also update the global chip explicitly
if (window.organic?.updateCurrentColorChip) {
window.organic.updateCurrentColorChip(selectedColorIdx);
}
renderWallPalette();
});
row.appendChild(sw);
});
wallPaletteEl.appendChild(row);
});
wallPaletteEl.appendChild(btn);
renderWallUsedPalette();
updateWallActiveChip(getActiveWallColorIdx());
updateWallReplacePreview();