Handle oudside click for filters popover (#4021)
* Oudside click handler * Oudside click handler * Changeset
This commit is contained in:
parent
d08527f7db
commit
436c2af00e
6 changed files with 39 additions and 4 deletions
5
.changeset/rotten-otters-shop.md
Normal file
5
.changeset/rotten-otters-shop.md
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
---
|
||||||
|
"saleor-dashboard": patch
|
||||||
|
---
|
||||||
|
|
||||||
|
Handle oudside click for filters popover
|
|
@ -17,7 +17,11 @@ import { useIntl } from "react-intl";
|
||||||
export const ExpressionFilters = () => {
|
export const ExpressionFilters = () => {
|
||||||
const [open, setOpen] = useState(false);
|
const [open, setOpen] = useState(false);
|
||||||
const { formatMessage } = useIntl();
|
const { formatMessage } = useIntl();
|
||||||
const { valueProvider } = useConditionalFilterContext();
|
const { valueProvider, containerState } = useConditionalFilterContext();
|
||||||
|
|
||||||
|
const clickOutside = () => {
|
||||||
|
containerState.clearEmpty()
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Popover open={open} onOpenChange={open => setOpen(open)}>
|
<Popover open={open} onOpenChange={open => setOpen(open)}>
|
||||||
|
@ -28,7 +32,7 @@ export const ExpressionFilters = () => {
|
||||||
})}
|
})}
|
||||||
</DropdownButton>
|
</DropdownButton>
|
||||||
</Popover.Trigger>
|
</Popover.Trigger>
|
||||||
<Popover.Content align="start">
|
<Popover.Content align="start" onInteractOutside={clickOutside}>
|
||||||
<Box
|
<Box
|
||||||
__minHeight="250px"
|
__minHeight="250px"
|
||||||
__minWidth="636px"
|
__minWidth="636px"
|
||||||
|
|
|
@ -164,6 +164,10 @@ export class FilterElement {
|
||||||
return UrlEntry.forStatic(this.condition.selected, this.value.value);
|
return UrlEntry.forStatic(this.condition.selected, this.value.value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public equals(element: FilterElement) {
|
||||||
|
return this.value.value === element.value.value
|
||||||
|
}
|
||||||
|
|
||||||
public static isCompatible(element: unknown): element is FilterElement {
|
public static isCompatible(element: unknown): element is FilterElement {
|
||||||
return (
|
return (
|
||||||
typeof element === "object" &&
|
typeof element === "object" &&
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
import { FilterContainer } from "./FilterElement";
|
import { FilterContainer, FilterElement } from "./FilterElement";
|
||||||
|
|
||||||
export interface FilterValueProvider {
|
export interface FilterValueProvider {
|
||||||
value: FilterContainer;
|
value: FilterContainer;
|
||||||
loading: boolean;
|
loading: boolean;
|
||||||
persist: (newValue: FilterContainer) => void;
|
persist: (newValue: FilterContainer) => void;
|
||||||
|
isPersisted: (element: FilterElement) => boolean;
|
||||||
clear: () => void;
|
clear: () => void;
|
||||||
count: number;
|
count: number;
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,7 @@ import { useEffect, useState } from "react";
|
||||||
import useRouter from "use-react-router";
|
import useRouter from "use-react-router";
|
||||||
|
|
||||||
import { InitialAPIState } from "../API";
|
import { InitialAPIState } from "../API";
|
||||||
import { FilterContainer } from "../FilterElement";
|
import { FilterContainer, FilterElement } from "../FilterElement";
|
||||||
import { FilterValueProvider } from "../FilterValueProvider";
|
import { FilterValueProvider } from "../FilterValueProvider";
|
||||||
import { useTokenArray } from "./TokenArray";
|
import { useTokenArray } from "./TokenArray";
|
||||||
import { UrlEntry } from "./UrlToken";
|
import { UrlEntry } from "./UrlToken";
|
||||||
|
@ -61,6 +61,10 @@ export const useUrlValueProvider = (
|
||||||
setValue([]);
|
setValue([]);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const isPersisted = (element: FilterElement) => {
|
||||||
|
return value.some(p => FilterElement.isCompatible(p) && p.equals(element))
|
||||||
|
}
|
||||||
|
|
||||||
const count = value.filter(v => typeof v !== "string").length;
|
const count = value.filter(v => typeof v !== "string").length;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
@ -68,6 +72,7 @@ export const useUrlValueProvider = (
|
||||||
loading,
|
loading,
|
||||||
persist,
|
persist,
|
||||||
clear,
|
clear,
|
||||||
|
isPersisted,
|
||||||
count,
|
count,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
@ -49,6 +49,17 @@ const removeElement = (container: FilterContainer, position: number) => {
|
||||||
return removeConstraint(newContainer);
|
return removeConstraint(newContainer);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const removeEmptyElements = (container: FilterContainer, provider: FilterValueProvider): FilterContainer => {
|
||||||
|
const emptyIndex = container.findIndex(el =>
|
||||||
|
FilterElement.isCompatible(el) && (!provider.isPersisted(el) || el.isEmpty())
|
||||||
|
)
|
||||||
|
|
||||||
|
if (emptyIndex < 0) return container
|
||||||
|
|
||||||
|
return removeEmptyElements(removeElement(container, emptyIndex), provider)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
export const useContainerState = (valueProvider: FilterValueProvider) => {
|
export const useContainerState = (valueProvider: FilterValueProvider) => {
|
||||||
const [value, setValue] = useState<FilterContainer>([]);
|
const [value, setValue] = useState<FilterContainer>([]);
|
||||||
|
|
||||||
|
@ -124,6 +135,10 @@ export const useContainerState = (valueProvider: FilterValueProvider) => {
|
||||||
setValue([]);
|
setValue([]);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const clearEmpty = () => {
|
||||||
|
setValue(v => removeEmptyElements(v, valueProvider))
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
create,
|
create,
|
||||||
exist,
|
exist,
|
||||||
|
@ -133,5 +148,6 @@ export const useContainerState = (valueProvider: FilterValueProvider) => {
|
||||||
removeAt,
|
removeAt,
|
||||||
value,
|
value,
|
||||||
clear,
|
clear,
|
||||||
|
clearEmpty
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in a new issue