Formulariosinputchips
Chip/Tag Input
Campo de entrada interactivo para etiquetas y chips extraíbles mediante teclado.
Vista Previa en Vivo
Uso
Copia y pega este código directamente en tu componente React:
tsx
import { ChipInput } from 'react-component-kit-ui/forms';
<ChipInput label="Product Tags" initialChips={["New", "Sale"]} placeholder="Add tag..." />Instalación Manual
Si no estás usando el paquete npm, crea un archivo nuevo y pega el siguiente código: src/components/ChipInput.tsx + ChipInput.css:
ChipInput.tsx
'use client';
import { useEffect, useRef } from 'react';
import './ChipInput.css';
/**
* ChipInput — An interactive input for managing a list of tags or chips.
*
* @param {string} label - Input labeling title.
* @param {string[]} initialChips - Optional array of strings to pre-populate.
* @param {string} name - HTML name for form submission.
* @param {string} placeholder - Placeholder text for the input area.
*/
export interface ChipInputProps {
label?: string;
initialChips?: string[];
name?: string;
placeholder?: string;
}
export function ChipInput({ label, initialChips = [], name, placeholder = "Add tag..." }: ChipInputProps) {
// Ported from the Astro <script>: runs once after mount (DOM logic kept as in the Astro kit).
const mounted = useRef(false);
useEffect(() => {
if (mounted.current) return;
mounted.current = true;
// Simplified client-side logic for adding/removing chips
document.querySelectorAll('[data-chip-input]').forEach(container => {
const list = container.querySelector('[data-chip-list]');
const input = container.querySelector('[data-chip-field]') as HTMLInputElement;
const name = "tags"; // Should ideally be passed from props but hidden inputs solve this for now
const addChip = (value: string) => {
if (!value.trim() || !list) return;
const chip = document.createElement('span');
chip.className = 'chip';
chip.innerHTML = `${value}<button type="button" aria-label="Remove ${value}" data-remove-chip>×</button>`;
const hidden = document.createElement('input');
hidden.type = 'hidden';
hidden.name = 'chips[]';
hidden.value = value;
chip.appendChild(hidden);
list.appendChild(chip);
input.value = '';
input.placeholder = '';
};
input?.addEventListener('keydown', (e) => {
if (e.key === 'Enter') {
e.preventDefault();
addChip(input.value);
}
if (e.key === 'Backspace' && !input.value && list?.lastElementChild) {
list.removeChild(list.lastElementChild);
if (list.children.length === 0) input.placeholder = "Add tag...";
}
});
container.addEventListener('click', (e) => {
const target = e.target as HTMLElement;
if (target.hasAttribute('data-remove-chip')) {
target.closest('.chip')?.remove();
if (list?.children.length === 0) input.placeholder = "Add tag...";
}
});
});
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
return (
<>
<div className="chip-input-container" data-rk-36ed34="">
{label && <label className="chip-input-label" data-rk-36ed34="">{label}</label>}
<div className="chip-input" data-chip-input data-rk-36ed34="">
<div className="chip-input__list" data-chip-list data-rk-36ed34="">
{initialChips.map((chip, __i: number) => (
<span key={__i} className="chip" data-rk-36ed34="">
{chip}
<button type="button" aria-label={`Remove ${chip}`} data-remove-chip data-rk-36ed34="">×</button>
<input type="hidden" name={`${name}[]`} defaultValue={chip} data-rk-36ed34="" />
</span>
))}
</div>
<input type="text" className="chip-input__field" placeholder={initialChips.length > 0 ? "" : placeholder} data-chip-field data-rk-36ed34="" />
</div>
</div>
</>
);
}
export default ChipInput;ChipInput.css
/* ChipInput — ported from astro-component-kit (forms/ChipInput.astro) */
.chip-input-container:where([data-rk-36ed34]) { display: flex; flex-direction: column; gap: var(--sp-2, 0.5rem); width: 100%; }
.chip-input-label:where([data-rk-36ed34]) { font-size: 0.85rem; font-weight: 600; color: var(--c-text-2, #94a3b8); margin-left: 0.5rem; }
.chip-input:where([data-rk-36ed34]) {
display: flex; flex-wrap: wrap; gap: 0.5rem; padding: 0.5rem;
background: var(--c-bg-elev, rgba(0,0,0,0.1));
border: 1px solid var(--c-border, rgba(255,255,255,0.1));
border-radius: var(--r-md, 10px);
transition: border-color 0.2s;
}
.chip-input:focus-within:where([data-rk-36ed34]) { border-color: var(--c-primary, #6366f1); }
.chip-input__list:where([data-rk-36ed34]) { display: flex; flex-wrap: wrap; gap: 0.5rem; }
.chip:where([data-rk-36ed34]) {
background: var(--c-primary, #6366f1);
color: #fff;
padding: 0.2rem 0.6rem;
border-radius: var(--r-sm, 6px);
font-size: 0.8rem;
display: flex; align-items: center; gap: 0.4rem;
}
.chip:where([data-rk-36ed34]) button:where([data-rk-36ed34]) {
background: none; border: none; color: #fff; cursor: pointer;
font-size: 1.1rem; line-height: 1; padding: 0;
opacity: 0.8; transition: opacity 0.2s;
}
.chip:where([data-rk-36ed34]) button:hover:where([data-rk-36ed34]) { opacity: 1; }
.chip-input__field:where([data-rk-36ed34]) {
background: none; border: none; outline: none;
color: var(--c-text-1, #fff); flex: 1; min-width: 100px;
padding: 0.2rem; font-family: inherit; font-size: 0.9rem;
}Información Rápida
- Categoría
- Formularios
- Nombre de archivo
ChipInput.tsx- Dependencias
- Ninguna — React + CSS puro
- Etiquetas
- inputchips