mirror of
https://github.com/cpvalente/ontime.git
synced 2026-09-07 23:39:09 +00:00
feat(settings): add searchable navigation
This commit is contained in:
committed by
Carlos Valente
parent
bdd815678b
commit
9e42f18299
@@ -1,10 +1,18 @@
|
||||
import { Fragment } from 'react';
|
||||
import { Fragment, useState } from 'react';
|
||||
|
||||
import Tooltip from '../../../common/components/tooltip/Tooltip';
|
||||
import { isKeyEnter } from '../../../common/utils/keyEvent';
|
||||
import { cx } from '../../../common/utils/styleUtils';
|
||||
import { SettingsOption, SettingsOptionId, useAppSettingsMenu } from '../useAppSettingsMenu';
|
||||
import * as Panel from '../panel-utils/PanelUtils';
|
||||
import {
|
||||
filterSettingsOptions,
|
||||
matchesSettingsOptionQuery,
|
||||
SettingsOption,
|
||||
SettingsOptionId,
|
||||
useAppSettingsMenu,
|
||||
} from '../useAppSettingsMenu';
|
||||
import useAppSettingsNavigation from '../useAppSettingsNavigation';
|
||||
import SettingsSearch from './SettingsSearch';
|
||||
|
||||
import style from './PanelList.module.scss';
|
||||
|
||||
@@ -16,23 +24,57 @@ interface PanelListProps extends PanelBaseProps {
|
||||
selectedPanel: string;
|
||||
}
|
||||
|
||||
/** Returns the first matching setting, preferring a matching child over its non-matching group. */
|
||||
function getFirstResultId(results: SettingsOption[], query: string): SettingsOptionId | null {
|
||||
const firstResult = results[0];
|
||||
if (!firstResult) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (matchesSettingsOptionQuery(firstResult, query.trim().toLowerCase())) {
|
||||
return firstResult.id as SettingsOptionId;
|
||||
}
|
||||
|
||||
return (firstResult.secondary?.[0]?.id as SettingsOptionId | undefined) ?? null;
|
||||
}
|
||||
|
||||
export default function PanelList({ selectedPanel, location }: PanelListProps) {
|
||||
const { options } = useAppSettingsMenu();
|
||||
const { setLocation } = useAppSettingsNavigation();
|
||||
const [query, setQuery] = useState('');
|
||||
|
||||
const results = filterSettingsOptions(options, query);
|
||||
|
||||
const handleSearchSubmit = () => {
|
||||
const target = getFirstResultId(results, query);
|
||||
if (target) {
|
||||
setLocation(target);
|
||||
setQuery('');
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<ul className={style.tabs}>
|
||||
{options.map((panel) => {
|
||||
const isSelected = selectedPanel === panel.id;
|
||||
if (panel.highlight) {
|
||||
return (
|
||||
<Tooltip key={panel.id} text={panel.highlight} render={<span />}>
|
||||
<PanelListItem panel={panel} location={location} isSelected={isSelected} />
|
||||
</Tooltip>
|
||||
);
|
||||
}
|
||||
return <PanelListItem key={panel.id} panel={panel} location={location} isSelected={isSelected} />;
|
||||
})}
|
||||
</ul>
|
||||
<div className={style.container}>
|
||||
<SettingsSearch query={query} onQueryChange={setQuery} onSubmit={handleSearchSubmit} />
|
||||
|
||||
{results.length === 0 ? (
|
||||
<Panel.EmptyState title='No settings match' description={`Nothing found for "${query.trim()}"`} />
|
||||
) : (
|
||||
<ul className={style.tabs}>
|
||||
{results.map((panel) => {
|
||||
const isSelected = selectedPanel === panel.id;
|
||||
if (panel.highlight) {
|
||||
return (
|
||||
<Tooltip key={panel.id} text={panel.highlight} render={<span />}>
|
||||
<PanelListItem panel={panel} location={location} isSelected={isSelected} />
|
||||
</Tooltip>
|
||||
);
|
||||
}
|
||||
return <PanelListItem key={panel.id} panel={panel} location={location} isSelected={isSelected} />;
|
||||
})}
|
||||
</ul>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -44,7 +86,15 @@ interface PanelListItemProps {
|
||||
|
||||
function PanelListItem({ panel, isSelected, location }: PanelListItemProps) {
|
||||
const { setLocation } = useAppSettingsNavigation();
|
||||
const classes = cx([style.primary, isSelected && style.active, panel.highlight && style.highlight]);
|
||||
const hasSelectedChild = Boolean(
|
||||
isSelected && panel.secondary?.some((secondary) => secondary.id.split('__')[1] === location),
|
||||
);
|
||||
const classes = cx([
|
||||
style.primary,
|
||||
isSelected && !hasSelectedChild && style.active,
|
||||
hasSelectedChild && style.groupActive,
|
||||
panel.highlight && style.highlight,
|
||||
]);
|
||||
|
||||
return (
|
||||
<Fragment key={panel.id}>
|
||||
@@ -75,6 +125,7 @@ function PanelListItem({ panel, isSelected, location }: PanelListItemProps) {
|
||||
}
|
||||
}}
|
||||
className={secondaryClasses}
|
||||
tabIndex={0}
|
||||
role='button'
|
||||
>
|
||||
{secondary.label}
|
||||
|
||||
Reference in New Issue
Block a user