Skip to content

Commit

Permalink
Merge pull request #96 from Franpastoragusti/Improve-current-ux
Browse files Browse the repository at this point in the history
Improve current ux
  • Loading branch information
escottalexander authored Apr 15, 2024
2 parents 101b1ae + bb4be91 commit 89780b9
Show file tree
Hide file tree
Showing 16 changed files with 389 additions and 80 deletions.
30 changes: 21 additions & 9 deletions packages/nextjs/app/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,18 +8,17 @@ import ImpactVectorDisplay from "~~/components/impact-vector/ImpactVectorDisplay
import ImpactVectorGraph from "~~/components/impact-vector/ImpactVectorGraph";
import ImpactVectorTable from "~~/components/impact-vector/ImpactVectorTable";
import ImpactvectorLists from "~~/components/impact-vector/ImpactvectorLists";
import { SearchBar } from "~~/components/impact-vector/SearchBar";
import { useGlobalState } from "~~/services/store/store";

const Home: NextPage = () => {
const { selectedVectors, setSelectedVectors } = useGlobalState();
const [impactData, setImpactData] = useState<DataSet[]>([]);
const [isVectors, setIsVectors] = useState<boolean>(true);
const [fullGraph, setFullGraph] = useState<boolean>(false);

const [projectsShown, setProjectsShown] = useState(20);
useEffect(() => {
// Initialize selected vector
setSelectedVectors([{ name: "Total Onchain Users", weight: 100 }]);
setSelectedVectors([{ name: "Total Onchain Users", weight: 100, dataType: "number", filters: [] }]);
}, [setSelectedVectors]);

useEffect(() => {
Expand Down Expand Up @@ -63,15 +62,29 @@ const Home: NextPage = () => {
}, [selectedVectors]);

return (
<main className={`w-full flex flex-col gap-6 sm:gap-10 p-3 ${!fullGraph && "lg:mb-[22rem]"} relative`}>
<main className={`w-full flex flex-col relative`}>
<div
className={`${
fullGraph ? "w-full" : "lg:w-[50%] xl:w-[58%] 2xl:w-[64%] 3xl:w-[70%]"
} duration-500 ease-in-out h-[60vh] transition-all`}
} duration-500 ease-in-out transition-all`}
>
<div className="flex w-full h-[60vh]">
<div
className="flex w-full h-[50vh] overflow"
onWheel={e => {
const isDown = e.deltaY < 0;
if ((isDown && projectsShown <= 10) || (!isDown && projectsShown >= impactData.length)) {
return;
}
setProjectsShown(current => current + (isDown ? -10 : 10));
}}
>
{impactData.length > 0 && (
<ImpactVectorGraph data={impactData} fullGraph={fullGraph} setFullGraph={setFullGraph} />
<ImpactVectorGraph
data={impactData}
projectsShown={projectsShown}
fullGraph={fullGraph}
setFullGraph={setFullGraph}
/>
)}
</div>
</div>
Expand All @@ -87,7 +100,7 @@ const Home: NextPage = () => {
<div
className={`b-md:w-[34rem] w-full ${
fullGraph ? "lg:relative mt-0" : "lg:absolute lg:top-0 lg:right-0"
} transition-all overflow-hidden h-auto b-md:max-w-[34rem] rounded-3xl p-6 border grid gap-6 mx-auto duration-1000 ease-in-out`}
} transition-all overflow-hidden b-md:max-w-[34rem] rounded-3xl p-6 border grid gap-6 mx-auto duration-1000 ease-in-out h-[90vh]`}
>
<div className="rounded-xl grid grid-cols-2 bg-base-300 p-1">
<button
Expand All @@ -107,7 +120,6 @@ const Home: NextPage = () => {
Lists
</button>
</div>
<SearchBar placeholder="Search Impact Vectors" />
{isVectors ? <ImpactVectorDisplay /> : <ImpactvectorLists />}
</div>
</div>
Expand Down
9 changes: 9 additions & 0 deletions packages/nextjs/app/types/data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,17 +68,26 @@ export interface DataSet {

type VectorSourceName = "OSO";

export type VectorDataType = "number" | "date" | "string";
export interface Vector {
name: keyof ImpactVectors;
description: string;
sourceName: VectorSourceName;
parent: string;
fieldName: string;
dataType?: VectorDataType;
}

export interface IFilter {
action: "include" | "exclude" | "multiply";
condition: string;
value: number | string;
}
export interface SelectedVector {
name: keyof ImpactVectors;
weight: number;
filters: IFilter[];
dataType: VectorDataType;
}

export interface VectorList {
Expand Down
203 changes: 184 additions & 19 deletions packages/nextjs/components/impact-vector/FilterModal.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
import { useRef } from "react";
import { SearchBar } from "./SearchBar";
import { FunnelIcon } from "@heroicons/react/24/outline";
import { useRef, useState } from "react";
import { SelectInput } from "./inputs/select";
import { FiPlus, FiTrash2 } from "react-icons/fi";
import { IoClose } from "react-icons/io5";
import { PiSlidersHorizontalBold } from "react-icons/pi";
import { IFilter, SelectedVector } from "~~/app/types/data";

const FilterModal = ({ vector, onFiltered }: { vector: SelectedVector; onFiltered: (filters: IFilter[]) => void }) => {
const [localFilters, setLocalFilters] = useState<IFilter[]>(vector.filters || []);

const FilterModal = () => {
const modalRef = useRef<HTMLDialogElement>(null);

const openModal = () => {
Expand All @@ -12,44 +17,204 @@ const FilterModal = () => {
modalRef.current?.close();
};

const onFilterApply = () => {
onFiltered(localFilters);
closeModal();
};
const clearAll = () => {
setLocalFilters([]);
};
const onFilterDelete = (index: number) => {
setLocalFilters(current => [...current.slice(0, index), ...current.slice(index + 1)]);
};

const handleKeyDown: React.KeyboardEventHandler<HTMLDivElement> = e => {
if (e.key === "Escape") {
setLocalFilters(vector.filters || []);
closeModal();
}
};

const handleOutsideClick: React.MouseEventHandler<HTMLDialogElement> = e => {
if (e.target === modalRef.current) {
setLocalFilters(vector.filters || []);
closeModal();
}
};

const onAddFilterClick = () => {
setLocalFilters(current => [...current, { action: "include", condition: "==", value: "" }]);
};

const onUpdateFilter = (newValue: IFilter, index: number) => {
setLocalFilters(current => [...current.slice(0, index), newValue, ...current.slice(index + 1)]);
};

return (
<>
<div onKeyDown={handleKeyDown} className="lg:mr-5 flex justify-end">
<button
onClick={openModal}
className="btn items-center btn-sm rounded-md border-opacity-20 outline-none font-light border-stone-500"
>
<span className="flex-row flex items-center whitespace-nowrap justify-center gap-x-1 text-sm">
<FunnelIcon className="w-5 h-4" />
Set filter
</span>
<span className="badge badge-xs rounded-[0.3rem] bg-slate-500 text-xs text-white p-1 py-2 h-[0.01rem]">
0
<div onKeyDown={handleKeyDown} className="lg:mr-5 flex justify-end relative">
{vector.filters.length > 0 ? (
<span className="bg-red-500 text-white flex absolute left-[10px] top-[-5px] justify-center items-center w-[16px] h-[16px] rounded-full text-xs">
{vector.filters.length}
</span>
) : (
<></>
)}
<button className="w-[10px]">
<PiSlidersHorizontalBold size={20} onClick={() => openModal()} />
</button>
</div>

<dialog role="dialog" ref={modalRef} className="modal" onClick={handleOutsideClick}>
<div className="modal-box min-h-[300px] relative">
<SearchBar placeholder="Search filters or presets" />

<div className="modal-action"></div>
<div className="modal-box min-h-[400px] min-w-[600px] flex flex-col relative">
<h3 className="font-bold text-lg justify-between">{vector.name}</h3>
<IoClose
className="absolute right-4"
size={24}
onClick={() => {
setLocalFilters(vector.filters || []);
closeModal();
}}
></IoClose>
<div className="modal-content min-h-[100%] flex justify-between flex-col flex-grow">
<div className="modal-content min-h-[100%] flex-col mt-2">
{localFilters.map((item, i) => (
<FilterItem
onChange={arg => onUpdateFilter(arg, i)}
item={item}
key={i}
onDelete={() => onFilterDelete(i)}
/>
))}
<button
className="flex items-center text-primary font-[600] "
onClick={() => onAddFilterClick()}
type="button"
>
<FiPlus /> <span className="ml-2">Add filter</span>
</button>
</div>
</div>
<div className="modal-action flex justify-between bottom-0">
<button className="btn btn-secondary btn-md w-[50%]" onClick={clearAll} type="button">
Clear all
</button>
<button className="btn btn-primary btn-md w-[50%]" onClick={onFilterApply}>
Apply
</button>
</div>
</div>
</dialog>
</>
);
};

interface IFilterItemProps {
item: IFilter;
onChange: (arg: IFilter) => void;
onDelete: () => void;
}
const FilterItem = ({ item, onChange, onDelete }: IFilterItemProps) => {
return (
<div className="modal-content min-h-[100%] gap-2 flex-grow flex items-center flex-row mb-2">
<SelectInput
value={item.action}
onChange={(value: string) => {
onChange({ ...item, action: value as "include" | "exclude" | "multiply" });
}}
name={"action"}
placeholder={"Action"}
options={[
{
value: "include",
label: "Include",
},
{
value: "exclude",
label: "Exclude",
},
{
value: "multiply",
label: "Multiply",
},
]}
/>
<p>{item.action != "multiply" ? "where" : "by"}</p>

{item.action != "multiply" ? (
<SelectInput
value={item.condition}
onChange={(value: string) => {
onChange({ ...item, condition: value });
}}
name={"exactValue"}
placeholder={"is equal to"}
options={[
{
value: "==",
label: "equal to",
},
{
value: "!=",
label: "not equal to",
},
{
value: ">",
label: "greater than",
},
{
value: "<",
label: "less than",
},
{
value: ">=",
label: "greater or equal to",
},
{
value: "<=",
label: "less or equal to",
},
{
value: "<>",
label: "is between",
},
]}
/>
) : (
<></>
)}
<input
type="number"
className=" max-w-[60px] pl-2 p-0 input input-info input-bordered bg-secondary focus:outline-none border border-neutral hover:border-gray-400 rounded-xl text-neutral-500"
placeholder="0"
onChange={e => {
onChange({ ...item, value: e.target.value });
}}
value={item.value}
/>
{item.condition == "<>" ? (
<>
<p>and</p>
<input
type="number"
className=" max-w-[60px] pl-2 p-0 input input-info input-bordered bg-secondary focus:outline-none border border-neutral hover:border-gray-400 rounded-xl text-neutral-500"
placeholder="0"
onChange={e => {
onChange({ ...item, value: e.target.value });
}}
value={item.value}
/>
</>
) : (
<></>
)}
<div className="flex-grow justify-end flex items-center">
<button className="w-[20px]">
<FiTrash2 size={20} onClick={() => onDelete()} />
</button>
</div>
</div>
);
};

export default FilterModal;
Loading

0 comments on commit 89780b9

Please sign in to comment.