Skip to content

Commit

Permalink
[Feat] Add list toggle to filters (#2115)
Browse files Browse the repository at this point in the history
- create a AddByDatasetButton
- Display flter by list or dataset
- Add filter button shows typeahead and dataset selection
- Move add buttons to right of title to filter / base map panel

Signed-off-by: Ihor Dykhta <dikhta.igor@gmail.com>
Co-authored-by: Shan He <heshan0131@gmail.com>
  • Loading branch information
igorDykhta and heshan0131 committed Feb 15, 2023
1 parent 20fcb66 commit fb82992
Show file tree
Hide file tree
Showing 36 changed files with 901 additions and 534 deletions.
12 changes: 6 additions & 6 deletions examples/demo-app/src/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -179,14 +179,14 @@ class App extends Component {
}

_loadSampleData() {
// this._loadPointData();
// this._loadGeojsonData();
this._loadTripGeoJson();
this._loadPointData();
this._loadGeojsonData();
// this._loadTripGeoJson();
// this._loadIconData();
// this._loadH3HexagonData();
// this._loadS2Data();
// this._loadScenegraphLayer();
this._loadGpsData();
// this._loadGpsData();
}

_loadPointData() {
Expand All @@ -200,8 +200,8 @@ class App extends Component {
data: sampleTripData
},
options: {
centerMap: true,
readOnly: false
// centerMap: true,
keepExistingConfig: true
},
config: sampleTripDataConfig
})
Expand Down
2 changes: 1 addition & 1 deletion src/actions/src/action-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@ const ActionTypes = {
ADD_NOTIFICATION: `${ACTION_PREFIX}ADD_NOTIFICATION`,
REMOVE_NOTIFICATION: `${ACTION_PREFIX}REMOVE_NOTIFICATION`,
SET_LOCALE: `${ACTION_PREFIX}SET_LOCALE`,
TOGGLE_LAYER_PANEL_LIST_VIEW: `${ACTION_PREFIX}TOGGLE_LAYER_PANEL_LIST_VIEW`,
TOGGLE_PANEL_LIST_VIEW: `${ACTION_PREFIX}TOGGLE_PANEL_LIST_VIEW`,

// uiState > export image
SET_EXPORT_IMAGE_SETTING: `${ACTION_PREFIX}SET_EXPORT_IMAGE_SETTING`,
Expand Down
26 changes: 16 additions & 10 deletions src/actions/src/ui-state-actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -453,24 +453,30 @@ export const setLocale: (
})
);

