ui: split up search component (1.22+ tbd?), restrict filter type to own modals
This commit is contained in:
parent
614c0655c2
commit
ca6a5e71e9
7 changed files with 155 additions and 123 deletions
|
|
@ -1,5 +1,5 @@
|
|||
import React, { useEffect, useState } from 'react';
|
||||
import FilterList from 'Shared/Filters/FilterList';
|
||||
import { EventsList } from 'Shared/Filters/FilterList';
|
||||
import SeriesName from './SeriesName';
|
||||
import cn from 'classnames';
|
||||
import { observer } from 'mobx-react-lite';
|
||||
|
|
@ -202,7 +202,7 @@ function FilterSeries(props: Props) {
|
|||
)}
|
||||
|
||||
{expanded ? (
|
||||
<FilterList
|
||||
<EventsList
|
||||
filter={series.filter}
|
||||
onUpdateFilter={onUpdateFilter}
|
||||
onRemoveFilter={onRemoveFilter}
|
||||
|
|
|
|||
|
|
@ -1,9 +1,8 @@
|
|||
import { GripHorizontal } from 'lucide-react';
|
||||
import { GripHorizontal, Plus, Filter } from 'lucide-react';
|
||||
import { observer } from 'mobx-react-lite';
|
||||
import React, { useEffect } from 'react';
|
||||
import { Button } from 'antd';
|
||||
import { Icon } from 'UI';
|
||||
|
||||
import FilterItem from '../FilterItem';
|
||||
import EventsOrder from 'Shared/Filters/FilterList/EventsOrder';
|
||||
import FilterSelection from '../FilterSelection/FilterSelection';
|
||||
|
|
@ -24,9 +23,71 @@ interface Props {
|
|||
actions?: React.ReactNode[];
|
||||
onlyFilters?: boolean;
|
||||
onAddFilter: (filter: any) => void;
|
||||
mergeDown?: boolean;
|
||||
mergeUp?: boolean;
|
||||
}
|
||||
|
||||
function FilterList(props: Props) {
|
||||
export const FilterList = observer((props: Props) => {
|
||||
const {
|
||||
observeChanges = () => {},
|
||||
filter,
|
||||
excludeFilterKeys = [],
|
||||
isConditional,
|
||||
onAddFilter,
|
||||
} = props;
|
||||
|
||||
const filters = filter.filters;
|
||||
useEffect(observeChanges, [filters]);
|
||||
|
||||
const onRemoveFilter = (filterIndex: any) => {
|
||||
props.onRemoveFilter(filterIndex);
|
||||
};
|
||||
return (
|
||||
<div
|
||||
className={'py-2 px-4 widget-wrapper'}
|
||||
style={{
|
||||
borderBottomLeftRadius: props.mergeDown ? 0 : 'unset',
|
||||
borderBottomRightRadius: props.mergeDown ? 0 : 'unset',
|
||||
borderTopLeftRadius: props.mergeUp ? 0 : 'unset',
|
||||
borderTopRightRadius: props.mergeUp ? 0 : 'unset',
|
||||
}}
|
||||
>
|
||||
<div className={'flex items-center gap-2 mb-2'}>
|
||||
<div className="font-semibold">Filters</div>
|
||||
<FilterSelection mode={'filters'} filter={undefined} onFilterClick={onAddFilter}>
|
||||
<Button icon={<Filter size={16} strokeWidth={1} />} type="default" size={'small'}>
|
||||
Add
|
||||
</Button>
|
||||
</FilterSelection>
|
||||
</div>
|
||||
{filters.map((filter: any, filterIndex: any) =>
|
||||
!filter.isEvent ? (
|
||||
<div
|
||||
className={'py-2 hover:bg-active-blue px-5'}
|
||||
style={{
|
||||
marginLeft: '-1.25rem',
|
||||
width: 'calc(100% + 2.5rem)',
|
||||
}}
|
||||
>
|
||||
<FilterItem
|
||||
key={filterIndex}
|
||||
readonly={props.readonly}
|
||||
isFilter={true}
|
||||
filterIndex={filterIndex}
|
||||
filter={filter}
|
||||
onUpdate={(filter) => props.onUpdateFilter(filterIndex, filter)}
|
||||
onRemoveFilter={() => onRemoveFilter(filterIndex)}
|
||||
excludeFilterKeys={excludeFilterKeys}
|
||||
isConditional={isConditional}
|
||||
/>
|
||||
</div>
|
||||
) : null
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
||||
export const EventsList = observer((props: Props) => {
|
||||
const {
|
||||
observeChanges = () => {},
|
||||
filter,
|
||||
|
|
@ -37,7 +98,6 @@ function FilterList(props: Props) {
|
|||
isConditional,
|
||||
actions = [],
|
||||
onAddFilter,
|
||||
onlyFilters,
|
||||
} = props;
|
||||
|
||||
const filters = filter.filters;
|
||||
|
|
@ -113,125 +173,81 @@ function FilterList(props: Props) {
|
|||
|
||||
const eventsNum = filters.filter((i: any) => i.isEvent).length;
|
||||
return (
|
||||
<div className="widget-wrapper flex flex-col">
|
||||
{onlyFilters ? null : (<div className={'border-b border-b-gray-lighter py-2 px-4'}>
|
||||
<div className="flex items-center mb-2 gap-2">
|
||||
<div className="font-semibold">
|
||||
{filter.eventsHeader || 'Events'}
|
||||
</div>
|
||||
<FilterSelection filter={undefined} onFilterClick={onAddFilter}>
|
||||
<Button
|
||||
icon={<Icon name={'filter'} />}
|
||||
type="default"
|
||||
size={'small'}
|
||||
>
|
||||
Add
|
||||
</Button>
|
||||
</FilterSelection>
|
||||
<div
|
||||
className={'widget-wrapper border-b border-b-gray-lighter py-2 px-4'}
|
||||
style={{
|
||||
borderBottomLeftRadius: props.mergeDown ? 0 : 'unset',
|
||||
borderBottomRightRadius: props.mergeDown ? 0 : 'unset',
|
||||
borderTopLeftRadius: props.mergeUp ? 0 : 'unset',
|
||||
borderTopRightRadius: props.mergeUp ? 0 : 'unset',
|
||||
marginBottom: props.mergeDown ? '-1px' : undefined,
|
||||
}}
|
||||
>
|
||||
<div className="flex items-center mb-2 gap-2">
|
||||
<div className="font-semibold">{filter.eventsHeader || 'Events'}</div>
|
||||
<FilterSelection mode={'events'} filter={undefined} onFilterClick={onAddFilter}>
|
||||
<Button icon={<Plus size={16} strokeWidth={1} />} type="default" size={'small'}>
|
||||
Add
|
||||
</Button>
|
||||
</FilterSelection>
|
||||
|
||||
<div className={'ml-auto'}>
|
||||
{!hideEventsOrder && (
|
||||
<EventsOrder
|
||||
filter={filter}
|
||||
onChange={props.onChangeEventsOrder}
|
||||
/>
|
||||
)}
|
||||
{actions &&
|
||||
actions.map((action, index) => <div key={index}>{action}</div>)}
|
||||
</div>
|
||||
</div>
|
||||
<div className={'flex flex-col'}>
|
||||
{filters.map((filter: any, filterIndex: number) =>
|
||||
filter.isEvent ? (
|
||||
<div
|
||||
style={{
|
||||
pointerEvents: 'unset',
|
||||
paddingTop:
|
||||
hoveredItem.i === filterIndex &&
|
||||
hoveredItem.position === 'top'
|
||||
? '1.5rem'
|
||||
: '0.5rem',
|
||||
paddingBottom:
|
||||
hoveredItem.i === filterIndex &&
|
||||
hoveredItem.position === 'bottom'
|
||||
? '1.5rem'
|
||||
: '0.5rem',
|
||||
marginLeft: '-1.25rem',
|
||||
width: 'calc(100% + 2.5rem)',
|
||||
}}
|
||||
className={
|
||||
'hover:bg-active-blue px-5 gap-2 items-center flex'
|
||||
}
|
||||
id={`${filter.key}-${filterIndex}`}
|
||||
onDragOver={(e) => handleDragOverEv(e, filterIndex)}
|
||||
onDrop={(e) => handleDrop(e)}
|
||||
key={`${filter.key}-${filterIndex}`}
|
||||
>
|
||||
{!!props.onFilterMove && eventsNum > 1 ? (
|
||||
<div
|
||||
className={'p-2 cursor-grab'}
|
||||
draggable={!!props.onFilterMove}
|
||||
onDragStart={(e) =>
|
||||
handleDragStart(
|
||||
e,
|
||||
filterIndex,
|
||||
`${filter.key}-${filterIndex}`
|
||||
)
|
||||
}
|
||||
>
|
||||
<GripHorizontal size={16} />
|
||||
</div>
|
||||
) : null}
|
||||
<FilterItem
|
||||
filterIndex={rowIndex++}
|
||||
filter={filter}
|
||||
onUpdate={(filter) =>
|
||||
props.onUpdateFilter(filterIndex, filter)
|
||||
}
|
||||
onRemoveFilter={() => onRemoveFilter(filterIndex)}
|
||||
saveRequestPayloads={saveRequestPayloads}
|
||||
disableDelete={cannotDeleteFilter}
|
||||
excludeFilterKeys={excludeFilterKeys}
|
||||
readonly={props.readonly}
|
||||
isConditional={isConditional}
|
||||
/>
|
||||
</div>
|
||||
) : null
|
||||
)}
|
||||
</div>
|
||||
</div>)}
|
||||
|
||||
<div className={'py-2 px-4'}>
|
||||
<div className={'flex items-center gap-2 mb-2'}>
|
||||
<div className="font-semibold">Filters</div>
|
||||
<FilterSelection filter={undefined} onFilterClick={onAddFilter}>
|
||||
<Button
|
||||
icon={<Icon name={'filter'} />}
|
||||
type="default"
|
||||
size={'small'}
|
||||
>
|
||||
Add
|
||||
</Button>
|
||||
</FilterSelection>
|
||||
<div className={'ml-auto'}>
|
||||
{!hideEventsOrder && (
|
||||
<EventsOrder filter={filter} onChange={props.onChangeEventsOrder} />
|
||||
)}
|
||||
{actions &&
|
||||
actions.map((action, index) => <div key={index}>{action}</div>)}
|
||||
</div>
|
||||
{filters.map((filter: any, filterIndex: any) =>
|
||||
!filter.isEvent ? (
|
||||
</div>
|
||||
<div className={'flex flex-col'}>
|
||||
{filters.map((filter: any, filterIndex: number) =>
|
||||
filter.isEvent ? (
|
||||
<div
|
||||
className={'py-2 hover:bg-active-blue px-5'}
|
||||
style={{
|
||||
pointerEvents: 'unset',
|
||||
paddingTop:
|
||||
hoveredItem.i === filterIndex &&
|
||||
hoveredItem.position === 'top'
|
||||
? '1.5rem'
|
||||
: '0.5rem',
|
||||
paddingBottom:
|
||||
hoveredItem.i === filterIndex &&
|
||||
hoveredItem.position === 'bottom'
|
||||
? '1.5rem'
|
||||
: '0.5rem',
|
||||
marginLeft: '-1.25rem',
|
||||
width: 'calc(100% + 2.5rem)',
|
||||
}}
|
||||
className={'hover:bg-active-blue px-5 gap-2 items-center flex'}
|
||||
id={`${filter.key}-${filterIndex}`}
|
||||
onDragOver={(e) => handleDragOverEv(e, filterIndex)}
|
||||
onDrop={(e) => handleDrop(e)}
|
||||
key={`${filter.key}-${filterIndex}`}
|
||||
>
|
||||
{!!props.onFilterMove && eventsNum > 1 ? (
|
||||
<div
|
||||
className={'p-2 cursor-grab'}
|
||||
draggable={!!props.onFilterMove}
|
||||
onDragStart={(e) =>
|
||||
handleDragStart(
|
||||
e,
|
||||
filterIndex,
|
||||
`${filter.key}-${filterIndex}`
|
||||
)
|
||||
}
|
||||
>
|
||||
<GripHorizontal size={16} />
|
||||
</div>
|
||||
) : null}
|
||||
<FilterItem
|
||||
key={filterIndex}
|
||||
readonly={props.readonly}
|
||||
isFilter={true}
|
||||
filterIndex={filterIndex}
|
||||
filterIndex={rowIndex++}
|
||||
filter={filter}
|
||||
onUpdate={(filter) => props.onUpdateFilter(filterIndex, filter)}
|
||||
onRemoveFilter={() => onRemoveFilter(filterIndex)}
|
||||
saveRequestPayloads={saveRequestPayloads}
|
||||
disableDelete={cannotDeleteFilter}
|
||||
excludeFilterKeys={excludeFilterKeys}
|
||||
readonly={props.readonly}
|
||||
isConditional={isConditional}
|
||||
/>
|
||||
</div>
|
||||
|
|
@ -240,6 +256,4 @@ function FilterList(props: Props) {
|
|||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default observer(FilterList);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
export { default } from './FilterList';
|
||||
export { FilterList, EventsList } from './FilterList';
|
||||
|
|
@ -80,13 +80,16 @@ const IconMap = {
|
|||
function filterJson(
|
||||
jsonObj: Record<string, any>,
|
||||
excludeKeys: string[] = [],
|
||||
allowedFilterKeys: string[] = []
|
||||
allowedFilterKeys: string[] = [],
|
||||
mode: 'filters' | 'events'
|
||||
): Record<string, any> {
|
||||
return Object.fromEntries(
|
||||
Object.entries(jsonObj)
|
||||
.map(([key, value]) => {
|
||||
const arr = value.filter((i: { key: string }) => {
|
||||
const arr = value.filter((i: { key: string, isEvent: boolean }) => {
|
||||
if (excludeKeys.includes(i.key)) return false;
|
||||
if (mode === 'events' && !i.isEvent) return false;
|
||||
if (mode === 'filters' && i.isEvent) return false;
|
||||
return !(
|
||||
allowedFilterKeys.length > 0 && !allowedFilterKeys.includes(i.key)
|
||||
);
|
||||
|
|
@ -139,6 +142,7 @@ interface Props {
|
|||
allowedFilterKeys?: Array<string>;
|
||||
isConditional?: boolean;
|
||||
isMobile?: boolean;
|
||||
mode: 'filters' | 'events';
|
||||
}
|
||||
|
||||
function FilterModal(props: Props) {
|
||||
|
|
@ -149,6 +153,7 @@ function FilterModal(props: Props) {
|
|||
excludeFilterKeys = [],
|
||||
allowedFilterKeys = [],
|
||||
isConditional,
|
||||
mode,
|
||||
} = props;
|
||||
const [searchQuery, setSearchQuery] = React.useState('');
|
||||
const [category, setCategory] = React.useState('ALL');
|
||||
|
|
@ -182,7 +187,7 @@ function FilterModal(props: Props) {
|
|||
: filters;
|
||||
const { matchingCategories, matchingFilters } = getMatchingEntries(
|
||||
searchQuery,
|
||||
filterJson(filterJsonObj, excludeFilterKeys, allowedFilterKeys)
|
||||
filterJson(filterJsonObj, excludeFilterKeys, allowedFilterKeys, mode)
|
||||
);
|
||||
|
||||
const isResultEmpty =
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
import React, { useState } from 'react';
|
||||
import FilterModal from '../FilterModal';
|
||||
import OutsideClickDetectingDiv from 'Shared/OutsideClickDetectingDiv';
|
||||
import { Icon } from 'UI';
|
||||
import { assist as assistRoute, isRoute } from 'App/routes';
|
||||
import cn from 'classnames';
|
||||
import { observer } from 'mobx-react-lite';
|
||||
|
|
@ -17,6 +16,7 @@ interface Props {
|
|||
disabled?: boolean;
|
||||
isConditional?: boolean;
|
||||
isMobile?: boolean;
|
||||
mode: 'filters' | 'events';
|
||||
}
|
||||
|
||||
function FilterSelection(props: Props) {
|
||||
|
|
@ -28,7 +28,8 @@ function FilterSelection(props: Props) {
|
|||
allowedFilterKeys = [],
|
||||
disabled = false,
|
||||
isConditional,
|
||||
isMobile
|
||||
isMobile,
|
||||
mode,
|
||||
} = props;
|
||||
const [showModal, setShowModal] = useState(false);
|
||||
|
||||
|
|
@ -83,6 +84,7 @@ function FilterSelection(props: Props) {
|
|||
allowedFilterKeys={allowedFilterKeys}
|
||||
isConditional={isConditional}
|
||||
isMobile={isMobile}
|
||||
mode={mode}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import React, { useEffect } from 'react';
|
||||
import FilterList from 'Shared/Filters/FilterList';
|
||||
import { FilterList } from 'Shared/Filters/FilterList';
|
||||
import { observer } from 'mobx-react-lite';
|
||||
import { useStore } from 'App/mstore';
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import React, { useEffect } from 'react';
|
||||
import { debounce } from 'App/utils';
|
||||
import FilterList from 'Shared/Filters/FilterList';
|
||||
import { FilterList, EventsList } from 'Shared/Filters/FilterList';
|
||||
|
||||
import { observer } from 'mobx-react-lite';
|
||||
import { useStore } from 'App/mstore';
|
||||
|
|
@ -81,7 +81,18 @@ function SessionFilters() {
|
|||
|
||||
return (
|
||||
<div className="relative">
|
||||
<EventsList
|
||||
filter={appliedFilter}
|
||||
onAddFilter={onAddFilter}
|
||||
onUpdateFilter={onUpdateFilter}
|
||||
onRemoveFilter={onRemoveFilter}
|
||||
onChangeEventsOrder={onChangeEventsOrder}
|
||||
saveRequestPayloads={saveRequestPayloads}
|
||||
onFilterMove={onFilterMove}
|
||||
mergeDown
|
||||
/>
|
||||
<FilterList
|
||||
mergeUp
|
||||
filter={appliedFilter}
|
||||
onAddFilter={onAddFilter}
|
||||
onUpdateFilter={onUpdateFilter}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue