CoolFace
Apppublic

lerobot/robot-learning-tutorial

sourceHugging Faceupdated 1y agoView on Hugging Face
508likes
ColorPicker.astro119 linesDownload Raw Back to components
1---2---3<div class="color-picker" style="width:100%; margin: 10px 0;">4  <style>5    .color-picker .picker__stack { display:flex; flex-direction:column; gap:12px; }6    .color-picker .current-card { display:grid; grid-template-columns: 30% 70%; align-items: center; gap:14px; padding:14px 32px 14px 16px; border:1px solid var(--border-color); background: var(--surface-bg); border-radius: 12px; }7    .color-picker .current-left { display:flex; flex-direction: column; gap:8px; min-width: 0; }8    .color-picker .current-right { display:flex; flex-direction: column; gap:8px; padding-left: 14px; border-left: 1px solid var(--border-color); }9    .color-picker .current-main { display:flex; align-items:center; gap:12px; min-width: 0; }10    .color-picker .current-swatch { width: 64px; height: 64px; border-radius: 8px; border: 1px solid var(--border-color); }11    .color-picker .current-text { display:flex; flex-direction: column; line-height: 1.2; min-width: 0; }12    .color-picker .current-name { font-size: 14px; font-weight: 800; color: var(--text-color); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: clamp(140px, 28vw, 260px); }13    .color-picker .current-hex, .color-picker .current-extra { font-size: 11px; color: var(--muted-color); letter-spacing: .02em; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: clamp(140px, 28vw, 260px); }14    .color-picker .picker__label { font-weight:700; font-size: 12px; color: var(--muted-color); text-transform: uppercase; letter-spacing: .02em; }15    .color-picker .hue-slider { position:relative; height:16px; border-radius:10px; border:1px solid var(--border-color); background: linear-gradient(to right, #f00 0%, #ff0 17%, #0f0 33%, #0ff 50%, #00f 67%, #f0f 83%, #f00 100%); cursor: ew-resize; touch-action: none; flex: 1 1 auto; min-width: 200px; }16    .color-picker .hue-knob { position:absolute; top:50%; left:93.6%; width:14px; height:14px; border-radius:50%; border:2px solid #fff; transform:translate(-50%, -50%); background: var(--surface-bg); z-index: 2; box-shadow: 0 0 0 1px rgba(0,0,0,.05); }17    .color-picker .hue-slider:focus-visible { outline: 2px solid var(--primary-color); outline-offset: 2px; }18    .color-picker .hue-value { font-variant-numeric: tabular-nums; color: var(--muted-color); font-size: 12px; }19    @media (max-width: 720px) { .color-picker .current-card { grid-template-columns: 1fr; } .color-picker .current-right { padding-left: 0; border-left: none; } }20  </style>21  <div class="picker__stack">22    <div class="current-card">23      <div class="current-left">24        <div class="current-main">25          <div class="current-swatch" aria-label="Current color" title="Current color"></div>26          <div class="current-text">27            <div class="current-name">—</div>28            <div class="current-hex">—</div>29            <div class="current-extra current-lch">—</div>30            <div class="current-extra current-rgb">—</div>31          </div>32        </div>33      </div>34      <div class="current-right">35        <div class="picker__label">Hue</div>36        <div class="hue-slider" role="slider" aria-label="Hue" aria-valuemin="0" aria-valuemax="360" aria-valuenow="337" tabindex="0">37          <div class="hue-knob"></div>38        </div>39        <div class="hue-value">337°</div>40      </div>41    </div>42  </div>43</div>44<script>45  (() => {46    const COLOR_NAMES = [{"name":"Candy Apple Red","hex":"#ff0800"},{"name":"Boiling Magma","hex":"#ff3300"},{"name":"Aerospace Orange","hex":"#ff4f00"},{"name":"Burtuqali Orange","hex":"#ff6700"},{"name":"American Orange","hex":"#ff8b00"},{"name":"Cheese","hex":"#ffa600"},{"name":"Amber","hex":"#ffbf00"},{"name":"Demonic Yellow","hex":"#ffe700"},{"name":"Bat-Signal","hex":"#feff00"},{"name":"Bitter Lime","hex":"#cfff00"},{"name":"Electric Lime","hex":"#ccff00"},{"name":"Bright Yellow Green","hex":"#9dff00"},{"name":"Lasting Lime","hex":"#88ff00"},{"name":"Bright Green","hex":"#66ff00"},{"name":"Chlorophyll Green","hex":"#4aff00"},{"name":"Green Screen","hex":"#22ff00"},{"name":"Electric Pickle","hex":"#00ff04"},{"name":"Acid","hex":"#00ff22"},{"name":"Lucent Lime","hex":"#00ff33"},{"name":"Cathode Green","hex":"#00ff55"},{"name":"Booger Buster","hex":"#00ff77"},{"name":"Green Gas","hex":"#00ff99"},{"name":"Enthusiasm","hex":"#00ffaa"},{"name":"Ice Ice Baby","hex":"#00ffdd"},{"name":"Master Sword Blue","hex":"#00ffee"},{"name":"Agressive Aqua","hex":"#00fbff"},{"name":"Vivid Sky Blue","hex":"#00ccff"},{"name":"Capri","hex":"#00bfff"},{"name":"Sky of Magritte","hex":"#0099ff"},{"name":"Azure","hex":"#007fff"},{"name":"Blue Ribbon","hex":"#0066ff"},{"name":"Blinking Blue","hex":"#0033ff"},{"name":"Icelandic Water","hex":"#0011ff"},{"name":"Blue","hex":"#0000ff"},{"name":"Blue Pencil","hex":"#2200ff"},{"name":"Electric Ultramarine","hex":"#3f00ff"},{"name":"Aladdin's Feather","hex":"#5500ff"},{"name":"Purple Climax","hex":"#8800ff"},{"name":"Amethyst Ganzstar","hex":"#8f00ff"},{"name":"Electric Purple","hex":"#bf00ff"},{"name":"Phlox","hex":"#df00ff"},{"name":"Brusque Pink","hex":"#ee00ff"},{"name":"Bright Magenta","hex":"#ff08e8"},{"name":"Big bang Pink","hex":"#ff00bb"},{"name":"Mean Girls Lipstick","hex":"#ff00ae"},{"name":"Pink","hex":"#ff0099"},{"name":"Hot Flamingoes","hex":"#ff005d"},{"name":"Blazing Dragonfruit","hex":"#ff0054"},{"name":"Carmine Red","hex":"#ff0038"},{"name":"Bright Red","hex":"#ff000d"}];47    if (!window.__colorNames) window.__colorNames = COLOR_NAMES;48 49    if (!window.__colorPickerBus) {50      window.__colorPickerBus = (() => {51        let hue = 337; let adjusting=false; const listeners = new Set();52        return { get: () => ({ hue, adjusting }), publish: (sourceId, nextHue, isAdj) => { hue=((nextHue%360)+360)%360; adjusting=!!isAdj; listeners.forEach(fn => { try { fn({ sourceId, hue, adjusting }); } catch{} }); }, subscribe: (fn) => { listeners.add(fn); return () => listeners.delete(fn); } };53      })();54    }55 56    const bootstrap = () => {57      const root = document.querySelector('.color-picker'); if (!root || root.dataset.mounted) return; root.dataset.mounted='true';58      const slider = root.querySelector('.hue-slider'); const knob = root.querySelector('.hue-knob'); const hueValue = root.querySelector('.hue-value'); const currentSwatch = root.querySelector('.current-swatch'); const currentName = root.querySelector('.current-name'); const currentHex = root.querySelector('.current-hex'); const currentLch = root.querySelector('.current-lch'); const currentRgb = root.querySelector('.current-rgb');59      const bus = window.__colorPickerBus; const instanceId = Math.random().toString(36).slice(2);60      const getKnobRadius = () => { try { const w = knob ? knob.getBoundingClientRect().width : 0; return w ? w/2 : 8; } catch { return 8; } };61      const hexToHsl = (H) => {62        const s = H.replace('#','');63        const v = s.length===3 ? s.split('').map(ch=>ch+ch).join('') : s;64        const bigint = parseInt(v, 16);65        let r = (bigint >> 16) & 255, g = (bigint >> 8) & 255, b = bigint & 255;66        r /= 255; g /= 255; b /= 255;67        const max = Math.max(r, g, b), min = Math.min(r, g, b);68        let h = 0, s2 = 0, l = (max + min) / 2;69        if (max !== min) {70          const d = max - min;71          s2 = l > 0.5 ? d / (2 - max - min) : d / (max + min);72          switch (max) { case r: h = (g - b) / d + (g < b ? 6 : 0); break; case g: h = (b - r) / d + 2; break; default: h = (r - g) / d + 4; }73          h /= 6;74        }75        return { h: Math.round(h*360), s: Math.round(s2*100), l: Math.round(l*100) };76      };77      const hslToHex = (h, s, l) => {78        s /= 100; l /= 100;79        const k = n => (n + h/30) % 12;80        const a = s * Math.min(l, 1 - l);81        const f = n => l - a * Math.max(-1, Math.min(k(n) - 3, Math.min(9 - k(n), 1)));82        const toHex = x => Math.round(255 * x).toString(16).padStart(2, '0');83        return `#${toHex(f(0))}${toHex(f(8))}${toHex(f(4))}`.toUpperCase();84      };85      // Precompute hues for the provided color-name list86      const NAME_HUES = COLOR_NAMES.map((c) => {87        const hh = hexToHsl(c.hex).h || 0;88        return { name: c.name, hue: hh };89      });90      // Pick closest name by circular hue distance; fallback to coarse labels91      const getName = (hex) => {92        const h = hexToHsl(hex).h || 0;93        let bestName = '—';94        let best = 361;95        for (let i = 0; i < NAME_HUES.length; i++) {96          const hh = NAME_HUES[i].hue;97          const d = Math.abs(hh - h);98          const dist = Math.min(d, 360 - d);99          if (dist < best) { best = dist; bestName = NAME_HUES[i].name; }100        }101        if (bestName !== '—') return bestName;102        const labels=['Red','Orange','Yellow','Lime','Green','Cyan','Blue','Indigo','Violet','Magenta'];103        const idx=Math.round(((h%360)/360)*(labels.length-1));104        return labels[idx];105      };106      const updateUI = (h, adjusting) => { const rect = slider.getBoundingClientRect(); const r=Math.min(getKnobRadius(), Math.max(0, rect.width/2 - 1)); const t=Math.max(0, Math.min(1, (h/360))); const leftPx = r + t * Math.max(0, (rect.width - 2*r)); if (knob) knob.style.left = (leftPx/rect.width*100) + '%'; if (hueValue) hueValue.textContent=`${Math.round(h)}°`; if (slider) slider.setAttribute('aria-valuenow', String(Math.round(h))); const L=62, S=72; const baseHex=hslToHex(h,S,L); if (currentSwatch) currentSwatch.style.background=baseHex; if (currentName) currentName.textContent=getName(baseHex); if (currentHex) currentHex.textContent=baseHex; if (currentLch) currentLch.textContent = `HSL ${L}, ${S}, ${Math.round(h)}°`; if (currentRgb){ const hex=baseHex.replace('#',''); const R=parseInt(hex.slice(0,2),16), G=parseInt(hex.slice(2,4),16), B=parseInt(hex.slice(4,6),16); currentRgb.textContent=`RGB ${R}, ${G}, ${B}`; } const hoverHex=hslToHex(h, Math.max(0,S-10), Math.max(0, L-8)); const rootEl=document.documentElement; rootEl.style.setProperty('--primary-color', baseHex); rootEl.style.setProperty('--primary-color-hover', hoverHex); };107      const getHueFromEvent = (ev) => { const rect=slider.getBoundingClientRect(); const clientX=ev.touches ? ev.touches[0].clientX : ev.clientX; const x = clientX - rect.left; const r=Math.min(getKnobRadius(), Math.max(0, rect.width/2 - 1)); const effX=Math.max(r, Math.min(rect.width - r, x)); const denom=Math.max(1, rect.width - 2*r); const t=(effX - r) / denom; return t*360; };108      const unsubscribe = bus.subscribe(({ sourceId, hue, adjusting }) => { if (sourceId === instanceId) return; updateUI(hue, adjusting); });109      try { let initH=337; if (window.ColorPalettes && typeof window.ColorPalettes.getPrimary==='function'){ const hex=window.ColorPalettes.getPrimary(); initH = hexToHsl(hex).h || initH; } else { const cssPrimary=getComputedStyle(document.documentElement).getPropertyValue('--primary-color').trim(); if (cssPrimary) { initH = hexToHsl(cssPrimary).h || initH; } } const { hue: sharedHue } = bus.get(); updateUI(initH ?? sharedHue, false); bus.publish(instanceId, initH ?? sharedHue, false); } catch { const { hue: sharedHue } = bus.get(); updateUI(sharedHue, false); }110      const onDown = (ev) => { ev.preventDefault(); const h=getHueFromEvent(ev); updateUI(h, true); bus.publish(instanceId, h, true); const move=(e)=>{ e.preventDefault && e.preventDefault(); const hh=getHueFromEvent(e); updateUI(hh, true); bus.publish(instanceId, hh, true); }; const up=()=>{ bus.publish(instanceId, getHueFromEvent(ev), false); window.removeEventListener('mousemove', move); window.removeEventListener('touchmove', move); window.removeEventListener('mouseup', up); window.removeEventListener('touchend', up); }; window.addEventListener('mousemove', move, { passive:false }); window.addEventListener('touchmove', move, { passive:false }); window.addEventListener('mouseup', up, { once:true }); window.addEventListener('touchend', up, { once:true }); };111      if (slider){ slider.addEventListener('mousedown', onDown); slider.addEventListener('touchstart', onDown, { passive:false }); slider.addEventListener('keydown', (e)=>{ const step=e.shiftKey?10:2; if (e.key==='ArrowLeft'){ e.preventDefault(); const { hue } = bus.get(); const h=hue-step; updateUI(h, true); bus.publish(instanceId, h, true); bus.publish(instanceId, h, false); } if (e.key==='ArrowRight'){ e.preventDefault(); const { hue } = bus.get(); const h=hue+step; updateUI(h, true); bus.publish(instanceId, h, true); bus.publish(instanceId, h, false); } }); }112      const ro=new MutationObserver(()=>{ if (!document.body.contains(root)){ unsubscribe && unsubscribe(); ro.disconnect(); } }); ro.observe(document.body, { childList:true, subtree:true });113    };114    if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', bootstrap, { once:true }); else bootstrap();115  })();116</script>117 118 119