/** TOGGLE_LAYER_PANEL_LIST_VIEW */
export type ToggleLayerPanelListViewAction = {
payload: string;
/** TOGGLE_PANEL_LIST_VIEW */
export type TogglePanelListViewAction = {
payload: {
panelId: string;
listView: string;
};
};

/**
* Toggle layer panel list view
* @memberof uiStateActions
* @param listView layer panel listView value. Can be 'list' or 'sortByDataset'
* @param payload
* @param payload.panelId panel id.
* @param payload.listView layer panel listView value. Can be 'list' or 'sortByDataset'
* @public
*/
export const toggleLayerPanelListView: (
listView: ToggleLayerPanelListViewAction['payload']
export const togglePanelListView: (
payload: TogglePanelListViewAction['payload']
) => Merge<
ToggleLayerPanelListViewAction,
{type: typeof ActionTypes.TOGGLE_LAYER_PANEL_LIST_VIEW}
TogglePanelListViewAction,
{type: typeof ActionTypes.TOGGLE_PANEL_LIST_VIEW}
> = createAction(
ActionTypes.TOGGLE_LAYER_PANEL_LIST_VIEW,
(listView: ToggleLayerPanelListViewAction['payload']) => ({payload: listView})
ActionTypes.TOGGLE_PANEL_LIST_VIEW,
(payload: TogglePanelListViewAction['payload']) => ({payload})
);

/**
Expand Down
14 changes: 13 additions & 1 deletion src/components/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ export {
} from './side-panel/layer-panel/dataset-section';
export {default as DatasetLayerSectionFactory} from './side-panel/layer-panel/dataset-layer-section';
export {default as DatasetLayerGroupFactory} from './side-panel/layer-panel/dataset-layer-group';
export {default as PanelViewListToggleFactory} from './side-panel/layer-panel/panel-view-list-toggle';
export {default as PanelViewListToggleFactory} from './side-panel/panel-view-list-toggle';
export {default as AddLayerButtonFactory} from './side-panel/layer-panel/add-layer-button';
export {default as LayerListFactory} from './side-panel/layer-panel/layer-list';
export {default as CustomPicker} from './side-panel/layer-panel/custom-picker';
Expand All @@ -101,6 +101,7 @@ export {default as DatasetTagFactory} from './side-panel/common/dataset-tag';

export {default as FilterManagerFactory} from './side-panel/filter-manager';
export {default as FilterPanelFactory} from './side-panel/filter-panel/filter-panel';
export {default as AddFilterButtonFactory} from './side-panel/filter-panel/add-filter-button';

export {default as InteractionManagerFactory} from './side-panel/interaction-manager';
export {default as BrushConfigFactory} from './side-panel/interaction-panel/brush-config';
Expand Down Expand Up @@ -249,6 +250,17 @@ export type {
StyledExportSectionProps
} from './common';

export type {CollapseButtonProps} from './side-panel/side-bar';
export type {PanelTabProps} from './side-panel/panel-tab';
export type {MapStyleSelectorProps} from './side-panel/map-style-panel/map-style-selector';
export type {LayerGroupSelectorProps} from './side-panel/map-style-panel/map-layer-selector';
export type {MapManagerProps} from './side-panel/map-manager';
export type {SourceDataSelectorProps} from './side-panel/common/types';
export type {DatasetTitleProps} from './side-panel/common/dataset-title';
export type {SourceDataCatalogProps} from './side-panel/common/source-data-catalog';
export type {DatasetInfoProps} from './side-panel/common/dataset-info';
export type {DatasetTagProps} from './side-panel/common/dataset-tag';
export type {CustomPanelsProps} from './side-panel/custom-panel';
export type {LayerTypeListItemType} from './side-panel/layer-panel/layer-type-list-item';

export {
Expand Down
23 changes: 12 additions & 11 deletions src/components/src/side-panel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@
// THE SOFTWARE.

import React, {useCallback, useMemo} from 'react';
import {FormattedMessage} from '@kepler.gl/localization';

import {
EXPORT_DATA_ID,
Expand All @@ -43,7 +42,6 @@ import FilterManagerFactory from './side-panel/filter-manager';
import InteractionManagerFactory from './side-panel/interaction-manager';
import MapManagerFactory from './side-panel/map-manager';
import CustomPanelsFactory from './side-panel/custom-panel';
import PanelTitleFactory from './side-panel/panel-title';

import styled from 'styled-components';
import get from 'lodash.get';
Expand All @@ -67,7 +65,6 @@ SidePanelFactory.deps = [
SidebarFactory,
PanelHeaderFactory,
PanelToggleFactory,
PanelTitleFactory,
LayerManagerFactory,
FilterManagerFactory,
InteractionManagerFactory,
Expand All @@ -82,7 +79,6 @@ export default function SidePanelFactory(
Sidebar: ReturnType<typeof SidebarFactory>,
PanelHeader: ReturnType<typeof PanelHeaderFactory>,
PanelToggle: ReturnType<typeof PanelToggleFactory>,
PanelTitle: ReturnType<typeof PanelTitleFactory>,
LayerManager: ReturnType<typeof LayerManagerFactory>,
FilterManager: ReturnType<typeof FilterManagerFactory>,
InteractionManager: ReturnType<typeof InteractionManagerFactory>,
Expand Down Expand Up @@ -223,11 +219,6 @@ export default function SidePanelFactory(
/>
<StyledSidePanelContent className="side-panel__content">
<div className="side-panel__content__inner">
{currentPanel?.id !== 'layer' ? (
<PanelTitle className="side-panel__content__title">
<FormattedMessage id={currentPanel?.label} />
</PanelTitle>
) : null}
{PanelComponent ? (
<PanelComponent
datasets={datasets}
Expand All @@ -249,10 +240,20 @@ export default function SidePanelFactory(
uiStateActions={uiStateActions}
visStateActions={visStateActions}
panelMetadata={currentPanel}
layerPanelListView={currentPanel?.id === 'layer' && uiState.layerPanelListView}
panelListView={
currentPanel?.id === 'layer'
? uiState.layerPanelListView
: currentPanel?.id === 'filter'
? uiState.filterPanelListView
: null
}
/>
) : null}
<CustomPanels {...customPanelProps} activeSidePanel={activeSidePanel} />
<CustomPanels
{...customPanelProps}
activeSidePanel={activeSidePanel}
updateTableColor={onUpdateTableColor}
/>
</div>
</StyledSidePanelContent>
</Sidebar>
Expand Down
200 changes: 200 additions & 0 deletions src/components/src/side-panel/add-by-dataset-button.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,200 @@
// Copyright (c) 2023 Uber Technologies, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

import React, {useCallback, useMemo, useState} from 'react';
import styled from 'styled-components';
import {FormattedMessage} from '@kepler.gl/localization';
import {Datasets} from '@kepler.gl/table';

import Tippy from '@tippyjs/react';
import {Add} from '../common/icons';
import {Button} from '../common/styled-components';
import {DatasetSquare} from '../..';
import Typeahead from '../common/item-selector/typeahead';
import Accessor from '../common/item-selector/accessor';
import {useIntl} from 'react-intl';

const DropdownContainer = styled.div.attrs({
className: 'add-layer-menu-dropdown'
})`
.list-selector {
border-top: 1px solid ${props => props.theme.secondaryInputBorderColor};
width: 100%;
/* disable scrolling, currently set to 280px internally */
max-height: unset;
}
.list__item > div {
display: flex;
flex-direction: row;
justify-content: flex-start;
line-height: 18px;
padding: 0;
svg {
margin-right: 10px;
}
}
`;

const DropdownMenu = styled.div.attrs({
className: 'dropdown-menu'
})`
display: flex;
flex-direction: column;
min-width: 240px;
max-width: 240px;
position: absolute;
top: 100%;
left: -53px;
z-index: 5;
`;

const ListItemWrapper = styled.div.attrs({
className: 'dropdown-menu-list-item-wrapper'
})`
display: flex;
color: ${props => props.theme.textColor};
font-size: 11px;
letter-spacing: 0.2px;
overflow: auto;
.dataset-color {
flex-shrink: 0;
margin-top: 3px;
}
.dataset-name {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
`;

const TYPEAHEAD_CLASS = 'typeahead';
const TYPEAHEAD_INPUT_CLASS = 'typeahead__input';

export type AddByDatasetButtonProps = {
datasets: Datasets;
onAdd: (dataId: string) => void;
width: string;
buttonIntlId: string;
inactive?: boolean;
className?: string;
};

const ListItem = ({value}) => (
<ListItemWrapper>
<DatasetSquare className="dataset-color" backgroundColor={value.color} />
<div className="dataset-name" title={value.label}>
{value.label}
</div>
</ListItemWrapper>
);

const AddByDatasetButton: React.FC<AddByDatasetButtonProps> = ({
datasets,
onAdd,
buttonIntlId,
width,
className,
inactive
}) => {
const [tippyInstance, setTippyInstance] = useState();

const options = useMemo(() => {
return Object.values(datasets).map(ds => ({
label: ds.label,
value: ds.id,
color: ds.color
}));
}, [datasets]);

const onClickBtn = useCallback(() => {
if (options.length === 1) {
onAdd(options[0].value);
}

return;
}, [options, onAdd]);

const onOptionSelected = useCallback(
option => {
onAdd(option.value);
if (tippyInstance) {
// @ts-ignore
tippyInstance.hide();
}
},
[onAdd, tippyInstance]
);

const intl = useIntl();

const buttonRendered = (
<Button
tabIndex={-1}
className={className || 'add-by-dataset-button'}
width={width}
onClick={onClickBtn}
disabled={!options.length || inactive}
>
<Add height="12px" />
<FormattedMessage id={buttonIntlId} />
</Button>
);

return options.length === 1 ? (
buttonRendered
) : (
<Tippy
trigger="click"
arrow={false}
interactive
placement="bottom"
appendTo="parent"
// @ts-ignore
onCreate={setTippyInstance}
duration={0}
content={
<DropdownMenu>
<DropdownContainer>
<Typeahead
className={TYPEAHEAD_CLASS}
customClasses={{
results: 'list-selector',
input: TYPEAHEAD_INPUT_CLASS,
listItem: 'list__item'
}}
placeholder={intl ? intl.formatMessage({id: 'placeholder.search'}) : 'Search'}
selectedItems={null}
options={options}
displayOption={Accessor.generateOptionToStringFor('label')}
filterOption={'label'}
searchable
onOptionSelected={onOptionSelected}
customListItemComponent={ListItem}
/>
</DropdownContainer>
</DropdownMenu>
}
>
{buttonRendered}
</Tippy>
);
};

export default AddByDatasetButton;

0 comments on commit fb82992

Please sign in to comment.