Files
bee-dashboard/src/modules/filemanager/components/FileBrowser/FileBrowserHeader/FileBrowserHeader.tsx
T
Bálint Ujvári 5bfe2a0331 Feat: FileManager (#98) (#703)
* feat: add file manager module

- Complete file manager implementation with UI/UX
- Add drive management functionality
- Add file upload/download with progress tracking
- Add stamp integration and handling
- Add bulk operations and context menus

Co-authored-by: Roland Seres <roland.seres90@gmail.com>
Co-authored-by: nidishk <nidishkrishnan45@gmail.com>
2025-11-12 11:26:00 +01:00

189 lines
4.8 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { ReactElement } from 'react'
import DownIcon from 'remixicon-react/ArrowDownSLineIcon'
import { useBulkActions } from '../../../hooks/useBulkActions'
import { SortDir, SortKey } from '../../../hooks/useSorting'
import { capitalizeFirstLetter } from '../../../../../../src/modules/filemanager/utils/common'
interface FileBrowserHeaderProps {
isSearchMode: boolean
bulk: ReturnType<typeof useBulkActions>
sortKey: SortKey
sortDir: SortDir
onSortName: () => void
onSortSize: () => void
onSortDate: () => void
onSortDrive: () => void
onClearSort: () => void
}
enum AriaSortValue {
Ascending = 'ascending',
Descending = 'descending',
None = 'none',
}
const Arrow = ({ active, dir }: { active: boolean; dir: SortDir }) => {
let title: string | undefined
if (active) {
const sortValue = dir === SortDir.Asc ? AriaSortValue.Ascending : AriaSortValue.Descending
title = capitalizeFirstLetter(sortValue)
} else {
title = undefined
}
return (
<div
className={'fm-file-browser-content-header-item-icon' + (active ? '' : ' is-inactive')}
aria-hidden={title ? 'false' : 'true'}
aria-label={title}
title={title}
>
<DownIcon size="16px" />
</div>
)
}
function HeaderCell({
label,
isActive,
dir,
onToggle,
onClear,
ariaSort,
'data-testid': testId,
}: {
label: string
isActive: boolean
dir: SortDir
onToggle: () => void
onClear: () => void
ariaSort: AriaSortValue
'data-testid'?: string
}) {
return (
<div className="fm-header-cell" role="columnheader" aria-sort={ariaSort} data-testid={testId}>
<button
type="button"
className="fm-header-button"
onClick={onToggle}
data-dir={isActive ? dir : undefined}
aria-label={
isActive
? `Sort by ${label.toLowerCase()}, currently ${
dir === SortDir.Asc ? AriaSortValue.Ascending : AriaSortValue.Descending
}`
: `Sort by ${label.toLowerCase()}`
}
title={
isActive
? `Currently ${capitalizeFirstLetter(
dir === SortDir.Asc ? AriaSortValue.Ascending : AriaSortValue.Descending,
)}`
: 'Click to sort'
}
>
<span>{label}</span>
<Arrow active={isActive} dir={dir} />
</button>
{isActive && (
<button
type="button"
className="fm-sort-clear"
onClick={e => {
e.preventDefault()
e.stopPropagation()
onClear()
}}
aria-label="Reset sorting to default"
title="Clear sorting"
>
×
</button>
)}
</div>
)
}
export function FileBrowserHeader({
isSearchMode,
bulk,
sortKey,
sortDir,
onSortName,
onSortSize,
onSortDate,
onSortDrive,
onClearSort,
}: FileBrowserHeaderProps): ReactElement {
const ariaSort = (thisKey: SortKey): AriaSortValue => {
if (sortKey !== thisKey) return AriaSortValue.None
return sortDir === SortDir.Asc ? AriaSortValue.Ascending : AriaSortValue.Descending
}
return (
<div className="fm-file-browser-content-header" role="row">
<input
type="checkbox"
checked={bulk.allChecked}
ref={el => {
if (el) el.indeterminate = bulk.someChecked
}}
onChange={e => (e.target.checked ? bulk.selectAll() : bulk.clearAll())}
/>
<div className="fm-file-browser-content-header-item fm-name">
<HeaderCell
label="Name"
isActive={sortKey === SortKey.Name}
dir={sortDir}
onToggle={onSortName}
onClear={onClearSort}
ariaSort={ariaSort(SortKey.Name)}
data-testid="hdr-name"
/>
</div>
{isSearchMode && (
<div className="fm-file-browser-content-header-item fm-drive">
<HeaderCell
label="Drive"
isActive={sortKey === SortKey.Drive}
dir={sortDir}
onToggle={onSortDrive}
onClear={onClearSort}
ariaSort={ariaSort(SortKey.Drive)}
data-testid="hdr-drive"
/>
</div>
)}
<div className="fm-file-browser-content-header-item fm-size">
<HeaderCell
label="Size"
isActive={sortKey === SortKey.Size}
dir={sortDir}
onToggle={onSortSize}
onClear={onClearSort}
ariaSort={ariaSort(SortKey.Size)}
data-testid="hdr-size"
/>
</div>
<div className="fm-file-browser-content-header-item fm-date-mod">
<HeaderCell
label="Date mod."
isActive={sortKey === SortKey.Timestamp}
dir={sortDir}
onToggle={onSortDate}
onClear={onClearSort}
ariaSort={ariaSort(SortKey.Timestamp)}
data-testid="hdr-date"
/>
</div>
</div>
)
}