Skip to content

Commit

Permalink
add fitlers
Browse files Browse the repository at this point in the history
  • Loading branch information
indaviande committed Dec 11, 2024
1 parent 349a17a commit aaf64c4
Show file tree
Hide file tree
Showing 2 changed files with 115 additions and 54 deletions.
101 changes: 67 additions & 34 deletions src/components/extenders/Select.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@ export const selectStyles = tv({
"text-main-11 rounded-sm flex items-center dim focus-visible:outline-main-12 !leading-none justify-between text-nowrap font-text font-semibold",
],
slots: {
dropdown: "outline-0 z-50 origin-top animate-drop animate-stretch mt-sm min-w-[var(--popover-anchor-width)]",
dropdown:
"outline-0 z-50 origin-top animate-drop animate-stretch mt-sm min-w-[var(--popover-anchor-width)]",
item: "rounded-sm flex justify-between items-center gap-lg cursor-pointer select-none p-sm outline-offset-0 outline-0 text-nowrap focus-visible:outline-main-12",
icon: "flex items-center",
value: "flex gap-sm items-center",
Expand Down Expand Up @@ -137,12 +138,14 @@ export type SelectProps<Value> = Component<{
}> &
RadixSelect.SelectProps;

type MaybeArray<T, IsArray extends undefined | boolean> = IsArray extends true ? T[] : T;
type MaybeArray<T, IsArray extends undefined | boolean> = IsArray extends true
? T[]
: T;

export default function Select<
T extends string | number,
Multiple extends undefined | boolean,
Value extends MaybeArray<T, Multiple>,
Value extends MaybeArray<T, Multiple>
>({
look,
size,
Expand Down Expand Up @@ -174,35 +177,45 @@ export default function Select<
});

const value = useMemo(() => getter ?? internal, [getter, internal]);
const setValue = useCallback((v: Value) => setter?.(v) ?? setInternal(v), [setter]);
const setValue = useCallback(
(v: Value) => setter?.(v) ?? setInternal(v),
[setter]
);

const [searchInput, setSearch] = useState<string>();

const matches = useMemo(() => {
if (!search) return Object.keys(options ?? {});
// const textToMatch = Object.keys(options ?? {}).map(option => `${option}_${options[option]?.props?.children?.filter(a => typeof a !== "object").join(" ")}`)
const textToMatch = Object.keys(options ?? {}).reduce(
(matches, option) => {
const opt = options?.[option];
const key =
typeof opt === "string"
? opt
: (
options?.[option] as Exclude<ReactNode, string | number | boolean | Iterable<ReactNode>>
)?.props?.children
?.filter?.((a: unknown) => typeof a !== "object")
?.join(" ");
const textToMatch = Object.keys(options ?? {}).reduce((matches, option) => {
const opt = options?.[option];
const key =
typeof opt === "string"
? opt
: (
options?.[option] as Exclude<
ReactNode,
string | number | boolean | Iterable<ReactNode>
>
)?.props?.children
?.filter?.((a: unknown) => typeof a !== "object")
?.join(" ");

return Object.assign(matches, { [`${option}`]: option }, { [`${key}`]: option });
},
{} as { [key: string]: keyof typeof options },
);
const searchMatches = matchSorter(Object.keys(textToMatch), searchInput ?? "").map(key => textToMatch[key]);
return Object.assign(
matches,
{ [`${option}`]: option },
{ [`${key}`]: option }
);
}, {} as { [key: string]: keyof typeof options });
const searchMatches = matchSorter(
Object.keys(textToMatch),
searchInput ?? ""
).map((key) => textToMatch[key]);
const uniqueOptionMatches = Array.from(
searchMatches.reduce((set, option) => {
set.add(option);
return set;
}, new Set()),
}, new Set())
) as (typeof value)[];

return uniqueOptionMatches;
Expand All @@ -211,7 +224,9 @@ export default function Select<
const label = useMemo(() => {
if (
value &&
(typeof value === "number" || typeof value === "string" || typeof value === "symbol") &&
(typeof value === "number" ||
typeof value === "string" ||
typeof value === "symbol") &&
options?.[value]
)
return options?.[value];
Expand All @@ -221,8 +236,9 @@ export default function Select<
<Text
className={mergeClass(
prefixLabel(),
"w-[1.2em] h-[1.2em] flex items-center justify-center rounded-full bg-main-6 text-main-12",
)}>
"w-[1.2em] h-[1.2em] flex items-center justify-center rounded-full bg-main-6 text-main-12"
)}
>
{value.length}
</Text>{" "}
{placeholder}
Expand All @@ -234,17 +250,23 @@ export default function Select<
return (
<Ariakit.ComboboxProvider
resetValueOnHide
setValue={value => {
setValue={(value) => {
setSearch(value);
}}>
}}
>
<Ariakit.SelectProvider
setValue={v => setValue(v as Value)}
setValue={(v) => setValue(v as Value)}
value={value as string}
defaultValue={multiple ? [] : undefined}>
defaultValue={multiple ? [] : undefined}
>
<Ariakit.Select className={mergeClass(base(), className)}>
<div className={valueStyle()}>{label}</div>
<div className={icon()}>
{loading ? <Icon className="animate-spin" remix="RiLoader4Fill" /> : <Icon remix="RiArrowDropDownLine" />}
{loading ? (
<Icon className="animate-spin" remix="RiLoader4Fill" />
) : (
<Icon remix="RiArrowDropDownLine" />
)}
</div>
</Ariakit.Select>
<Ariakit.SelectPopover gutter={4} className={dropdown()}>
Expand All @@ -254,7 +276,11 @@ export default function Select<
<Ariakit.Combobox
autoSelect
placeholder="Search..."
className={mergeClass(inputStyles({ size: "sm", look: "base" }), "w-full", !search && "hidden")}
className={mergeClass(
inputStyles({ size: "sm", look: "base" }),
"w-full",
!search && "hidden"
)}
/>
</div>
)}
Expand All @@ -277,7 +303,11 @@ export default function Select<
key="select"
className={mergeClass(
check(),
!((typeof value === "object" && value?.length > 0) || value === undefined) && "opacity-0",
!(
(typeof value === "object" &&
value?.length > 0) ||
value === undefined
) && "opacity-0"
)}
size="sm"
remix="RiCheckFill"
Expand All @@ -287,7 +317,7 @@ export default function Select<
}
/>
)}
{matches?.map(_value => (
{matches?.map((_value) => (
<Ariakit.SelectItem
key={_value as string}
value={_value as string}
Expand All @@ -302,8 +332,11 @@ export default function Select<
key="select"
className={mergeClass(
check(),
!((typeof value === "object" && value?.includes(_value as T)) || value === _value) &&
"opacity-0",
!(
(typeof value === "object" &&
value?.includes(_value as T)) ||
value === _value
) && "opacity-0"
)}
size="sm"
remix="RiCheckFill"
Expand Down
68 changes: 48 additions & 20 deletions src/components/primitives/Input.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,22 +7,22 @@ import type { Component, GetSet, Styled } from "../../utils/types";
import Group from "../extenders/Group";

export const inputStyles = tv({
base: "text-main-12 flex items-center text-nowrap font-text",
base: "flex items-center placeholder:text-main-11 text-nowrap font-text",
variants: {
look: {
none: "bg-main-0 border-0",
soft: "bg-main-2 border-main-2 hover:border-main-4 active:border-main-7 hover:text-main-12 focus-within:outline focus-within:outline-main-12",
base: "bg-main-6 focus-within:outline focus-within:outline-main-12",
bold: "bg-main-3 border-main-4 hover:border-main-4 active:border-main-7 hover:text-main-12 focus-within:outline focus-within:outline-main-12",
tint: "bg-main-1 border-accent-6 hover:border-accent-8 active:bg-main-2 text-main-12 focus-within:outline focus-within:outline-main-12",
hype: "bg-main-0 border-accent-8 hover:border-accent-10 active:border-accent-8 hover:text-main-12 focus-within:outline focus-within:outline-main-12",
none: "text-main-12 bg-main-0 border-0",
soft: "text-main-11 bg-main-0 border-main-9 border-1 active:border-main-7 focus-within:outline focus-within:outline-main-12",
base: "text-main-12 bg-main-6 focus-within:outline focus-within:outline-main-12",
bold: "text-main-12 bg-main-3 border-main-4 hover:border-main-4 active:border-main-7 hover:text-main-12 focus-within:outline focus-within:outline-main-12",
tint: "text-main-12 bg-main-1 border-accent-6 hover:border-accent-8 active:bg-main-2 focus-within:outline focus-within:outline-main-12",
hype: "text-main-12 bg-main-0 border-accent-8 hover:border-accent-10 active:border-accent-8 hover:text-main-12 focus-within:outline focus-within:outline-main-12",
},
size: {
xs: "px-xs py-xs text-xs rounded-xs",
sm: "px-sm py-sm text-sm rounded-sm",
md: "px-md py-md text-md rounded-md",
lg: "px-lg py-lg text-lg rounded-lg",
xl: "px-xl py-xl text-3xl rounded-xl",
md: "px-md py-sm text-md rounded-md",
lg: "px-lg py-md text-lg rounded-lg",
xl: "px-xl py-lg text-3xl rounded-xl",
},
},
defaultVariants: {
Expand All @@ -31,7 +31,14 @@ export const inputStyles = tv({
},
});

export const extensions = ["header", "footer", "prefix", "suffix", "label", "hint"] as const;
export const extensions = [
"header",
"footer",
"prefix",
"suffix",
"label",
"hint",
] as const;
export type InputExtension = (typeof extensions)[number];

export type InputProps<T = string> = Component<
Expand All @@ -44,19 +51,30 @@ export type InputProps<T = string> = Component<
function Input({ look, size, state, className, ...props }: InputProps) {
const { header, footer, prefix, suffix, label, hint, ...rest } = props;

if (extensions.some(extension => !!props?.[extension]))
if (extensions.some((extension) => !!props?.[extension]))
return (
<label className={mergeClass(inputStyles({ look, size }), className, "flex-col flex")} htmlFor="input">
<label
className={mergeClass(
inputStyles({ look, size }),
className,
"flex-col flex"
)}
htmlFor="input"
>
<label htmlFor="input" className="w-full flex">
{header}
</label>
<Group className="w-full flex-row flex-nowrap items-center">
{prefix && <label htmlFor="input">{prefix}</label>}
<input
id="input"
className={mergeClass(inputStyles({ look: "none", size }), className, "w-full !flex-1 !px-0 !py-0")}
className={mergeClass(
inputStyles({ look: "none", size }),
className,
"w-full !flex-1 !px-0 !py-0"
)}
value={state?.[0]}
onChange={e => state?.[1]?.(e?.target?.value)}
onChange={(e) => state?.[1]?.(e?.target?.value)}
{...rest}
/>
{suffix && <label htmlFor="input">{suffix}</label>}
Expand All @@ -70,13 +88,17 @@ function Input({ look, size, state, className, ...props }: InputProps) {
<input
className={mergeClass(inputStyles({ look, size }), className)}
value={state?.[0]}
onChange={e => state?.[1]?.(e?.target?.value)}
onChange={(e) => state?.[1]?.(e?.target?.value)}
{...rest}
/>
);
}

Input.BigInt = function InputBigInt({ state, base, ...props }: InputProps<bigint> & { base: number }) {
Input.BigInt = function InputBigInt({
state,
base,
...props
}: InputProps<bigint> & { base: number }) {
const [internal, setInternal] = useState<bigint>();
const [displayed, setDisplayed] = useState("0.0");
const [_getter, setter] = state ?? [];
Expand All @@ -98,7 +120,8 @@ Input.BigInt = function InputBigInt({ state, base, ...props }: InputProps<bigint

const raw = v?.replaceAll(",", "") ?? "";
const isInputtingDecimals =
v?.split(".")?.[1]?.[v?.split?.(".")?.[1]?.length - 1] === "0" || v?.[v.length - 1] === ".";
v?.split(".")?.[1]?.[v?.split?.(".")?.[1]?.length - 1] === "0" ||
v?.[v.length - 1] === ".";
const transformed = parseUnits(raw, base);

if (raw === "0") setDisplayed("");
Expand All @@ -107,11 +130,16 @@ Input.BigInt = function InputBigInt({ state, base, ...props }: InputProps<bigint
setter?.(transformed) ?? setInternal(transformed);
} catch (_err) {}
},
[setter, base],
[setter, base]
);

//TODO: implement setter callback
return <Input state={[displayed, v => typeof v !== "function" && setValue(v)]} {...props} />;
return (
<Input
state={[displayed, (v) => typeof v !== "function" && setValue(v)]}
{...props}
/>
);
};

export default Input;

0 comments on commit aaf64c4

Please sign in to comment.