Skip to content

Commit

Permalink
feat(device-page): fitler devices in select
Browse files Browse the repository at this point in the history
  • Loading branch information
marcusds committed Dec 23, 2023
1 parent f13702f commit 43c93f0
Show file tree
Hide file tree
Showing 4 changed files with 108 additions and 29 deletions.
92 changes: 92 additions & 0 deletions src/components/device-page/header-device-selector.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
import React, { CSSProperties, PropsWithChildren, forwardRef, useState } from 'react';
import Dropdown from 'react-bootstrap/Dropdown';
import Form from 'react-bootstrap/Form';
import { Devices } from '../../store';
import { TabName } from './types';
import { WithTranslation } from 'react-i18next';
import { TFunction } from 'i18next';

interface SearchMenuProps {
style?: CSSProperties;
className?: string;
searchTerm?: string;
setSearchTerm: (value: string) => void;
t: TFunction;
}

interface HeaderDeviceSelectorProps {
devices: Devices;
dev: string;
tab?: TabName;
t: TFunction;
}

interface HeaderDeviceSelectorItemsProps {
devices: Devices;
dev: string;
tab?: TabName;
setSearchTerm: (value: string) => void;
}

const SearchMenu = forwardRef<HTMLDivElement, PropsWithChildren<SearchMenuProps>>(function SearchMenu(
{ children, style, className, searchTerm, setSearchTerm, t },
ref,
) {
return (
<div ref={ref} style={style} className={className}>
<Form.Control
autoFocus
className="mx-3 my-2 w-auto"
placeholder={t('type_to_filter')}
onChange={(e) => setSearchTerm(e.target.value)}
value={searchTerm}
/>
<ul className="list-unstyled">
{React.Children.toArray(children).filter(
(child) =>
!searchTerm ||
(React.isValidElement(child) &&
child.props.children.toLowerCase().includes(searchTerm.toLowerCase())),
)}
</ul>
</div>
);
});

export function HeaderDeviceSelector(props: HeaderDeviceSelectorProps): JSX.Element {
const { devices, dev, tab = 'info', t } = props;
const [searchTerm, setSearchTerm] = useState<string>('');

const device = devices[dev];

return (
<h1 className="h3">
<Dropdown>
<Dropdown.Toggle aria-label={t('select_a_device')} variant="" size="lg">
{device.friendly_name}{' '}
</Dropdown.Toggle>

<Dropdown.Menu as={SearchMenu} searchTerm={searchTerm} t={t} setSearchTerm={setSearchTerm}>
<HeaderDeviceSelectorItems devices={devices} dev={dev} tab={tab} setSearchTerm={setSearchTerm} />
</Dropdown.Menu>
</Dropdown>
</h1>
);
}

function HeaderDeviceSelectorItems({ devices, dev, tab, setSearchTerm }: HeaderDeviceSelectorItemsProps): JSX.Element {
return (
<>
{Object.entries(devices).map(([id, device]) => (
<Dropdown.Item
active={id === dev}
key={id}
href={`#/device/${id}/${tab}`}
onClick={() => setSearchTerm('')}
>
{device.friendly_name}
</Dropdown.Item>
))}
</>
);
}
31 changes: 3 additions & 28 deletions src/components/device-page/index.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import React from 'react';
import { NavLink, Redirect, RouteComponentProps, withRouter } from 'react-router-dom';
import { connect } from 'unistore/react';
import Dropdown from 'react-bootstrap/Dropdown';
import actions from '../../actions/actions';
import { GlobalState } from '../../store';
import DeviceInfo from './info';
Expand All @@ -18,6 +17,8 @@ import DevConsole from './dev-console';
import { DeviceApi } from '../../actions/DeviceApi';
import { WithTranslation, withTranslation } from 'react-i18next';
import DeviceSpecificSettings from './DeviceSpecificSettings';
import { HeaderDeviceSelector } from './header-device-selector';
import { TabName } from './types';

const getDeviceLinks = (dev: string) => [
{
Expand Down Expand Up @@ -61,17 +62,6 @@ const getDeviceLinks = (dev: string) => [
url: `/device/${dev}/dev-console`,
},
];
type TabName =
| 'info'
| 'bind'
| 'state'
| 'exposes'
| 'clusters'
| 'reporting'
| 'settings'
| 'settings-specific'
| 'dev-console'
| 'scene';
type UrlParams = {
dev: string;
tab?: TabName;
Expand Down Expand Up @@ -135,22 +125,7 @@ export function DevicePage(props: DevicePageProps): JSX.Element {

return (
<>
<h1 className="h3">
<Dropdown>
<Dropdown.Toggle aria-label={t('select_a_device')} variant="" size="lg">
{device.friendly_name}{' '}
</Dropdown.Toggle>

<Dropdown.Menu>
{Object.entries(devices).map(([id, device]) => (
<Dropdown.Item active={id === dev} key={id} href={`#/device/${id}/${tab}`}>
{device.friendly_name}
</Dropdown.Item>
))}
</Dropdown.Menu>
</Dropdown>
</h1>

<HeaderDeviceSelector devices={devices} dev={dev} tab={tab} t={t} />
<div className="tab">
<ul className="nav nav-tabs">
{links.map((link) => (
Expand Down
11 changes: 11 additions & 0 deletions src/components/device-page/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
export type TabName =
| 'info'
| 'bind'
| 'state'
| 'exposes'
| 'clusters'
| 'reporting'
| 'settings'
| 'settings-specific'
| 'dev-console'
| 'scene';
3 changes: 2 additions & 1 deletion src/i18n/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,8 @@
"scene": "Scene",
"unknown_device": "Unknown device",
"exposes": "Exposes",
"select_a_device": "Select a device"
"select_a_device": "Select a device",
"type_to_filter": "Type to filter..."
},
"exposes": {
"empty_exposes_definition": "Empty exposes definition"
Expand Down

0 comments on commit 43c93f0

Please sign in to comment.