CoolFace
Apppublic

Murlocninja/DictionaryColorCombinations

sourceHugging Faceupdated 6mo agoView on Hugging Face
1likes
script.js153 linesDownload Raw Back to root
1document.addEventListener('DOMContentLoaded', () => {2    const grid = document.getElementById('bento-grid');3    const root = document.documentElement;4    const toast = document.getElementById('toast');5    const filterBtns = document.querySelectorAll('.filter-btn');6    const randomizeBtn = document.getElementById('randomize-btn');7    let toastTimeout;8    let activeFilter = 'all';9 10    // ─── Fisher-Yates Shuffle ──────────────────────────────────────────────11    function shuffle(arr) {12        const a = [...arr];13        for (let i = a.length - 1; i > 0; i--) {14            const j = Math.floor(Math.random() * (i + 1));15            [a[i], a[j]] = [a[j], a[i]];16        }17        return a;18    }19 20    // ─── Build a single tile DOM node ──────────────────────────────────────21    function buildTile(palette) {22        const tile = document.createElement('div');23        tile.className = `palette-tile ${palette.span} visible`;24        tile.dataset.colorCount = palette.colors.length;25        tile.dataset.id = palette.id;26 27        // Harmony overlay on hover28        const harmonyColor = palette.colors[0];29        tile.addEventListener('mouseenter', () => {30            root.style.setProperty('--harmony-overlay', harmonyColor);31        });32        tile.addEventListener('mouseleave', () => {33            root.style.setProperty('--harmony-overlay', 'transparent');34        });35 36        // Build swatches37        palette.colors.forEach(color => {38            const swatch = document.createElement('div');39            swatch.className = 'color-swatch';40            swatch.style.backgroundColor = color;41            swatch.style.setProperty('--swatch-color', color);42 43            const hexLabel = document.createElement('div');44            hexLabel.className = 'hex-label';45            hexLabel.textContent = color.toUpperCase();46            swatch.appendChild(hexLabel);47 48            swatch.addEventListener('click', (e) => {49                e.stopPropagation();50                navigator.clipboard.writeText(color.toUpperCase()).then(() => {51                    showToast(`Copied ${color.toUpperCase()}`);52                }).catch(() => {});53            });54 55            tile.appendChild(swatch);56        });57 58        return tile;59    }60 61    // ─── Render grid from a (possibly shuffled) palette array ──────────────62    let currentOrder = shuffle(palettes); // randomize on first load63 64    function renderGrid(orderedPalettes) {65        grid.innerHTML = '';66        orderedPalettes.forEach((palette, index) => {67            const tile = buildTile(palette);68 69            // Staggered entrance animation70            tile.style.opacity = '0';71            tile.style.transform = 'translateY(24px) scale(0.96)';72            grid.appendChild(tile);73 74            setTimeout(() => {75                tile.style.transition = 'opacity 0.5s cubic-bezier(0.16, 1, 0.3, 1), transform 0.5s cubic-bezier(0.16, 1, 0.3, 1)';76                tile.style.opacity = '1';77                tile.style.transform = 'translateY(0) scale(1)';78 79                // After entrance, hand off to CSS classes80                setTimeout(() => {81                    tile.style.transition = '';82                    tile.style.opacity = '';83                    tile.style.transform = '';84                    tile.classList.add('visible');85                }, 520);86            }, 40 * index);87        });88 89        // Apply the current filter after render90        applyFilter(activeFilter, false);91    }92 93    // ─── Filter Logic ──────────────────────────────────────────────────────94    function applyFilter(filter, animate = true) {95        activeFilter = filter;96        const tiles = grid.querySelectorAll('.palette-tile');97 98        tiles.forEach((tile, i) => {99            const count = parseInt(tile.dataset.colorCount, 10);100            const matches = filter === 'all' || count === parseInt(filter, 10);101 102            if (matches) {103                tile.style.display = '';104                // Tiny stagger for "reveal" feel105                const delay = animate ? i * 25 : 0;106                setTimeout(() => {107                    tile.classList.remove('hidden');108                    tile.classList.add('visible');109                }, delay);110            } else {111                tile.classList.remove('visible');112                tile.classList.add('hidden');113                setTimeout(() => {114                    // Only hide from layout after transition115                    if (tile.classList.contains('hidden')) {116                        tile.style.display = 'none';117                    }118                }, 350);119            }120        });121    }122 123    // ─── Wire up filter buttons ────────────────────────────────────────────124    filterBtns.forEach(btn => {125        btn.addEventListener('click', () => {126            filterBtns.forEach(b => b.classList.remove('active'));127            btn.classList.add('active');128            applyFilter(btn.dataset.filter);129        });130    });131 132    // ─── Shuffle Button ────────────────────────────────────────────────────133    randomizeBtn.addEventListener('click', () => {134        // Spin animation135        randomizeBtn.classList.add('spinning');136        setTimeout(() => randomizeBtn.classList.remove('spinning'), 460);137 138        currentOrder = shuffle(palettes);139        renderGrid(currentOrder);140    });141 142    // ─── Toast ─────────────────────────────────────────────────────────────143    function showToast(message = 'Copied to clipboard') {144        toast.textContent = message;145        if (toastTimeout) clearTimeout(toastTimeout);146        toast.classList.add('show');147        toastTimeout = setTimeout(() => toast.classList.remove('show'), 2000);148    }149 150    // ─── Initial Render (already shuffled) ─────────────────────────────────151    renderGrid(currentOrder);152});153