Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/features/pagination/ResultCount.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ import useFilteredObjects from '@features/transforms/filtering/useFilteredObject
const ResultCount: React.FC = () => {
const { filteredObjects } = useFilteredObjects({});
return (
<span style={{ fontSize: '0.9em', color: 'var(--color-text)', whiteSpace: 'nowrap' }}>
{filteredObjects.length} Results
<span style={{ color: 'var(--color-text)', whiteSpace: 'nowrap' }}>
{filteredObjects.length.toLocaleString()} Results{' '}
</span>
);
};
Expand Down
6 changes: 6 additions & 0 deletions src/features/params/ui/Selector.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ type Props<T> = {
getOptionLabel?: (value: T) => React.ReactNode;
labelWhenEmpty?: string; // for multi-select options
onChange: (value: T) => void;
optionStyle?: React.CSSProperties;
options: readonly T[];
selected: T | T[];
selectorDescription?: ReactNode;
Expand All @@ -31,6 +32,7 @@ function Selector<T extends React.Key>({
getOptionLabel = (val) => val as string,
labelWhenEmpty,
onChange,
optionStyle,
options,
selected,
selectorDescription,
Expand Down Expand Up @@ -58,6 +60,7 @@ function Selector<T extends React.Key>({
setExpanded(false);
onChange(option);
}}
optionStyle={optionStyle}
options={options}
selected={selected}
/>
Expand Down Expand Up @@ -155,6 +158,7 @@ type OptionsProps<T> = {
getOptionDescription?: (value: T) => React.ReactNode;
getOptionLabel?: (value: T) => React.ReactNode; // optional label renderer
onClick: (value: T) => void;
optionStyle?: React.CSSProperties;
options: readonly T[];
selected: T | T[];
};
Expand All @@ -163,6 +167,7 @@ function Options<T extends React.Key>({
getOptionDescription,
getOptionLabel,
onClick,
optionStyle,
options,
selected,
}: OptionsProps<T>) {
Expand All @@ -173,6 +178,7 @@ function Options<T extends React.Key>({
getOptionLabel={getOptionLabel}
onClick={onClick}
option={option}
optionStyle={optionStyle}
isSelected={Array.isArray(selected) ? selected.includes(option) : selected === option}
position={getPositionInGroup(i, options.length)}
/>
Expand Down
4 changes: 3 additions & 1 deletion src/features/params/ui/SelectorOption.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ type OptionProps<T> = {
labelWhenEmpty?: string; // for multi-select options
onClick: (value: T) => void;
option: T | T[];
optionStyle?: React.CSSProperties;
position?: PositionInGroup; // used for styling
};

Expand All @@ -25,6 +26,7 @@ function SelectorOption<T extends React.Key>({
labelWhenEmpty,
onClick,
option,
optionStyle,
position = PositionInGroup.Standalone,
}: OptionProps<T>) {
let className = 'selectorOption';
Expand All @@ -44,7 +46,7 @@ function SelectorOption<T extends React.Key>({
}
onClick={() => onClick(Array.isArray(option) ? option[0] : option)}
role="option"
style={getOptionStyle(display, isSelected, position)}
style={{ ...getOptionStyle(display, isSelected, position), ...optionStyle }}
>
<OptionLabel<T>
option={option}
Expand Down
2 changes: 0 additions & 2 deletions src/features/transforms/filtering/FilterPath.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -200,15 +200,13 @@ const FilterPath: React.FC = () => {
if (filters.filter((f) => f).length === 0) {
return (
<>
<SlashIcon size="1em" />
<Deemphasized>No filters applied</Deemphasized>
</>
);
Comment on lines 201 to 205
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can remove the <></> syntax since its only one component

}

return (
<>
<SlashIcon size="1em" />
Comment on lines 208 to -211
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here too

{filters
.filter((f) => f)
.map((filter, i) => (
Expand Down
7 changes: 4 additions & 3 deletions src/features/transforms/sorting/SortBySelector.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { ArrowDownUpIcon } from 'lucide-react';
import React from 'react';

import Selector from '@features/params/ui/Selector';
Expand All @@ -7,14 +8,14 @@ import { getApplicableFields } from '@features/transforms/fields/FieldApplicabil

import TransformEnum from '../TransformEnum';

const SortBySelector: React.FC = () => {
const SortBySelector: React.FC<{ showLabel?: boolean }> = ({ showLabel = true }) => {
const { sortBy, updatePageParams, objectType } = usePageParams();
const applicableSortBys = getApplicableFields(TransformEnum.Sort, objectType);

return (
<Selector
selectorLabel="Sort By"
selectorDescription="Choose the order of items in the view."
selectorLabel={showLabel ? 'Sort By' : <ArrowDownUpIcon size="1.2em" />}
selectorDescription={showLabel ? 'Choose the order of items in the view.' : undefined}
options={applicableSortBys}
onChange={(sortBy) => updatePageParams({ sortBy })}
selected={sortBy}
Expand Down
48 changes: 27 additions & 21 deletions src/pages/DataPageBody.tsx
Original file line number Diff line number Diff line change
@@ -1,47 +1,53 @@
import { SlidersHorizontalIcon } from 'lucide-react';
import React, { Suspense } from 'react';

import useFilterPanel from '@widgets/controls/useFilterPanel';
import FilterPanelToggle from '@widgets/controls/FilterPanelToggle';
import ViewSelector from '@widgets/controls/selectors/ViewSelector';
import Loading from '@widgets/Loading';
import PathNav from '@widgets/pathnav/PathNav';
import { PathContainer } from '@widgets/pathnav/PathNav';

import HoverableButton from '@features/layers/hovercard/HoverableButton';
import ResultCount from '@features/pagination/ResultCount';
import FilterPath from '@features/transforms/filtering/FilterPath';
import SearchBar from '@features/transforms/search/SearchBar';
import SortBySelector from '@features/transforms/sorting/SortBySelector';

import EntityTypeTabs from './dataviews/EntityTypeTabs';

const DataViews = React.lazy(() => import('./dataviews/DataViews'));

const DataPageBody: React.FC = () => {
const { isOpen, setIsOpen } = useFilterPanel();

return (
<main style={{ padding: '1em', flex: 1, overflow: 'auto', width: '100%' }}>
<div style={{ display: 'flex', alignItems: 'center', flexDirection: 'column' }}>
<SearchBar />
<EntityTypeTabs />
</div>

<div
style={{
display: 'flex',
alignItems: 'center',
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The alignment doesn't line up between the right and left sides.

justifyContent: 'space-between',
width: '100%',
marginBottom: '1rem',
}}
>
<div style={{ display: 'flex', alignItems: 'center', gap: '0.5em' }}>
<FilterPanelToggle />
<ResultCount />
<PathContainer>
<FilterPath />
</PathContainer>
</div>
<div
style={{
display: 'flex',
alignItems: 'center',
justifyContent: 'space-between',
width: '100%',
justifyContent: 'flex-end',
gap: '0.5rem',
}}
>
<div style={{ display: 'flex', alignItems: 'center', gap: '0.5em' }}>
<HoverableButton
className={isOpen ? 'selected primary' : 'primary'}
hoverContent={isOpen ? 'Close filters panel' : 'Open filters panel'}
onClick={() => setIsOpen((open) => !open)}
style={{ padding: '0.4em', borderRadius: '0.5em', display: 'flex' }}
aria-label={isOpen ? 'Close filters panel' : 'Open filters panel'}
>
<SlidersHorizontalIcon size="1.2em" />
</HoverableButton>
<ResultCount />
</div>
<PathNav />
<SortBySelector showLabel={false} />
<ViewSelector />
</div>
</div>
<div
Expand Down
24 changes: 24 additions & 0 deletions src/widgets/controls/FilterPanelToggle.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { SlidersHorizontalIcon } from 'lucide-react';

import HoverableButton from '@features/layers/hovercard/HoverableButton';

import useFilterPanel from './useFilterPanel';

const FilterPanelToggle: React.FC = () => {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, this is a good idea. I'm a bit confused why it wasn't part of your previously merged commit -- But I guess you merged before you could get it in.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, it got left out before the merge. Added now.

const { isOpen, setIsOpen } = useFilterPanel();
const label = isOpen ? 'Close filters panel' : 'Open filters panel';

return (
<HoverableButton
className={isOpen ? 'selected primary' : 'primary'}
hoverContent={label}
onClick={() => setIsOpen((open) => !open)}
style={{ padding: '0.5em', display: 'flex' }}
aria-label={label}
>
<SlidersHorizontalIcon size="1.2em" />
</HoverableButton>
);
};

export default FilterPanelToggle;
53 changes: 40 additions & 13 deletions src/widgets/controls/selectors/ViewSelector.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { ChartColumnBigIcon, Grid2x2Icon, ListTreeIcon, MapIcon, Table2Icon } from 'lucide-react';
import React from 'react';

import { View } from '@features/params/PageParamTypes';
Expand All @@ -10,25 +11,36 @@ const ViewSelector: React.FC = () => {

return (
<Selector
selectorLabel="Display"
getOptionDescription={(option) => <img src={getImageSrc(option)} width={180} />}
options={Object.values(View)}
onChange={(view: View) => updatePageParams({ view })}
display={SelectorDisplay.ButtonList}
getOptionLabel={(view) =>
[View.Map, View.Reports].includes(view) ? (
<>
{view} <em>β</em>
</>
) : (
view
)
}
selected={view}
onChange={(nextView: View) => updatePageParams({ view: nextView })}
getOptionLabel={(option) => getViewIcon(option)}
getOptionDescription={(option) => getViewLabel(option)}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see you removed the screenshots from the mouseover -- that was intentional because you want new screenshots?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have added Screenshots back in the mouseover descriptions along with the view label.

display={SelectorDisplay.ButtonGroup}
optionStyle={{
width: 'fit-content',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
padding: '0.5rem',
}}
selectorStyle={{ gap: '0.25rem' }}
Comment on lines +20 to +27
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TODO for me: check if we need all of these styles, I'm not certain (ideally it would come from SelectorDisplay so it scales better for more usages).

Nonetheless we don't need to get ahead of ourselves and scale things that we are only using in one place right now.

/>
);
};

function getViewLabel(view: View): React.ReactNode {
const isBeta = [View.Map, View.Reports].includes(view);
return (
<div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', gap: '0.25rem' }}>
<span style={{ display: 'flex', gap: '0.125rem' }}>
{view} {isBeta && <em>β</em>}
</span>
<img src={getImageSrc(view)} width={180} />
</div>
);
}

function getImageSrc(view: View): string {
switch (view) {
case View.CardList:
Expand All @@ -44,4 +56,19 @@ function getImageSrc(view: View): string {
}
}

function getViewIcon(view: View): React.ReactNode {
switch (view) {
case View.CardList:
return <Grid2x2Icon size="1.2em" />;
case View.Hierarchy:
return <ListTreeIcon size="1.2em" />;
case View.Map:
return <MapIcon size="1.2em" />;
case View.Table:
return <Table2Icon size="1.2em" />;
case View.Reports:
return <ChartColumnBigIcon size="1.2em" />;
}
}

export default ViewSelector;
38 changes: 0 additions & 38 deletions src/widgets/pathnav/PathNav.tsx
Original file line number Diff line number Diff line change
@@ -1,45 +1,9 @@
import { SlashIcon } from 'lucide-react';
import React from 'react';

import { View } from '@features/params/PageParamTypes';
import Selector from '@features/params/ui/Selector';
import {
SelectorDisplay,
SelectorDisplayProvider,
} from '@features/params/ui/SelectorDisplayContext';
import usePageParams from '@features/params/usePageParams';
import FilterPath from '@features/transforms/filtering/FilterPath';

const PathNav: React.FC = () => {
return (
<PathContainer>
<SlashIcon size="1em" />
<ViewSelector />
<FilterPath />
</PathContainer>
);
};

const ViewSelector: React.FC = () => {
const { view, updatePageParams } = usePageParams();

return (
<Selector
options={Object.values(View)}
onChange={(view: View) => updatePageParams({ view })}
selected={view}
getOptionLabel={(view) =>
[View.Map, View.Reports].includes(view) ? (
<>
{view} <em>β</em>
</>
) : (
view
)
}
/>
);
};

export const PathContainer: React.FC<{
children: React.ReactNode;
Expand All @@ -55,5 +19,3 @@ export const PathContainer: React.FC<{
</div>
);
};

export default PathNav;
Loading