Browse Source

Added map filter indication

Added map filter indication
+ some more type fixes...
pull/585/head
Jeremy Gallant 1 year ago
parent
commit
f399d17721
  1. 33
      src/core/hooks/useNodeFilters.ts
  2. 12
      src/pages/Map/FilterControl.tsx
  3. 15
      src/pages/Map/index.tsx

33
src/core/hooks/useNodeFilters.ts

@ -6,7 +6,7 @@ interface BooleanFilter {
key: string;
label: string;
type: "boolean";
predicate: (node: Node, value: boolean) => boolean;
predicate: (node: Protobuf.Mesh.NodeInfo, value: boolean) => boolean;
}
interface RangeFilter {
@ -14,14 +14,14 @@ interface RangeFilter {
label: string;
type: "range";
bounds: [number, number];
predicate: (node: Node, value: [number, number]) => boolean;
predicate: (node: Protobuf.Mesh.NodeInfo, value: [number, number]) => boolean;
}
interface SearchFilter {
key: string;
label: string;
type: "search";
predicate: (node: Node, value: string) => boolean;
predicate: (node: Protobuf.Mesh.NodeInfo, value: string) => boolean;
}
export type FilterConfig = BooleanFilter | RangeFilter | SearchFilter;
@ -113,7 +113,7 @@ export function useNodeFilters(nodes: Protobuf.Mesh.NodeInfo[]) {
acc[cfg.key] = false;
break;
case "range":
acc[cfg.key] = cfg.bounds!;
acc[cfg.key] = cfg.bounds;
break;
case "search":
acc[cfg.key] = "";
@ -141,13 +141,36 @@ export function useNodeFilters(nodes: Protobuf.Mesh.NodeInfo[]) {
const filteredNodes = useMemo(
() =>
nodes.filter((node) =>
filterConfigs.every((cfg) => cfg.predicate(node, filters[cfg.key]))
filterConfigs.every((cfg) => {
const val = filters[cfg.key];
switch (cfg.type) {
case "boolean":
if (typeof val !== "boolean") return true;
return cfg.predicate(node, val);
case "range":
if (
!Array.isArray(val) ||
val.length !== 2 ||
typeof val[0] !== "number" ||
typeof val[1] !== "number"
) {
return true;
}
return cfg.predicate(node, val);
case "search":
if (typeof val !== "string") return true;
return cfg.predicate(node, val);
}
})
),
[nodes, filters],
);
return {
filters,
defaultState,
onFilterChange,
resetFilters,
filteredNodes,

12
src/pages/Map/FilterControl.tsx

@ -10,6 +10,7 @@ import type {
FilterConfig,
FilterValueMap,
} from "@core/hooks/useNodeFilters.ts";
import { cn } from "@core/utils/cn.ts";
interface FilterControlProps {
configs: FilterConfig[];
@ -19,18 +20,25 @@ interface FilterControlProps {
value: FilterValueMap[K],
) => void;
resetFilters: () => void;
isDirty: boolean;
children?: React.ReactNode;
}
export function FilterControl(
{ configs, values, onChange, resetFilters, children }: FilterControlProps,
{ configs, values, onChange, resetFilters, isDirty, children }:
FilterControlProps,
) {
return (
<Popover>
<PopoverTrigger asChild>
<button
type="button"
className="fixed bottom-17 right-2 px-1 py-1 bg-slate-100 text-slate-600 rounded shadow-md"
className={cn(
"fixed bottom-17 right-2 px-1 py-1 rounded shadow-md",
isDirty
? " text-slate-100 bg-green-600"
: "text-slate-600 bg-slate-100",
)}
aria-label="Filter"
>
<FunnelIcon />

15
src/pages/Map/index.tsx

@ -56,13 +56,25 @@ const MapPage = () => {
);
const {
filteredNodes,
filters,
defaultState,
onFilterChange,
resetFilters,
filteredNodes,
filterConfigs,
} = useNodeFilters(validNodes);
const isDirty = useMemo(() => {
return Object.keys(filters).some((key) => {
const a = filters[key];
const b = defaultState[key];
// simple deep‐equal for primitives and [number,number]
return Array.isArray(a) && Array.isArray(b)
? a[0] !== b[0] || a[1] !== b[1]
: a !== b;
});
}, [filters, defaultState]);
const handleMarkerClick = useCallback(
(node: Protobuf.Mesh.NodeInfo, event: { originalEvent: MouseEvent }) => {
event?.originalEvent?.stopPropagation();
@ -213,6 +225,7 @@ const MapPage = () => {
values={filters}
onChange={onFilterChange}
resetFilters={resetFilters}
isDirty={isDirty}
/>
</PageLayout>
</>

Loading…
Cancel
Save