5bfe2a0331
* 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>
189 lines
4.8 KiB
TypeScript
189 lines
4.8 KiB
TypeScript
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>
|
||
)
|
||
}
|