Skip to content

Commit

Permalink
feat: add Akumatus Filter Builder example
Browse files Browse the repository at this point in the history
  • Loading branch information
lawvs committed Sep 13, 2024
1 parent 479f048 commit 502af04
Show file tree
Hide file tree
Showing 3 changed files with 186 additions and 6 deletions.
172 changes: 172 additions & 0 deletions packages/docs/src/components/examples/akumatus-filter-builder.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
import {
createFilterTheme,
FilterBuilder,
FilterSphereProvider,
useFilterGroup,
useFilterRule,
useFilterSphere,
useView,
} from "@fn-sphere/filter";
import { z } from "zod";

const schema = z.object({
id: z.number(),
name: z.string(),
createdAt: z.date(),
status: z.union([
z.literal("pending"),
z.literal("completed"),
z.literal("cancelled"),
]),
});

// Ported from [akumatus/FilterBuilder](https://github.com/akumatus/FilterBuilder)
// Licensed under MIT

const theme = createFilterTheme({
primitives: {
input: ({ ...props }) => {
return (
<input
{...props}
className="w-full px-2 py-1 h-[30px] border border-[#d2d6de] text-[#555] focus:border-[#3c8dbc] outline-none"
/>
);
},
select: ({ ...props }) => {
return (
<select
{...props}
className="w-full p-1 border border-[#d2d6de] text-[#555] focus:border-[#3c8dbc] outline-none"
/>
);
},
},
templates: {
FilterGroupContainer: ({ rule, children }) => {
const {
ruleState: { isRoot, isLastGroup },
toggleGroupOp,
appendChildGroup,
appendChildRule,
removeGroup,
} = useFilterGroup(rule);
const isAnd = rule.op === "and";
return (
<div
className={[
"flex flex-col gap-4 w-100 p-2 relative rounded-sm border border-solid border-[#6d77b8] border-t-4 border-t-[#6d77b8] mb-5 shadow-[0_1px_1px_rgba(0,0,0,0.1)] bg-[rgba(255,255,255,0.9)]",
"before:content-[''] before:absolute before:left-[-17px] before:w-4 before:h-[calc(50%+18px)] before:border-solid before:border-[#c0c5e2] before:border-b-2 before:border-l-2 before:top-[-18px]",
!isLastGroup
? "after:content-[''] after:absolute after:left-[-17px] after:w-4 after:h-[calc(50%+18px)] after:border-solid after:border-[#c0c5e2] after:border-l-2 after:top-[50%]"
: "",
isRoot ? "before:border-none after:border-none" : "ml-9",
].join(" ")}
>
<div className="flex justify-between">
<div className="flex items-center gap-1">
<button
className={`flex justify-center items-center px-2 py-0.5 text-xs leading-[1.5] whitespace-nowrap cursor-pointer select-none rounded-[11px] border ${isAnd ? "text-white bg-[#6d77b8]" : "bg-white border-[#6d77b8]"}`}
onClick={() => {
toggleGroupOp("and");
}}
>
And
</button>
<button
className={`flex justify-center items-center px-2 py-0.5 text-xs leading-[1.5] whitespace-nowrap cursor-pointer select-none rounded-[11px] border ${!isAnd ? "text-white bg-[#6d77b8]" : "bg-white border-[#6d77b8]"}`}
onClick={() => {
toggleGroupOp("or");
}}
>
Or
</button>
</div>

<div className="flex items-center gap-1">
<button
className={
"flex justify-center items-center px-2 py-0.5 text-xs leading-[1.5] whitespace-nowrap cursor-pointer select-none rounded-[3px] text-white bg-[#6d77b8]"
}
onClick={() => {
appendChildRule();
}}
>
+ add
</button>
<button
className={
"flex justify-center items-center px-2 py-0.5 text-xs leading-[1.5] whitespace-nowrap cursor-pointer select-none rounded-[3px] text-white bg-[#6d77b8]"
}
onClick={() => {
appendChildGroup();
}}
>
+ ( group )
</button>
{isRoot ? null : (
<button
className={[
"flex justify-center items-center px-2 py-0.5 text-xs leading-[1.5] whitespace-nowrap cursor-pointer select-none rounded-[3px] bg-[#6d77b8]",
// https://stackoverflow.com/questions/48152562/changing-font-color-of-html-symbol
// https://www.hyperui.dev/blog/text-shadow-with-tailwindcss
"text-transparent [text-shadow:_0_0_0_white]",
].join(" ")}
onClick={() => {
removeGroup();
}}
>
{/* Unicode character for "X" cancel / close https://stackoverflow.com/questions/5353461/unicode-character-for-x-cancel-close */}
&#x2716;
</button>
)}
</div>
</div>
{children}
</div>
);
},
SingleFilter: ({ rule }) => {
const {
ruleState: { isLastRule },
removeRule,
} = useFilterRule(rule);
const { FieldSelect, FilterSelect, FilterDataInput } =
useView("templates");
return (
<div
className={[
"flex items-center gap-8 ml-9 relative",
"before:content-[''] before:absolute before:-translate-x-full before:w-4 before:h-[calc(50%+17px)] before:border-solid before:border-[#c0c5e2] before:border-b-2 before:border-l-2 before:top-[-17px]",
!isLastRule
? "after:content-[''] after:absolute after:-translate-x-full after:w-4 after:h-[calc(50%+17px)] after:border-solid after:border-[#c0c5e2] after:border-l-2 after:top-[50%]"
: "",
].join(" ")}
>
<FieldSelect rule={rule} />
<FilterSelect rule={rule} />
<FilterDataInput rule={rule} />
<button
className="flex justify-center items-center aspect-square text-xs font-bold cursor-pointer rounded-full bg-white text-[#6d77b8] border border-[#6d77b8]"
onClick={() => {
removeRule();
}}
>
{/* Unicode character for "X" cancel / close https://stackoverflow.com/questions/5353461/unicode-character-for-x-cancel-close */}
&#x2715;
</button>
<div className="flex items-center gap-1"></div>
</div>
);
},
},
});

export function AdvancedFilter() {
const { context } = useFilterSphere({ schema });
return (
<FilterSphereProvider context={context} theme={theme}>
<FilterBuilder />
</FilterSphereProvider>
);
}
6 changes: 0 additions & 6 deletions packages/docs/src/content/docs/reference/example.md

This file was deleted.

14 changes: 14 additions & 0 deletions packages/docs/src/content/docs/reference/example.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
---
title: 🚧 Example
description: Practical implementations of Filter Sphere
---

## bootstrap styles

This example is ported from [akumatus/FilterBuilder](https://github.com/akumatus/FilterBuilder).

import { AdvancedFilter } from "~/components/examples/akumatus-filter-builder";

<div className="not-content" style={{ marginTop: "1rem" }}>
<AdvancedFilter client:load />
</div>

0 comments on commit 502af04

Please sign in to comment.