WalisonCruz/function-gemma
0
1import gradio as gr2 3from gradio.events import Dependency4 5class ColoredDropdown(gr.HTML):6 def __init__(7 self,8 choices: list[str] | None = None,9 *,10 placeholder: str = "Select...",11 value: str | None = None,12 colors: list[str] | None = None,13 label: str | None = None,14 **kwargs,15 ):16 html_template = """17 <div class="colored-dropdown-container">18 ${label ? `<span class="dropdown-label">${label}</span>` : ''}19 <div class="dropdown-wrapper">20 <button type="button" class="dropdown-trigger">21 <span class="selected-display">22 ${value ? `<span class="color-dot" style="background: ${colors[choices.indexOf(value)]};"></span>${value}` : `<span class="placeholder">${placeholder}</span>`}23 </span>24 <svg class="dropdown-arrow" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">25 <polyline points="6 9 12 15 18 9"></polyline>26 </svg>27 </button>28 <div class="dropdown-menu">29 ${choices.map((choice, i) => `30 <div class="dropdown-item ${value === choice ? 'selected' : ''}" data-value="${choice}" data-color="${colors[i]}">31 <span class="color-dot" style="background: ${colors[i]};"></span>32 <span class="item-text">${choice}</span>33 </div>34 `).join('')}35 </div>36 </div>37 </div>38 """39 40 css_template = """41 .colored-dropdown-container {42 display: flex;43 flex-direction: column;44 gap: var(--spacing-sm);45 overflow: visible;46 }47 .dropdown-label {48 color: var(--block-title-text-color);49 font-size: var(--text-sm);50 font-weight: 500;51 }52 .dropdown-wrapper {53 position: relative;54 z-index: 1;55 }56 .dropdown-trigger {57 width: 100%;58 display: flex;59 align-items: center;60 justify-content: space-between;61 padding: 8px 12px;62 border: 1px solid var(--border-color-primary);63 border-radius: var(--radius-md);64 background: var(--input-background-fill);65 color: var(--body-text-color);66 cursor: pointer;67 font-size: var(--text-md);68 text-align: left;69 transition: border-color 0.15s ease;70 }71 .dropdown-trigger:hover {72 border-color: var(--border-color-accent);73 }74 .dropdown-trigger:focus {75 outline: none;76 border-color: var(--color-accent);77 box-shadow: 0 0 0 2px var(--color-accent-soft);78 }79 .selected-display {80 display: flex;81 align-items: center;82 gap: 8px;83 flex: 1;84 overflow: hidden;85 text-overflow: ellipsis;86 white-space: nowrap;87 }88 .placeholder {89 color: var(--input-placeholder-color);90 }91 .dropdown-arrow {92 width: 16px;93 height: 16px;94 flex-shrink: 0;95 transition: transform 0.2s ease;96 }97 .dropdown-wrapper.open .dropdown-arrow {98 transform: rotate(180deg);99 }100 .dropdown-menu {101 position: fixed;102 max-height: 240px;103 overflow-y: auto;104 background: var(--background-fill-primary);105 border: 1px solid var(--border-color-primary);106 border-radius: var(--radius-md);107 box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);108 z-index: 9999;109 display: none;110 }111 .dropdown-wrapper.open .dropdown-menu {112 display: block;113 }114 .dropdown-item {115 display: flex;116 align-items: center;117 gap: 8px;118 padding: 8px 12px;119 cursor: pointer;120 transition: background 0.1s ease;121 }122 .dropdown-item:hover {123 background: var(--background-fill-secondary);124 }125 .dropdown-item.selected {126 background: var(--color-accent-soft);127 }128 .item-text {129 overflow: hidden;130 text-overflow: ellipsis;131 white-space: nowrap;132 }133 .color-dot {134 width: 10px;135 height: 10px;136 border-radius: 50%;137 flex-shrink: 0;138 }139 """140 141 js_on_load = """142 const wrapper = element.querySelector('.dropdown-wrapper');143 const triggerButton = element.querySelector('.dropdown-trigger');144 const menu = element.querySelector('.dropdown-menu');145 const selectedDisplay = element.querySelector('.selected-display');146 147 function positionMenu() {148 const rect = triggerButton.getBoundingClientRect();149 menu.style.position = 'fixed';150 menu.style.top = (rect.bottom + 4) + 'px';151 menu.style.left = rect.left + 'px';152 menu.style.width = rect.width + 'px';153 menu.style.right = 'auto';154 }155 156 triggerButton.addEventListener('click', (e) => {157 e.stopPropagation();158 wrapper.classList.toggle('open');159 if (wrapper.classList.contains('open')) {160 positionMenu();161 }162 });163 164 menu.addEventListener('click', (e) => {165 const item = e.target.closest('.dropdown-item');166 if (!item) return;167 168 const value = item.dataset.value;169 const color = item.dataset.color;170 171 menu.querySelectorAll('.dropdown-item').forEach(el => el.classList.remove('selected'));172 item.classList.add('selected');173 174 selectedDisplay.innerHTML = `<span class="color-dot" style="background: ${color};"></span>${value}`;175 176 props.value = value;177 trigger('input');178 179 wrapper.classList.remove('open');180 });181 182 document.addEventListener('click', (e) => {183 if (!element.contains(e.target)) {184 wrapper.classList.remove('open');185 }186 });187 188 window.addEventListener('resize', () => {189 if (wrapper.classList.contains('open')) {190 positionMenu();191 }192 });193 194 window.addEventListener('scroll', () => {195 if (wrapper.classList.contains('open')) {196 positionMenu();197 }198 }, true);199 200 triggerButton.addEventListener('keydown', (e) => {201 if (e.key === 'Enter' || e.key === ' ') {202 e.preventDefault();203 wrapper.classList.toggle('open');204 if (wrapper.classList.contains('open')) {205 positionMenu();206 }207 } else if (e.key === 'Escape') {208 wrapper.classList.remove('open');209 }210 });211 """212 213 super().__init__(214 value=value,215 html_template=html_template,216 css_template=css_template,217 js_on_load=js_on_load,218 choices=choices,219 colors=colors,220 label=label,221 placeholder=placeholder,222 **kwargs,223 )224 225 def api_info(self):226 return {227 "enum": self.props["choices"],228 "title": "Dropdown",229 "type": "string",230 }231 from typing import Callable, Literal, Sequence, Any, TYPE_CHECKING232 from gradio.blocks import Block233 if TYPE_CHECKING:234 from gradio.components import Timer235 from gradio.components.base import Component236 237 238if __name__ == "__main__":239 240 def generate_color_variants(color: str, count: int):241 if color.startswith("rgb"):242 rgb_values = (243 color.replace("rgba", "").replace("rgb", "").strip("()").split(",")244 )245 r, g, b = (246 int(float(rgb_values[0])),247 int(float(rgb_values[1])),248 int(float(rgb_values[2])),249 )250 else:251 r = int(color[1:3], 16)252 g = int(color[3:5], 16)253 b = int(color[5:7], 16)254 255 colors = []256 for i in range(count):257 factor = 0.3 + (0.7 * i / max(count - 1, 1))258 nr = int(r * factor + (255 - r * factor) * (1 - factor) * 0.3)259 ng = int(g * factor + (255 - g * factor) * (1 - factor) * 0.3)260 nb = int(b * factor + (255 - b * factor) * (1 - factor) * 0.3)261 nr, ng, nb = min(255, nr), min(255, ng), min(255, nb)262 colors.append(f"#{nr:02x}{ng:02x}{nb:02x}")263 return colors264 265 def update_dropdown(color: str, s: int):266 items = [f"Item {i + 1} ({s + 1})" for i in range(12)]267 colors = generate_color_variants(color, len(items))268 return ColoredDropdown(269 choices=items,270 colors=colors,271 label=f"Select Item ({s + 1})",272 ), s + 1273 274 with gr.Blocks() as demo:275 s = gr.State(0)276 with gr.Row():277 with gr.Column():278 cp = gr.ColorPicker(value="#FF0000", label="Base Color")279 with gr.Column(scale=2):280 items = [f"Item {i + 1}" for i in range(15)]281 colors = generate_color_variants("#FF0000", len(items))282 dropdown = ColoredDropdown(283 choices=items,284 colors=colors,285 label="Select Item",286 value=items[0],287 )288 output = gr.Textbox(label="Selected Value")289 dropdown.input(lambda x: x, inputs=dropdown, outputs=output)290 cp.change(291 update_dropdown,292 inputs=[cp, s],293 outputs=[dropdown, s],294 show_progress="hidden",295 )296 demo.launch()