---
name: color_picker_grid
kind: component
lang: ts
domain: ui
version: "1.0.0"
purity: impure
framework: react
signature: "ColorPickerGrid(props: ColorPickerGridProps): JSX.Element"
description: "Grid de swatches de color con boton extra que abre un modal con ColorPicker de Mantine para seleccionar un hexadecimal libre. Soporta tokens Mantine y valores hex. El swatch activo recibe borde blanco + box-shadow azul; el custom-active aplica el mismo feedback visual."
tags: [color, picker, swatch, grid, modal, mantine, ui, pendiente-usar]
uses_functions:
  - color_swatch_ts_ui
  - color_border_ts_ui
uses_types: []
returns: []
returns_optional: false
error_type: ""
imports:
  - "@mantine/core"
  - "@tabler/icons-react"
tested: false
tests: []
test_file_path: ""
file_path: "frontend/functions/ui/color_picker_grid.tsx"
has_state: true
props:
  - name: value
    type: "string"
    required: true
    description: "Color seleccionado actualmente. Token Mantine (blue, red, ...) o hex (#rrggbb)."
  - name: onChange
    type: "(color: string) => void"
    required: true
    description: "Callback invocado al seleccionar cualquier swatch o cambiar el color en el picker libre."
  - name: options
    type: "{ value: string; label: string }[]"
    required: false
    description: "Lista de opciones a mostrar como swatches circulares. Default: DEFAULT_COLOR_OPTIONS (27 entradas)."
  - name: swatchSize
    type: "number"
    required: false
    description: "Diametro de cada swatch en px. Default: 26."
  - name: customSwatches
    type: "string[]"
    required: false
    description: "Array de hex para los swatches rapidos dentro del ColorPicker de Mantine. Default: 14 colores."
  - name: modalTitle
    type: "string"
    required: false
    description: "Titulo del modal que contiene el ColorPicker libre. Default: 'Color personalizado'."
emits:
  - onChange
params:
  - name: value
    desc: "Color activo. Token Mantine o hex."
  - name: onChange
    desc: "Recibe el nuevo color cada vez que el usuario selecciona un swatch o mueve el ColorPicker."
  - name: options
    desc: "Paleta de swatches a renderizar. Omitir para usar DEFAULT_COLOR_OPTIONS."
  - name: swatchSize
    desc: "Tamaño del circulo en pixeles."
  - name: customSwatches
    desc: "Hexadecimales de acceso rapido dentro del picker modal."
  - name: modalTitle
    desc: "Titulo que aparece en la cabecera del modal del picker."
output: "Grupo horizontal de swatches circulares mas boton '+' (IconPalette). Click en swatch llama onChange con el token/hex. Click '+' abre Modal interno con ColorPicker de Mantine; onChange se dispara en cada cambio del picker. Exporta DEFAULT_COLOR_OPTIONS con 27 entradas (tokens Mantine + hexadecimales de acento)."
---

## Ejemplo

```tsx
import { ColorPickerGrid, DEFAULT_COLOR_OPTIONS } from "@fn_library/color_picker_grid";
import { useState } from "react";

function Demo() {
  const [color, setColor] = useState("blue");

  return (
    <ColorPickerGrid
      value={color}
      onChange={setColor}
    />
  );
}
```

Con opciones personalizadas y swatch reducido:

```tsx
<ColorPickerGrid
  value={color}
  onChange={setColor}
  options={[
    { value: "red", label: "Rojo" },
    { value: "blue", label: "Azul" },
    { value: "#ff6600", label: "Naranja custom" },
  ]}
  swatchSize={32}
  modalTitle="Elige un color"
/>
```

## Notas

- `DEFAULT_COLOR_OPTIONS` se exporta desde el mismo archivo para que los consumidores puedan extenderla o filtrarla.
- El estado del color custom (hex libre) es local al componente. Si el valor inicial es hex, se inicializa con el; si no, arranca en `#888888`.
- La deteccion de "custom activo" usa `value.startsWith("#") && !options.some(o => o.value === value)` — un hex que coincide con una opcion de la paleta se trata como swatch fijo, no como custom.
- `colorSwatch` y `colorBorder` del registry resuelven tokens Mantine y hex a valores CSS listos para `background` y `border`.
- El Modal usa `withinPortal`, `zIndex: 2000`, `closeOnClickOutside: false`, `closeOnEscape: false` y `trapFocus: false` para evitar conflictos con Popovers / Menus padres.
