diff --git a/src/components/common/EntryList.tsx b/src/components/common/EntryList.tsx new file mode 100644 index 0000000..ab5144b --- /dev/null +++ b/src/components/common/EntryList.tsx @@ -0,0 +1,111 @@ +import { useState, KeyboardEvent } from 'react' +import { Button } from '@/components/buttons' +import { Icon, IconName } from '@/components/icons' + +export interface EntryListProps { + /** The entries to display (e.g. file paths) */ + items: string[] + /** Called with a manually-typed entry when the user presses Enter */ + onAddManual: (value: string) => void + /** Called when the browse button (next to the input) is clicked */ + onBrowse: () => void + /** Called with the entry to remove when its remove button is clicked */ + onRemove: (item: string) => void + /** Placeholder text for the manual-entry input */ + placeholder: string + /** Message shown when there are no entries yet */ + emptyLabel: string + /** Disables all interaction, e.g. while a request is in flight */ + disabled?: boolean + /** Icon shown next to each entry */ + icon?: IconName | string +} + +/** + * Generic list of string entries (paths, URLs, anything long and one-line) + * with add/remove controls. The add row (manual entry + browse button) is + * always pinned at the bottom it never scrolls out of view, even once + * the entries above it start scrolling. Long entries are truncated from the + * start so the end, usually the most identifying part stays visible. + */ +const EntryList = ({ + items, + onAddManual, + onBrowse, + onRemove, + placeholder, + emptyLabel, + disabled = false, + icon = 'Folder', +}: EntryListProps) => { + const [manualValue, setManualValue] = useState('') + + const submitManualValue = () => { + const trimmed = manualValue.trim() + if (!trimmed) return + onAddManual(trimmed) + setManualValue('') + } + + const handleKeyDown = (e: KeyboardEvent) => { + if (e.key === 'Enter') submitManualValue() + } + + return ( +
+
+ {items.length === 0 ? ( +
+ +

{emptyLabel}

+
+ ) : ( +
    + {items.map((item) => ( +
  • + + + {item} + +
  • + ))} +
+ )} +
+ +
+ setManualValue(e.target.value)} + onKeyDown={handleKeyDown} + disabled={disabled} + /> +
+
+ ) +} + +export default EntryList