Files
SkinbaseNova/docs/ui/tag-input.md
2026-02-14 15:14:12 +01:00

2.8 KiB

TagInput UI Component

Overview

TagInput is the reusable tag entry component for Skinbase artwork flows.

It is designed for:

  • Upload page
  • Artwork edit page
  • Admin moderation screens

The component encapsulates all tag UX behavior (chips, search, keyboard flow, AI suggestions, status hints) so pages stay thin.

File Location

  • resources/js/components/tags/TagInput.jsx

Props

Prop Type Default Description
value string | string[] required Controlled selected tags. Can be CSV string or array.
onChange (tags: string[]) => void required Called whenever selected tags change.
suggestedTags Array<string | object> [] AI-suggested tags shown as clickable pills.
disabled boolean false Disables input and interactions.
maxTags number 15 Maximum number of selected tags.
minLength number 2 Minimum normalized tag length.
maxLength number 32 Maximum normalized tag length.
placeholder string Type tags… Input placeholder text.
searchEndpoint string /api/tags/search Search API endpoint.
popularEndpoint string /api/tags/popular Popular tags endpoint when input is empty.

Normalization

Tags are normalized client-side before being added:

  • lowercase
  • trim
  • spaces → -
  • remove unsupported characters
  • collapse repeated separators
  • max length = 32

Server-side normalization/validation still applies and remains authoritative.

Keyboard & Interaction

  • Enter → add tag
  • Comma → add tag
  • Tab → accept highlighted suggestion
  • Backspace (empty input) → remove last tag
  • Escape → close suggestion dropdown
  • Paste CSV (a, b, c) → split and add valid tags

Accessibility

  • Suggestion dropdown uses role="listbox"
  • Suggestions use role="option"
  • Active item uses aria-selected
  • Input uses aria-expanded, aria-controls, aria-autocomplete

API Usage

The component performs debounced search (300ms):

  • GET /api/tags/search?q=<query>
  • GET /api/tags/popular (empty query)

Behavior:

  • caches recent query results
  • aborts outdated requests
  • max 8 suggestions
  • excludes already-selected tags
  • shows non-blocking message when search fails

Upload Integration Example

<TagInput
  value={state.metadata.tags}
  onChange={(nextTags) => {
    dispatch({ type: 'SET_METADATA', payload: { tags: nextTags.join(', ') } })
  }}
  suggestedTags={props.suggested_tags || []}
  maxTags={15}
  minLength={2}
  maxLength={32}
/>

Events & Save Strategy

TagInput itself does not persist to backend on keystrokes.

Persistence is done on save/publish boundary by page logic, e.g.:

  • PUT /api/artworks/{id}/tags

This keeps UI responsive and avoids unnecessary API writes.