8618aa1be3
- frontend/functions/core/format_datetime_short.md - frontend/functions/core/format_datetime_short.test.ts - frontend/functions/core/format_datetime_short.ts - frontend/functions/core/format_duration.md - frontend/functions/core/format_duration.test.ts - frontend/functions/core/format_duration.ts - frontend/functions/core/month_grid.md - frontend/functions/core/month_grid.test.ts - frontend/functions/core/month_grid.ts - frontend/functions/core/string_hash_palette.md - ... Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
106 lines
3.8 KiB
Markdown
106 lines
3.8 KiB
Markdown
---
|
|
name: sticker_picker
|
|
kind: component
|
|
lang: ts
|
|
domain: ui
|
|
version: "1.0.0"
|
|
framework: react
|
|
purity: impure
|
|
signature: "StickerPicker(props: StickerPickerProps): JSX.Element"
|
|
description: "Selector de emoji/sticker encapsulado en un Popover de Mantine. Monta emoji-mart Picker una sola vez para evitar re-creaciones en cada render."
|
|
tags: [emoji, sticker, picker, popover, mantine, emoji-mart, react]
|
|
uses_functions: []
|
|
uses_types: []
|
|
returns: []
|
|
returns_optional: false
|
|
error_type: ""
|
|
imports:
|
|
- "@mantine/core"
|
|
- "@emoji-mart/data"
|
|
- "emoji-mart"
|
|
tested: false
|
|
tests: []
|
|
test_file_path: ""
|
|
file_path: "frontend/functions/ui/sticker_picker.tsx"
|
|
props:
|
|
- name: opened
|
|
type: "boolean"
|
|
required: true
|
|
description: "Controla si el Popover está abierto."
|
|
- name: onClose
|
|
type: "() => void"
|
|
required: true
|
|
description: "Callback invocado al cerrar (click fuera, Escape, o tras selección)."
|
|
- name: onSelect
|
|
type: "(emoji: string) => void"
|
|
required: true
|
|
description: "Callback invocado con el emoji seleccionado. Prefiere unicode nativo; si no está disponible usa shortcode."
|
|
- name: target
|
|
type: "React.ReactNode"
|
|
required: true
|
|
description: "Elemento ancla que dispara el Popover."
|
|
- name: theme
|
|
type: '"dark" | "light" | "auto"'
|
|
required: false
|
|
description: 'Tema visual del Picker de emoji-mart. Por defecto "dark".'
|
|
- name: position
|
|
type: 'PopoverProps["position"]'
|
|
required: false
|
|
description: 'Posición del Popover respecto al ancla. Por defecto "bottom-start".'
|
|
emits: [onClose, onSelect]
|
|
has_state: false
|
|
output: "Popover de Mantine con emoji-mart Picker embebido. Tras selección emite onSelect(emoji) y onClose()."
|
|
params:
|
|
- name: opened
|
|
desc: "Estado de visibilidad del picker. Gestionado por el componente padre."
|
|
- name: onClose
|
|
desc: "Se llama cuando el picker debe cerrarse."
|
|
- name: onSelect
|
|
desc: "Se llama con el string unicode o shortcode del emoji elegido."
|
|
- name: target
|
|
desc: "Nodo React que actúa como ancla del Popover (generalmente un botón o icono)."
|
|
- name: theme
|
|
desc: 'Tema de color del picker. Valores: "dark" | "light" | "auto". Por defecto "dark".'
|
|
- name: position
|
|
desc: 'Posición del Popover. Cualquier valor de PopoverProps["position"] de Mantine. Por defecto "bottom-start".'
|
|
---
|
|
|
|
## Ejemplo
|
|
|
|
```tsx
|
|
import { useState } from "react";
|
|
import { ActionIcon } from "@mantine/core";
|
|
import { StickerPicker } from "@fn_library/sticker_picker";
|
|
|
|
function MyCard() {
|
|
const [pickerOpen, setPickerOpen] = useState(false);
|
|
const [sticker, setSticker] = useState<string | null>(null);
|
|
|
|
return (
|
|
<StickerPicker
|
|
opened={pickerOpen}
|
|
onClose={() => setPickerOpen(false)}
|
|
onSelect={(emoji) => setSticker(emoji)}
|
|
target={
|
|
<ActionIcon onClick={() => setPickerOpen((o) => !o)}>
|
|
{sticker ?? "😀"}
|
|
</ActionIcon>
|
|
}
|
|
theme="dark"
|
|
position="bottom-start"
|
|
/>
|
|
);
|
|
}
|
|
```
|
|
|
|
## Notas
|
|
|
|
**Dependencias externas requeridas** (no incluidas en el registry):
|
|
```bash
|
|
pnpm add @emoji-mart/data emoji-mart
|
|
```
|
|
|
|
El componente interno `PickerInner` instancia `emoji-mart` Picker con `useEffect` y lo monta en un `<div ref>`. El cleanup borra el `innerHTML` del div al desmontar. `onSelectRef` mantiene el callback actualizado sin recrear la instancia en cada render del padre. El prop `theme` se pasa al montar — cambios posteriores de `theme` no provocan remontaje (la instancia mantiene el tema inicial), comportamiento aceptable para la mayoría de casos de uso. Si se necesita cambio dinámico de tema, destructurar el key del componente padre fuerza remontaje.
|
|
|
|
El Popover usa `withinPortal` para evitar clipping por overflow de contenedores padre, `closeOnClickOutside` y `closeOnEscape` para comportamiento estándar, y `trapFocus={false}` para que emoji-mart gestione el foco internamente.
|