Skip to content
Snippets Groups Projects

(ui): Add custom scroll bar

Merged Matthias Feyll requested to merge ui-add-scroll-devices into ui-integration
16 files
+ 574
387
Compare changes
  • Side-by-side
  • Inline
Files
16
import { useDeviceBoxViewModel } from '@component/devices/view_model/device.box.viewmodel'
import { faPlus } from '@fortawesome/free-solid-svg-icons'
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
import { insertMarkTags } from '@helper/text'
import { Scrollbar } from '@shared/components/scrollbar/Scrollbar.view'
import DOMPurify from 'dompurify'
import { RefObject, useCallback } from 'react'
import { Button, Col, Form, OverlayTrigger, Row, Tooltip } from 'react-bootstrap'
import { useTranslation } from 'react-i18next'
import { Device } from '../../reducer/device.reducer'
import AddDeviceModal from '../subcomponent/modal.view'
export const DeviceList = ({ searchRef }: { searchRef: RefObject<HTMLInputElement> }) => {
const { t } = useTranslation('common')
const {
filteredDevices,
handleItemClick,
selectedDevice,
pnds,
addModal,
openAddModal,
closeModal,
searchValue,
handleSearch,
} = useDeviceBoxViewModel(searchRef)
const cropUUID = (uuid: string): string => {
return uuid.substring(0, 3) + '...' + uuid.substring(uuid.length - 3, uuid.length)
}
const renderDeviceItem = useCallback(
(device: Device) => {
const user = pnds.find(pnd => pnd.id === device.pid)
const username = user?.name || ''
const deviceId = device.id!
const croppedId = cropUUID(deviceId)
const devicename = device.name || ''
const isSelected = selectedDevice?.device.id === deviceId
return (
<div
key={deviceId}
className={`border-bottom border-primary p-2 transitions ${
isSelected && 'bg-gradient-fade py-2'
} ${!isSelected && 'text-disabled disabled-hover'}`}
onClick={() => handleItemClick(device)}>
<Row className="align-items-center clickable">
<Col xs={12} sm={5}>
<span
dangerouslySetInnerHTML={{
__html: searchValue
? insertMarkTags(devicename, searchValue)
: DOMPurify.sanitize(devicename),
}}
/>
</Col>
<Col xs={12} sm={3}>
<OverlayTrigger overlay={<Tooltip id={deviceId}>{deviceId}</Tooltip>}>
<span
className="text-gray-500"
dangerouslySetInnerHTML={{
__html: searchValue
? insertMarkTags(croppedId, searchValue)
: DOMPurify.sanitize(croppedId),
}}
/>
</OverlayTrigger>
</Col>
<Col xs={12} sm={4}>
<span
className="text-gray-500"
dangerouslySetInnerHTML={{
__html: searchValue
? insertMarkTags(username, searchValue)
: DOMPurify.sanitize(username),
}}
/>
</Col>
</Row>
</div>
)
},
[selectedDevice, pnds, handleItemClick, searchValue],
)
return (
<div className="d-flex flex-column h-100">
{/* Fixed top section */}
<div className="flex-shrink-0">
<Row className="mb-3 align-items-center">
<Col xs={12} md={6} lg={8}>
<Form.Group controlId="device.search">
<Form.Control
type="text"
placeholder={t('device.search.placeholder')}
ref={searchRef}
value={searchValue}
onChange={e => handleSearch(e.target.value)}
/>
</Form.Group>
</Col>
<Col xs={12} md={6} lg={4} className="mt-3 mt-md-0 text-md-end">
<Button
variant="primary::button"
className="btn-primary-button"
onClick={openAddModal}>
<FontAwesomeIcon icon={faPlus} className="me-2" />
{t('device.add_device_button')}
</Button>
<AddDeviceModal show={addModal} onHide={closeModal} />
</Col>
</Row>
</div>
{/* Scrollable list section */}
<Scrollbar className="flex-grow-1 overflow-y-auto overflow-x-hidden" scrollX={false}>
<div className="rounded border border-primary">
{/* Fixed header */}
<div className="sticky-top bg-white border-bottom border-primary">
<Row className="px-2 py-2 mx-0">
<Col xs={12} sm={5}>
<span className="font-medium">{t('device.table.header.name')}</span>
</Col>
<Col xs={12} sm={3}>
<span className="font-medium">{t('device.table.header.uuid')}</span>
</Col>
<Col xs={12} sm={4}>
<span className="font-medium">{t('device.table.header.user')}</span>
</Col>
</Row>
</div>
{/* Scrollable content */}
<div className="device-list-content">
{filteredDevices.map(renderDeviceItem)}
</div>
</div>
</Scrollbar>
</div>
)
}
Loading