mirror of
https://github.com/Novattz/creamlinux-installer.git
synced 2026-08-01 19:18:25 -04:00
EntryList component
This commit is contained in:
@@ -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<HTMLInputElement>) => {
|
||||||
|
if (e.key === 'Enter') submitManualValue()
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="entry-list">
|
||||||
|
<div className="entry-list-scroll-area">
|
||||||
|
{items.length === 0 ? (
|
||||||
|
<div className="entry-list-empty">
|
||||||
|
<Icon name={icon} className="icon-secondary" variant="solid" size="lg" />
|
||||||
|
<p>{emptyLabel}</p>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<ul className="entry-list-items">
|
||||||
|
{items.map((item) => (
|
||||||
|
<li key={item} className="entry-list-item">
|
||||||
|
<Icon name={icon} variant="solid" size="md" className="entry-list-item-icon" />
|
||||||
|
<span className="entry-list-item-text" title={item}>
|
||||||
|
{item}
|
||||||
|
</span>
|
||||||
|
<Button
|
||||||
|
variant="secondary"
|
||||||
|
size="small"
|
||||||
|
onClick={() => onRemove(item)}
|
||||||
|
disabled={disabled}
|
||||||
|
title="Remove"
|
||||||
|
className="entry-list-remove-button"
|
||||||
|
leftIcon={<Icon name="Trash" variant="solid" size="sm" />}
|
||||||
|
iconOnly
|
||||||
|
/>
|
||||||
|
</li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="entry-list-input-row">
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
className="entry-list-input"
|
||||||
|
placeholder={placeholder}
|
||||||
|
value={manualValue}
|
||||||
|
onChange={(e) => setManualValue(e.target.value)}
|
||||||
|
onKeyDown={handleKeyDown}
|
||||||
|
disabled={disabled}
|
||||||
|
/>
|
||||||
|
<Button
|
||||||
|
variant="primary"
|
||||||
|
size="medium"
|
||||||
|
onClick={onBrowse}
|
||||||
|
disabled={disabled}
|
||||||
|
title="Browse for a folder"
|
||||||
|
className="entry-list-browse-button"
|
||||||
|
leftIcon={<Icon name="Folder" variant="solid" size="md" />}
|
||||||
|
iconOnly
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default EntryList
|
||||||
Reference in New Issue
Block a user