"use client"; /** * Shared "Applicable Scope" field used by the `decision-approaches` and * `conflict-management` create flow modals. Pairs an `InputLabel` with a * horizontally-wrapping list of toggle-chips plus an inline "+ Add" affordance * that reveals a pill text input for creating new scope values. */ import { memo, useState } from "react"; import Chip from "../../../components/controls/Chip"; import InputLabel from "../../../components/type/InputLabel"; export interface ApplicableScopeFieldProps { /** Label rendered above the capsule row. */ label: string; /** Text for the "+ Add …" affordance (e.g. "Add Applicable Scope"). */ addLabel: string; /** * The full list of chip values shown to the user. Each value is a unique * string (chip label). */ scopes: string[]; /** Values currently toggled on (rendered in the Chip "Selected" state). */ selectedScopes: string[]; /** Fired when a chip is clicked; caller toggles inclusion in `selectedScopes`. */ onToggleScope: (_scope: string) => void; /** * Fired when the user submits a new scope via the inline input. Duplicate * values (already in `scopes`) are filtered out before the callback fires. */ onAddScope: (_scope: string) => void; /** * Optional placeholder for the inline input. Defaults to `addLabel`. */ inputPlaceholder?: string; className?: string; } function ApplicableScopeFieldComponent({ label, addLabel, scopes, selectedScopes, onToggleScope, onAddScope, inputPlaceholder, className = "", }: ApplicableScopeFieldProps) { const [draft, setDraft] = useState(""); const [isAdding, setIsAdding] = useState(false); const submitDraft = () => { const trimmed = draft.trim(); if (!trimmed) { setIsAdding(false); setDraft(""); return; } if (!scopes.includes(trimmed)) { onAddScope(trimmed); } setDraft(""); setIsAdding(false); }; return (
{scopes.map((scope) => { const isSelected = selectedScopes.includes(scope); return ( onToggleScope(scope)} ariaLabel={`${isSelected ? "Deselect" : "Select"} ${scope}`} /> ); })} {isAdding ? ( setDraft(e.target.value)} onBlur={submitDraft} onKeyDown={(e) => { if (e.key === "Enter") { e.preventDefault(); submitDraft(); } else if (e.key === "Escape") { setDraft(""); setIsAdding(false); } }} placeholder={inputPlaceholder ?? addLabel} aria-label={inputPlaceholder ?? addLabel} className="h-[30px] rounded-[9999px] border border-[var(--color-border-default-tertiary)] bg-transparent px-3 font-inter text-[length:var(--sizing-300,12px)] font-medium leading-[14px] text-[color:var(--color-content-default-primary)] outline-none placeholder:text-[color:var(--color-content-default-tertiary)] focus-visible:border-[var(--color-border-default-brand-primary)]" /> ) : ( )}
); } function AddGlyph() { return ( ); } ApplicableScopeFieldComponent.displayName = "ApplicableScopeField"; export default memo(ApplicableScopeFieldComponent);