Use modal picker for organic and wall palettes
This commit is contained in:
parent
f546662143
commit
99d943643b
111
organic.js
111
organic.js
@ -143,6 +143,13 @@
|
|||||||
const garlandAccentChip = document.getElementById('garland-accent-chip');
|
const garlandAccentChip = document.getElementById('garland-accent-chip');
|
||||||
const garlandAccentClearBtn = document.getElementById('garland-accent-clear');
|
const garlandAccentClearBtn = document.getElementById('garland-accent-clear');
|
||||||
const garlandControls = document.getElementById('garland-controls');
|
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 sizePresetGroup = document.getElementById('size-preset-group');
|
||||||
const toggleShineBtn = null;
|
const toggleShineBtn = null;
|
||||||
@ -1112,45 +1119,25 @@
|
|||||||
function renderAllowedPalette() {
|
function renderAllowedPalette() {
|
||||||
if (!paletteBox) return;
|
if (!paletteBox) return;
|
||||||
paletteBox.innerHTML = '';
|
paletteBox.innerHTML = '';
|
||||||
(window.PALETTE || []).forEach(group => {
|
const btn = document.createElement('button');
|
||||||
const title = document.createElement('div');
|
btn.type = 'button';
|
||||||
title.className = 'family-title';
|
btn.className = 'btn-dark w-full';
|
||||||
title.textContent = group.family;
|
btn.textContent = 'Choose color';
|
||||||
paletteBox.appendChild(title);
|
btn.addEventListener('click', () => {
|
||||||
|
if (!window.openColorPicker) return;
|
||||||
const row = document.createElement('div');
|
window.openColorPicker({
|
||||||
row.className = 'swatch-row';
|
title: 'Choose active color',
|
||||||
(group.colors || []).forEach(c => {
|
subtitle: 'Applies to drawing and path tools',
|
||||||
const idx =
|
items: (FLAT_COLORS || []).map((c, idx) => ({ label: c.name || c.hex, metaText: c.family || '', idx })),
|
||||||
FLAT_COLORS.find(fc => fc.name === c.name && fc.hex === c.hex && fc.family === group.family)?._idx
|
onSelect: (item) => {
|
||||||
?? HEX_TO_FIRST_IDX.get(normalizeHex(c.hex));
|
if (!Number.isInteger(item.idx)) return;
|
||||||
const sw = document.createElement('button');
|
selectedColorIdx = item.idx;
|
||||||
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();
|
|
||||||
updateCurrentColorChip();
|
updateCurrentColorChip();
|
||||||
persist();
|
persist();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
row.appendChild(sw);
|
|
||||||
});
|
|
||||||
paletteBox.appendChild(row);
|
|
||||||
});
|
});
|
||||||
|
paletteBox.appendChild(btn);
|
||||||
}
|
}
|
||||||
|
|
||||||
function getUsedColors() {
|
function getUsedColors() {
|
||||||
@ -1212,8 +1199,6 @@
|
|||||||
onSelect: (item) => {
|
onSelect: (item) => {
|
||||||
if (!Number.isInteger(item.idx)) return;
|
if (!Number.isInteger(item.idx)) return;
|
||||||
selectedColorIdx = item.idx;
|
selectedColorIdx = item.idx;
|
||||||
renderAllowedPalette();
|
|
||||||
renderUsedPalette();
|
|
||||||
updateCurrentColorChip();
|
updateCurrentColorChip();
|
||||||
persist();
|
persist();
|
||||||
}
|
}
|
||||||
@ -1238,58 +1223,8 @@
|
|||||||
|
|
||||||
function renderUsedPalette() {
|
function renderUsedPalette() {
|
||||||
if (!usedPaletteBox) return;
|
if (!usedPaletteBox) return;
|
||||||
usedPaletteBox.innerHTML = '';
|
usedPaletteBox.innerHTML = '<div class="hint">Palette opens in modal.</div>';
|
||||||
const used = getUsedColors();
|
|
||||||
if (used.length === 0) {
|
|
||||||
usedPaletteBox.innerHTML = '<div class="hint">No colors yet.</div>';
|
|
||||||
if (replaceFromSel) replaceFromSel.innerHTML = '';
|
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();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ====== Balloon Ops & Data/Export ======
|
// ====== Balloon Ops & Data/Export ======
|
||||||
|
|||||||
85
wall.js
85
wall.js
@ -623,36 +623,7 @@
|
|||||||
|
|
||||||
function renderWallUsedPalette() {
|
function renderWallUsedPalette() {
|
||||||
if (!wallUsedPaletteEl) return;
|
if (!wallUsedPaletteEl) return;
|
||||||
const used = wallUsedColors();
|
wallUsedPaletteEl.innerHTML = '<div class="text-xs text-gray-500">Palette opens in modal.</div>';
|
||||||
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);
|
|
||||||
populateWallReplaceSelects();
|
populateWallReplaceSelects();
|
||||||
updateWallReplacePreview();
|
updateWallReplacePreview();
|
||||||
}
|
}
|
||||||
@ -1003,47 +974,25 @@
|
|||||||
if (!wallPaletteEl) return;
|
if (!wallPaletteEl) return;
|
||||||
wallPaletteEl.innerHTML = '';
|
wallPaletteEl.innerHTML = '';
|
||||||
populateWallReplaceSelects();
|
populateWallReplaceSelects();
|
||||||
(window.PALETTE || []).forEach(group => {
|
const btn = document.createElement('button');
|
||||||
const title = document.createElement('div');
|
btn.type = 'button';
|
||||||
title.className = 'family-title';
|
btn.className = 'btn-dark w-full';
|
||||||
title.textContent = group.family;
|
btn.textContent = 'Choose color';
|
||||||
wallPaletteEl.appendChild(title);
|
btn.addEventListener('click', () => {
|
||||||
|
if (!window.openColorPicker) return;
|
||||||
const row = document.createElement('div');
|
window.openColorPicker({
|
||||||
row.className = 'swatch-row';
|
title: 'Choose active wall color',
|
||||||
(group.colors || []).forEach(c => {
|
subtitle: 'Applies to wall fill tools',
|
||||||
const normHex = (c.hex || '').toLowerCase();
|
items: (FLAT_COLORS || []).map((c, idx) => ({ label: c.name || c.hex, metaText: c.family || '', idx })),
|
||||||
let idx = FLAT_COLORS.findIndex(fc => fc.name === c.name && fc.hex === c.hex && fc.family === group.family);
|
onSelect: (item) => {
|
||||||
if (idx < 0 && window.shared?.HEX_TO_FIRST_IDX?.has(normHex)) {
|
if (!Number.isInteger(item.idx)) return;
|
||||||
idx = window.shared.HEX_TO_FIRST_IDX.get(normHex);
|
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();
|
renderWallUsedPalette();
|
||||||
updateWallActiveChip(getActiveWallColorIdx());
|
updateWallActiveChip(getActiveWallColorIdx());
|
||||||
updateWallReplacePreview();
|
updateWallReplacePreview();
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user