Initial commit for new panel
This commit is contained in:
@@ -0,0 +1,35 @@
|
||||
import { useState } from "react";
|
||||
|
||||
const CheckboxFive = () => {
|
||||
const [isChecked, setIsChecked] = useState<boolean>(false);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<label
|
||||
htmlFor="checkboxLabelFive"
|
||||
className="flex cursor-pointer select-none items-center text-body-sm font-medium text-dark dark:text-white"
|
||||
>
|
||||
<div className="relative">
|
||||
<input
|
||||
type="checkbox"
|
||||
id="checkboxLabelFive"
|
||||
className="sr-only"
|
||||
onChange={() => {
|
||||
setIsChecked(!isChecked);
|
||||
}}
|
||||
/>
|
||||
<div
|
||||
className={`box mr-2 flex h-5 w-5 items-center justify-center rounded-full border border-primary ${
|
||||
isChecked && "!border-4"
|
||||
}`}
|
||||
>
|
||||
<span className="h-2.5 w-2.5 rounded-full bg-white dark:bg-transparent"></span>
|
||||
</div>
|
||||
</div>
|
||||
Checkbox Text
|
||||
</label>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default CheckboxFive;
|
||||
@@ -0,0 +1,43 @@
|
||||
import { useState } from "react";
|
||||
|
||||
const CheckboxFour = () => {
|
||||
const [isChecked, setIsChecked] = useState<boolean>(false);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<label
|
||||
htmlFor="checkboxLabelFour"
|
||||
className="flex cursor-pointer select-none items-center text-body-sm font-medium text-dark dark:text-white"
|
||||
>
|
||||
<div className="relative">
|
||||
<input
|
||||
type="checkbox"
|
||||
id="checkboxLabelFour"
|
||||
className="sr-only"
|
||||
onChange={() => {
|
||||
setIsChecked(!isChecked);
|
||||
}}
|
||||
/>
|
||||
<div
|
||||
className={`mr-2 flex h-5 w-5 items-center justify-center rounded-full border ${
|
||||
isChecked
|
||||
? "border-primary bg-gray-2 dark:bg-dark-2"
|
||||
: "border-dark-5 dark:border-dark-6"
|
||||
}`}
|
||||
>
|
||||
<span
|
||||
className={`h-2.5 w-2.5 rounded-full bg-transparent ${
|
||||
isChecked && "!bg-primary"
|
||||
}`}
|
||||
>
|
||||
{" "}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
Checkbox Text
|
||||
</label>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default CheckboxFour;
|
||||
@@ -0,0 +1,39 @@
|
||||
import { useState } from "react";
|
||||
|
||||
const CheckboxOne = () => {
|
||||
const [isChecked, setIsChecked] = useState<boolean>(false);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<label
|
||||
htmlFor="checkboxLabelOne"
|
||||
className="flex cursor-pointer select-none items-center text-body-sm font-medium"
|
||||
>
|
||||
<div className="relative">
|
||||
<input
|
||||
type="checkbox"
|
||||
id="checkboxLabelOne"
|
||||
className="sr-only"
|
||||
onChange={() => {
|
||||
setIsChecked(!isChecked);
|
||||
}}
|
||||
/>
|
||||
<div
|
||||
className={`mr-2 flex h-5 w-5 items-center justify-center rounded border ${
|
||||
isChecked
|
||||
? "border-primary bg-gray-2 dark:bg-transparent"
|
||||
: "border-dark-5 dark:border-dark-6"
|
||||
}`}
|
||||
>
|
||||
<span
|
||||
className={`h-2.5 w-2.5 rounded-sm ${isChecked && "bg-primary"}`}
|
||||
></span>
|
||||
</div>
|
||||
</div>
|
||||
Checkbox Text
|
||||
</label>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default CheckboxOne;
|
||||
@@ -0,0 +1,64 @@
|
||||
import { useState } from "react";
|
||||
|
||||
const CheckboxThree = () => {
|
||||
const [isChecked, setIsChecked] = useState<boolean>(false);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<label
|
||||
htmlFor="checkboxLabelThree"
|
||||
className="flex cursor-pointer select-none items-center text-body-sm font-medium"
|
||||
>
|
||||
<div className="relative">
|
||||
<input
|
||||
type="checkbox"
|
||||
id="checkboxLabelThree"
|
||||
className="sr-only"
|
||||
onChange={() => {
|
||||
setIsChecked(!isChecked);
|
||||
}}
|
||||
/>
|
||||
<div
|
||||
className={`box mr-2 flex h-5 w-5 items-center justify-center rounded border ${
|
||||
isChecked
|
||||
? "border-primary bg-gray-2 dark:bg-transparent"
|
||||
: "border-dark-5 dark:border-dark-6"
|
||||
}`}
|
||||
>
|
||||
<span
|
||||
className={`text-primary opacity-0 ${
|
||||
isChecked && "!opacity-100"
|
||||
}`}
|
||||
>
|
||||
<svg
|
||||
className="fill-current"
|
||||
width="11"
|
||||
height="11"
|
||||
viewBox="0 0 11 11"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<g clipPath="url(#clip0_803_2686)">
|
||||
<path
|
||||
fillRule="evenodd"
|
||||
clipRule="evenodd"
|
||||
d="M1.23529 2.29669C0.942402 2.00379 0.942402 1.52892 1.23529 1.23603C1.52819 0.943134 2.00306 0.943134 2.29596 1.23603L5.37433 4.3144L8.45261 1.23612C8.7455 0.943225 9.22038 0.943225 9.51327 1.23612C9.80616 1.52901 9.80616 2.00389 9.51327 2.29678L6.43499 5.37506L9.51327 8.45334C9.80616 8.74624 9.80616 9.22111 9.51327 9.514C9.22038 9.8069 8.7455 9.8069 8.45261 9.514L5.37433 6.43572L2.29596 9.51409C2.00306 9.80699 1.52819 9.80699 1.23529 9.51409C0.942402 9.2212 0.942402 8.74633 1.23529 8.45343L4.31367 5.37506L1.23529 2.29669Z"
|
||||
fill=""
|
||||
/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_803_2686">
|
||||
<rect width="10.75" height="10.75" fill="white" />
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
Checkbox Text
|
||||
</label>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default CheckboxThree;
|
||||
@@ -0,0 +1,52 @@
|
||||
import { useState } from "react";
|
||||
|
||||
const CheckboxTwo = () => {
|
||||
const [isChecked, setIsChecked] = useState<boolean>(false);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<label
|
||||
htmlFor="checkboxLabelTwo"
|
||||
className="flex cursor-pointer select-none items-center text-body-sm font-medium"
|
||||
>
|
||||
<div className="relative">
|
||||
<input
|
||||
type="checkbox"
|
||||
id="checkboxLabelTwo"
|
||||
className="sr-only"
|
||||
onChange={() => {
|
||||
setIsChecked(!isChecked);
|
||||
}}
|
||||
/>
|
||||
<div
|
||||
className={`mr-2 flex h-5 w-5 items-center justify-center rounded border ${
|
||||
isChecked
|
||||
? "border-primary bg-gray-2 dark:bg-transparent"
|
||||
: "border border-dark-5 dark:border-dark-6"
|
||||
}`}
|
||||
>
|
||||
<span className={`opacity-0 ${isChecked && "!opacity-100"}`}>
|
||||
<svg
|
||||
width="11"
|
||||
height="8"
|
||||
viewBox="0 0 11 8"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M10.0915 0.951972L10.0867 0.946075L10.0813 0.940568C9.90076 0.753564 9.61034 0.753146 9.42927 0.939309L4.16201 6.22962L1.58507 3.63469C1.40401 3.44841 1.11351 3.44879 0.932892 3.63584C0.755703 3.81933 0.755703 4.10875 0.932892 4.29224L0.932878 4.29225L0.934851 4.29424L3.58046 6.95832C3.73676 7.11955 3.94983 7.2 4.1473 7.2C4.36196 7.2 4.55963 7.11773 4.71406 6.9584L10.0468 1.60234C10.2436 1.4199 10.2421 1.1339 10.0915 0.951972ZM4.2327 6.30081L4.2317 6.2998C4.23206 6.30015 4.23237 6.30049 4.23269 6.30082L4.2327 6.30081Z"
|
||||
fill="#5750F1"
|
||||
stroke="#5750F1"
|
||||
strokeWidth="0.4"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
Checkbox Text
|
||||
</label>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default CheckboxTwo;
|
||||
@@ -0,0 +1,38 @@
|
||||
"use client";
|
||||
|
||||
import { Calendar } from "@/components/Layouts/sidebar/icons";
|
||||
import flatpickr from "flatpickr";
|
||||
import { useEffect } from "react";
|
||||
|
||||
const DatePickerOne = () => {
|
||||
useEffect(() => {
|
||||
// Init flatpickr
|
||||
flatpickr(".form-datepicker", {
|
||||
mode: "single",
|
||||
static: true,
|
||||
monthSelectorType: "static",
|
||||
dateFormat: "M j, Y",
|
||||
});
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<label className="mb-3 block text-body-sm font-medium text-dark dark:text-white">
|
||||
Date picker
|
||||
</label>
|
||||
<div className="relative">
|
||||
<input
|
||||
className="form-datepicker w-full rounded-[7px] border-[1.5px] border-stroke bg-transparent px-5 py-3 font-normal outline-none transition focus:border-primary active:border-primary dark:border-dark-3 dark:bg-dark-2 dark:focus:border-primary"
|
||||
placeholder="mm/dd/yyyy"
|
||||
data-class="flatpickr-right"
|
||||
/>
|
||||
|
||||
<div className="pointer-events-none absolute inset-0 left-auto right-5 flex items-center">
|
||||
<Calendar className="size-5 text-[#9CA3AF]" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default DatePickerOne;
|
||||
@@ -0,0 +1,38 @@
|
||||
"use client";
|
||||
|
||||
import { ChevronUpIcon } from "@/assets/icons";
|
||||
import flatpickr from "flatpickr";
|
||||
import { useEffect } from "react";
|
||||
|
||||
const DatePickerTwo = () => {
|
||||
useEffect(() => {
|
||||
// Init flatpickr
|
||||
flatpickr(".form-datepicker", {
|
||||
mode: "single",
|
||||
static: true,
|
||||
monthSelectorType: "static",
|
||||
dateFormat: "M j, Y",
|
||||
});
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<label className="mb-3 block text-body-sm font-medium text-dark dark:text-white">
|
||||
Select date
|
||||
</label>
|
||||
<div className="relative">
|
||||
<input
|
||||
className="form-datepicker w-full rounded-[7px] border-[1.5px] border-stroke bg-transparent px-5 py-3 font-normal outline-none transition focus:border-primary active:border-primary dark:border-dark-3 dark:bg-dark-2 dark:focus:border-primary"
|
||||
placeholder="mm/dd/yyyy"
|
||||
data-class="flatpickr-right"
|
||||
/>
|
||||
|
||||
<div className="pointer-events-none absolute inset-0 left-auto right-5 flex items-center text-dark-4 dark:text-dark-6">
|
||||
<ChevronUpIcon className="rotate-180" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default DatePickerTwo;
|
||||
@@ -0,0 +1,122 @@
|
||||
import { cn } from "@/lib/utils";
|
||||
import { type HTMLInputTypeAttribute, useId } from "react";
|
||||
import {
|
||||
type FieldErrors,
|
||||
type FieldValues,
|
||||
type Path,
|
||||
type UseFormRegister,
|
||||
type UseFormRegisterReturn,
|
||||
} from "react-hook-form";
|
||||
|
||||
type InputGroupProps<T extends FieldValues> = {
|
||||
name: Path<T>;
|
||||
label: string;
|
||||
type: HTMLInputTypeAttribute;
|
||||
placeholder: string;
|
||||
className?: string;
|
||||
fileStyleVariant?: "style1" | "style2";
|
||||
required?: boolean;
|
||||
disabled?: boolean;
|
||||
active?: boolean;
|
||||
handleChange?: (e: React.ChangeEvent<HTMLInputElement>) => void;
|
||||
value?: string;
|
||||
icon?: React.ReactNode;
|
||||
iconPosition?: "left" | "right";
|
||||
height?: "sm" | "default";
|
||||
defaultValue?: string;
|
||||
register?: UseFormRegister<T>;
|
||||
errors?: FieldErrors<T>;
|
||||
} & Partial<UseFormRegisterReturn>;
|
||||
|
||||
const InputGroup = <T extends FieldValues>({
|
||||
className,
|
||||
label,
|
||||
type,
|
||||
placeholder,
|
||||
required,
|
||||
disabled,
|
||||
active,
|
||||
handleChange,
|
||||
icon,
|
||||
name,
|
||||
register,
|
||||
errors,
|
||||
|
||||
onChange,
|
||||
onBlur,
|
||||
ref,
|
||||
...props
|
||||
}: InputGroupProps<T>): React.ReactElement => {
|
||||
const id = useId();
|
||||
|
||||
const error = errors && errors[name];
|
||||
|
||||
return (
|
||||
<div className={className}>
|
||||
<label
|
||||
htmlFor={id}
|
||||
className="text-body-sm font-medium text-dark dark:text-white"
|
||||
>
|
||||
{label}
|
||||
{required && <span className="text-red ml-1 select-none">*</span>}
|
||||
</label>
|
||||
|
||||
<div
|
||||
className={cn(
|
||||
"relative mt-3 [&_svg]:absolute [&_svg]:top-1/2 [&_svg]:-translate-y-1/2",
|
||||
props.iconPosition === "left"
|
||||
? "[&_svg]:left-4.5"
|
||||
: "[&_svg]:right-4.5",
|
||||
)}
|
||||
>
|
||||
<input
|
||||
id={id}
|
||||
type={type}
|
||||
placeholder={placeholder}
|
||||
name={name}
|
||||
ref={ref}
|
||||
onChange={(e) => {
|
||||
onChange?.(e);
|
||||
handleChange?.(e);
|
||||
}}
|
||||
onBlur={onBlur}
|
||||
{...(!onChange &&
|
||||
props.value !== undefined && { value: props.value })}
|
||||
{...(!onChange &&
|
||||
props.defaultValue !== undefined && {
|
||||
defaultValue: props.defaultValue,
|
||||
})}
|
||||
className={cn(
|
||||
"w-full rounded-lg border-[1.5px] border-stroke bg-transparent outline-none transition focus:border-primary disabled:cursor-default disabled:bg-gray-2 data-[active=true]:border-primary dark:border-dark-3 dark:bg-dark-2 dark:focus:border-primary dark:disabled:bg-dark dark:data-[active=true]:border-primary",
|
||||
// Add error styles conditionally
|
||||
error && "border-red focus:border-red dark:border-red",
|
||||
type === "file"
|
||||
? getFileStyles(props.fileStyleVariant!)
|
||||
: "px-5.5 py-3 text-dark placeholder:text-dark-6 dark:text-white",
|
||||
props.iconPosition === "left" && "pl-12.5",
|
||||
props.height === "sm" && "py-2.5",
|
||||
)}
|
||||
disabled={disabled}
|
||||
data-active={active}
|
||||
/>
|
||||
|
||||
{icon}
|
||||
</div>
|
||||
|
||||
{error && (
|
||||
<p className="text-red mt-1.5 text-sm">{error.message as string}</p>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default InputGroup;
|
||||
|
||||
function getFileStyles(variant: "style1" | "style2") {
|
||||
switch (variant) {
|
||||
case "style1":
|
||||
return `file:mr-5 file:border-collapse file:cursor-pointer file:border-0 file:border-r file:border-solid file:border-stroke file:bg-[#E2E8F0] file:px-6.5 file:py-[13px] file:text-body-sm file:font-medium file:text-dark-5 file:hover:bg-primary file:hover:bg-opacity-10 dark:file:border-dark-3 dark:file:bg-white/30 dark:file:text-white`;
|
||||
default:
|
||||
return `file:mr-4 file:rounded file:border-[0.5px] file:border-stroke file:bg-stroke file:px-2.5 file:py-1 file:text-body-xs file:font-medium file:text-dark-5 file:focus:border-primary dark:file:border-dark-3 dark:file:bg-white/30 dark:file:text-white px-3 py-[9px]`;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
import { cn } from "@/lib/utils";
|
||||
import { useId } from "react";
|
||||
import {
|
||||
type FieldErrors,
|
||||
type FieldValues,
|
||||
type Path,
|
||||
type UseFormRegister,
|
||||
type UseFormRegisterReturn,
|
||||
} from "react-hook-form";
|
||||
|
||||
type PropsType<T extends FieldValues> = {
|
||||
name: Path<T>;
|
||||
label: string;
|
||||
placeholder: string;
|
||||
required?: boolean;
|
||||
disabled?: boolean;
|
||||
active?: boolean;
|
||||
className?: string;
|
||||
icon?: React.ReactNode;
|
||||
defaultValue?: string;
|
||||
handleChange?: (e: React.ChangeEvent<HTMLTextAreaElement>) => void;
|
||||
register?: UseFormRegister<T>;
|
||||
errors?: FieldErrors<T>;
|
||||
} & Partial<UseFormRegisterReturn>;
|
||||
|
||||
export function TextAreaGroup<T extends FieldValues>({
|
||||
name,
|
||||
label,
|
||||
placeholder,
|
||||
required,
|
||||
disabled,
|
||||
active,
|
||||
className,
|
||||
icon,
|
||||
defaultValue,
|
||||
handleChange,
|
||||
register,
|
||||
errors,
|
||||
onChange,
|
||||
onBlur,
|
||||
ref,
|
||||
...props
|
||||
}: PropsType<T>) {
|
||||
const id = useId();
|
||||
|
||||
const error = errors && errors[name];
|
||||
|
||||
return (
|
||||
<div className={cn(className)}>
|
||||
<label
|
||||
htmlFor={id}
|
||||
className="mb-3 block text-body-sm font-medium text-dark dark:text-white"
|
||||
>
|
||||
{label}
|
||||
{required && <span className="text-red ml-1 select-none">*</span>}
|
||||
</label>
|
||||
|
||||
<div className="relative mt-3 [&_svg]:pointer-events-none [&_svg]:absolute [&_svg]:left-5.5 [&_svg]:top-5.5">
|
||||
<textarea
|
||||
id={id}
|
||||
name={name}
|
||||
ref={ref}
|
||||
rows={6}
|
||||
placeholder={placeholder}
|
||||
defaultValue={defaultValue}
|
||||
onChange={(e) => {
|
||||
onChange?.(e);
|
||||
handleChange?.(e);
|
||||
}}
|
||||
onBlur={onBlur}
|
||||
className={cn(
|
||||
"w-full rounded-lg border-[1.5px] border-stroke bg-transparent px-5.5 py-3 text-dark outline-none transition focus:border-primary disabled:cursor-default disabled:bg-gray-2 data-[active=true]:border-primary dark:border-dark-3 dark:bg-dark-2 dark:text-white dark:focus:border-primary dark:disabled:bg-dark dark:data-[active=true]:border-primary",
|
||||
error && "border-red focus:border-red dark:border-red",
|
||||
icon && "py-5 pl-13 pr-5",
|
||||
)}
|
||||
required={required}
|
||||
disabled={disabled}
|
||||
data-active={active}
|
||||
/>
|
||||
|
||||
{icon}
|
||||
</div>
|
||||
|
||||
{error && (
|
||||
<p className="text-red mt-1.5 text-sm">{error.message as string}</p>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,241 @@
|
||||
"use client";
|
||||
|
||||
import React, { useEffect, useRef, useState, useId } from "react";
|
||||
import {
|
||||
type FieldErrors,
|
||||
type FieldValues,
|
||||
type Path,
|
||||
type UseFormRegisterReturn,
|
||||
} from "react-hook-form";
|
||||
|
||||
interface Option {
|
||||
value: string;
|
||||
text: string;
|
||||
selected: boolean;
|
||||
}
|
||||
|
||||
type MultiSelectProps<T extends FieldValues> = {
|
||||
label: string;
|
||||
items: { value: string; text: string }[];
|
||||
errors?: FieldErrors<T>;
|
||||
required?: boolean;
|
||||
placeholder?: string;
|
||||
className?: string;
|
||||
} & UseFormRegisterReturn<Path<T>>;
|
||||
|
||||
function MultiSelect<T extends FieldValues>({
|
||||
name,
|
||||
label,
|
||||
items,
|
||||
errors,
|
||||
required,
|
||||
placeholder = "Select an option",
|
||||
className,
|
||||
onChange,
|
||||
onBlur,
|
||||
ref,
|
||||
}: MultiSelectProps<T>) {
|
||||
const id = useId();
|
||||
const error = errors && errors[name];
|
||||
|
||||
const [options, setOptions] = useState<Option[]>([]);
|
||||
const [selected, setSelected] = useState<number[]>([]);
|
||||
const [show, setShow] = useState(false);
|
||||
const dropdownRef = useRef<HTMLDivElement>(null);
|
||||
const trigger = useRef<HTMLDivElement | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
setOptions(
|
||||
items.map((item) => ({
|
||||
...item,
|
||||
selected: false,
|
||||
})),
|
||||
);
|
||||
}, [items]);
|
||||
|
||||
const open = () => {
|
||||
setShow(true);
|
||||
};
|
||||
|
||||
const isOpen = () => {
|
||||
return show === true;
|
||||
};
|
||||
|
||||
const combinedRef = (el: HTMLDivElement) => {
|
||||
trigger.current = el;
|
||||
if (typeof ref === "function") {
|
||||
ref(el);
|
||||
}
|
||||
};
|
||||
|
||||
const handleSelect = (index: number) => {
|
||||
const newOptions = [...options];
|
||||
let newSelected = [...selected];
|
||||
|
||||
if (!newOptions[index].selected) {
|
||||
newOptions[index].selected = true;
|
||||
newSelected.push(index);
|
||||
} else {
|
||||
newOptions[index].selected = false;
|
||||
newSelected = newSelected.filter((i) => i !== index);
|
||||
}
|
||||
|
||||
setOptions(newOptions);
|
||||
setSelected(newSelected);
|
||||
|
||||
const selectedValues = newSelected.map((i) => options[i].value);
|
||||
onChange?.({ target: { name, value: selectedValues } });
|
||||
};
|
||||
|
||||
const remove = (index: number, event: React.MouseEvent) => {
|
||||
event.stopPropagation();
|
||||
const newOptions = [...options];
|
||||
const newSelected = selected.filter((i) => i !== index);
|
||||
newOptions[index].selected = false;
|
||||
|
||||
setOptions(newOptions);
|
||||
setSelected(newSelected);
|
||||
|
||||
const selectedValues = newSelected.map((i) => options[i].value);
|
||||
onChange?.({ target: { name, value: selectedValues } });
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
const clickHandler = ({ target }: MouseEvent) => {
|
||||
if (!dropdownRef.current) return;
|
||||
if (
|
||||
!show ||
|
||||
dropdownRef.current.contains(target as Node) ||
|
||||
trigger.current?.contains(target as Node)
|
||||
)
|
||||
return;
|
||||
setShow(false);
|
||||
};
|
||||
document.addEventListener("click", clickHandler);
|
||||
return () => document.removeEventListener("click", clickHandler);
|
||||
}, [show]);
|
||||
|
||||
return (
|
||||
<div className={`relative z-50 ${className}`}>
|
||||
<label
|
||||
htmlFor={id}
|
||||
className="mb-3 block text-body-sm font-medium text-dark dark:text-white"
|
||||
>
|
||||
{label}
|
||||
</label>
|
||||
|
||||
<div className="relative">
|
||||
<div
|
||||
ref={combinedRef}
|
||||
onClick={open}
|
||||
onBlur={onBlur}
|
||||
tabIndex={0}
|
||||
className={`flex min-h-[46px] w-full cursor-pointer items-center rounded-[7px] border-[1.5px] bg-transparent py-2 pl-3 pr-3 outline-none transition focus:border-primary active:border-primary dark:bg-dark-2 ${
|
||||
error
|
||||
? "border-red focus:border-red dark:border-red"
|
||||
: "border-stroke dark:border-dark-3"
|
||||
}`}
|
||||
>
|
||||
<div className="flex flex-auto flex-wrap gap-2">
|
||||
{selected.map((index) => (
|
||||
<div
|
||||
key={index}
|
||||
className="flex items-center justify-center rounded-[5px] border-[.5px] border-stroke bg-gray-2 px-2.5 py-1 text-body-sm font-medium dark:border-dark-3 dark:bg-dark"
|
||||
>
|
||||
<div className="max-w-full flex-initial">
|
||||
{options[index]?.text}
|
||||
</div>
|
||||
<div className="flex flex-auto flex-row-reverse">
|
||||
<div
|
||||
onClick={(e) => remove(index, e)}
|
||||
className="hover:text-red cursor-pointer pl-2"
|
||||
>
|
||||
<svg
|
||||
className="fill-current"
|
||||
role="button"
|
||||
width="12"
|
||||
height="12"
|
||||
viewBox="0 0 12 12"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
fillRule="evenodd"
|
||||
clipRule="evenodd"
|
||||
d="M9.35355 3.35355C9.54882 3.15829 9.54882 2.84171 9.35355 2.64645C9.15829 2.45118 8.84171 2.45118 8.64645 2.64645L6 5.29289L3.35355 2.64645C3.15829 2.45118 2.84171 2.45118 2.64645 2.64645C2.45118 2.84171 2.45118 3.15829 2.64645 3.35355L5.29289 6L2.64645 8.64645C2.45118 8.84171 2.45118 9.15829 2.64645 9.35355C2.84171 9.54882 3.15829 9.54882 3.35355 9.35355L6 6.70711L8.64645 9.35355C8.84171 9.54882 9.15829 9.54882 9.35355 9.35355C9.54882 9.15829 9.54882 8.84171 9.35355 8.64645L6.70711 6L9.35355 3.35355Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
{selected.length === 0 && (
|
||||
<div className="flex-1">
|
||||
<span className="h-full w-full bg-transparent p-1 px-2 text-dark-5 outline-none dark:text-dark-6">
|
||||
{placeholder}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div className="flex items-center py-1 pl-1 pr-1">
|
||||
<button
|
||||
type="button"
|
||||
className="cursor-pointer text-dark-4 outline-none focus:outline-none dark:text-dark-6"
|
||||
>
|
||||
<svg
|
||||
className={`fill-current transition-transform duration-200 ${
|
||||
isOpen() ? "rotate-180" : ""
|
||||
}`}
|
||||
width="20"
|
||||
height="20"
|
||||
viewBox="0 0 20 20"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
fillRule="evenodd"
|
||||
clipRule="evenodd"
|
||||
d="M3.69149 7.09327C3.91613 6.83119 4.31069 6.80084 4.57277 7.02548L9.99936 11.6768L15.4259 7.02548C15.688 6.80084 16.0826 6.83119 16.3072 7.09327C16.5319 7.35535 16.5015 7.74991 16.2394 7.97455L10.4061 12.9745C10.172 13.1752 9.82667 13.1752 9.59261 12.9745L3.75928 7.97455C3.4972 7.74991 3.46685 7.35535 3.69149 7.09327Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
className={`max-h-select absolute left-0 top-full z-40 w-full overflow-y-auto rounded bg-white shadow-1 dark:bg-dark-2 dark:shadow-card ${
|
||||
isOpen() ? "" : "hidden"
|
||||
}`}
|
||||
ref={dropdownRef}
|
||||
>
|
||||
<div className="flex w-full flex-col">
|
||||
{options.map((option, index) => (
|
||||
<div key={index}>
|
||||
<div
|
||||
className="w-full cursor-pointer rounded-t border-b border-stroke hover:bg-primary/5 dark:border-dark-3"
|
||||
onClick={() => handleSelect(index)}
|
||||
>
|
||||
<div
|
||||
className={`relative flex w-full items-center border-l-2 p-2 pl-2 ${
|
||||
option.selected ? "border-primary" : "border-transparent"
|
||||
}`}
|
||||
>
|
||||
<div className="flex w-full items-center">
|
||||
<div className="mx-2 leading-6">{option.text}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{error && (
|
||||
<p className="text-red mt-1.5 text-sm">{error.message as string}</p>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
export default MultiSelect;
|
||||
@@ -0,0 +1,33 @@
|
||||
import { useState } from "react";
|
||||
|
||||
const SwitcherFour = () => {
|
||||
const [enabled, setEnabled] = useState<boolean>(false);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<label
|
||||
htmlFor="toggle4"
|
||||
className="flex cursor-pointer select-none items-center"
|
||||
>
|
||||
<div className="relative">
|
||||
<input
|
||||
type="checkbox"
|
||||
id="toggle4"
|
||||
className="sr-only"
|
||||
onChange={() => {
|
||||
setEnabled(!enabled);
|
||||
}}
|
||||
/>
|
||||
<div className="block h-8 w-14 rounded-full bg-[#212B36] dark:bg-primary"></div>
|
||||
<div
|
||||
className={`absolute left-1 top-1 flex h-6 w-6 items-center justify-center rounded-full bg-white transition ${
|
||||
enabled && "!right-1 !translate-x-full"
|
||||
}`}
|
||||
></div>
|
||||
</div>
|
||||
</label>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default SwitcherFour;
|
||||
@@ -0,0 +1,33 @@
|
||||
import { useState } from "react";
|
||||
|
||||
const SwitcherOne = () => {
|
||||
const [enabled, setEnabled] = useState<boolean>(false);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<label
|
||||
htmlFor="toggle1"
|
||||
className="flex cursor-pointer select-none items-center"
|
||||
>
|
||||
<div className="relative">
|
||||
<input
|
||||
type="checkbox"
|
||||
id="toggle1"
|
||||
className="sr-only"
|
||||
onChange={() => {
|
||||
setEnabled(!enabled);
|
||||
}}
|
||||
/>
|
||||
<div className="block h-8 w-14 rounded-full bg-gray-3 dark:bg-[#5A616B]"></div>
|
||||
<div
|
||||
className={`absolute left-1 top-1 h-6 w-6 rounded-full bg-white shadow-switch-1 transition ${
|
||||
enabled && "!right-1 !translate-x-full !bg-primary dark:!bg-white"
|
||||
}`}
|
||||
></div>
|
||||
</div>
|
||||
</label>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default SwitcherOne;
|
||||
@@ -0,0 +1,75 @@
|
||||
import { useState } from "react";
|
||||
|
||||
const SwitcherThree = () => {
|
||||
const [enabled, setEnabled] = useState(false);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<label
|
||||
htmlFor="toggle3"
|
||||
className="flex cursor-pointer select-none items-center"
|
||||
>
|
||||
<div className="relative">
|
||||
<input
|
||||
type="checkbox"
|
||||
id="toggle3"
|
||||
className="sr-only"
|
||||
onChange={() => {
|
||||
setEnabled(!enabled);
|
||||
}}
|
||||
/>
|
||||
<div className="block h-8 w-14 rounded-full bg-gray-3 dark:bg-[#5A616B]"></div>
|
||||
<div
|
||||
className={`dot absolute left-1 top-1 flex h-6 w-6 items-center justify-center rounded-full bg-white shadow-switch-1 transition ${
|
||||
enabled && "!right-1 !translate-x-full !bg-primary dark:!bg-white"
|
||||
}`}
|
||||
>
|
||||
<span className={`hidden ${enabled && "!block"}`}>
|
||||
<svg
|
||||
className="fill-white dark:fill-dark"
|
||||
width="11"
|
||||
height="8"
|
||||
viewBox="0 0 11 8"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
fillRule="evenodd"
|
||||
clipRule="evenodd"
|
||||
d="M10.2355 0.812752L10.2452 0.824547C10.4585 1.08224 10.4617 1.48728 10.1855 1.74621L4.85633 7.09869C4.66442 7.29617 4.41535 7.4001 4.14693 7.4001C3.89823 7.4001 3.63296 7.29979 3.43735 7.09851L0.788615 4.43129C0.536589 4.1703 0.536617 3.758 0.788643 3.49701C1.04747 3.22897 1.4675 3.22816 1.72731 3.49457L4.16182 5.94608L9.28643 0.799032C9.54626 0.532887 9.96609 0.533789 10.2248 0.801737L10.2355 0.812752Z"
|
||||
fill=""
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
<span className={`${enabled && "hidden"}`}>
|
||||
<svg
|
||||
className="fill-current"
|
||||
width="11"
|
||||
height="11"
|
||||
viewBox="0 0 11 11"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<g clipPath="url(#clip0_803_2686)">
|
||||
<path
|
||||
fillRule="evenodd"
|
||||
clipRule="evenodd"
|
||||
d="M1.23529 2.29669C0.942402 2.00379 0.942402 1.52892 1.23529 1.23603C1.52819 0.943134 2.00306 0.943134 2.29596 1.23603L5.37433 4.3144L8.45261 1.23612C8.7455 0.943225 9.22038 0.943225 9.51327 1.23612C9.80616 1.52901 9.80616 2.00389 9.51327 2.29678L6.43499 5.37506L9.51327 8.45334C9.80616 8.74624 9.80616 9.22111 9.51327 9.514C9.22038 9.8069 8.7455 9.8069 8.45261 9.514L5.37433 6.43572L2.29596 9.51409C2.00306 9.80699 1.52819 9.80699 1.23529 9.51409C0.942402 9.2212 0.942402 8.74633 1.23529 8.45343L4.31367 5.37506L1.23529 2.29669Z"
|
||||
fill=""
|
||||
/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_803_2686">
|
||||
<rect width="10.75" height="10.75" fill="white" />
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</label>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default SwitcherThree;
|
||||
@@ -0,0 +1,33 @@
|
||||
import { useState } from "react";
|
||||
|
||||
const SwitcherTwo = () => {
|
||||
const [enabled, setEnabled] = useState(false);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<label
|
||||
htmlFor="toggle2"
|
||||
className="flex cursor-pointer select-none items-center"
|
||||
>
|
||||
<div className="relative">
|
||||
<input
|
||||
id="toggle2"
|
||||
type="checkbox"
|
||||
className="sr-only"
|
||||
onChange={() => {
|
||||
setEnabled(!enabled);
|
||||
}}
|
||||
/>
|
||||
<div className="h-5 w-14 rounded-full bg-gray-3 dark:bg-[#5A616B]"></div>
|
||||
<div
|
||||
className={`dot shadow-switch-2 absolute -top-1 left-0 h-7 w-7 rounded-full bg-white transition ${
|
||||
enabled && "!right-0 !translate-x-full !bg-primary dark:!bg-white"
|
||||
}`}
|
||||
></div>
|
||||
</div>
|
||||
</label>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default SwitcherTwo;
|
||||
@@ -0,0 +1,69 @@
|
||||
import { CheckIcon, XIcon } from "@/assets/icons";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { useId } from "react";
|
||||
|
||||
type PropsType = {
|
||||
withIcon?: "check" | "x";
|
||||
withBg?: boolean;
|
||||
label: string;
|
||||
name?: string;
|
||||
minimal?: boolean;
|
||||
onChange?: (e: React.ChangeEvent<HTMLInputElement>) => void;
|
||||
radius?: "default" | "md";
|
||||
};
|
||||
|
||||
export function Checkbox({
|
||||
withIcon,
|
||||
label,
|
||||
name,
|
||||
withBg,
|
||||
minimal,
|
||||
onChange,
|
||||
radius,
|
||||
}: PropsType) {
|
||||
const id = useId();
|
||||
|
||||
return (
|
||||
<div>
|
||||
<label
|
||||
htmlFor={id}
|
||||
className={cn(
|
||||
"flex cursor-pointer select-none items-center",
|
||||
!minimal && "text-body-sm font-medium",
|
||||
)}
|
||||
>
|
||||
<div className="relative">
|
||||
<input
|
||||
type="checkbox"
|
||||
onChange={onChange}
|
||||
name={name}
|
||||
id={id}
|
||||
className="peer sr-only"
|
||||
/>
|
||||
|
||||
<div
|
||||
className={cn(
|
||||
"mr-2 flex size-5 items-center justify-center rounded border border-dark-5 peer-checked:border-primary dark:border-dark-6 peer-checked:[&>*]:block",
|
||||
withBg
|
||||
? "peer-checked:bg-primary [&>*]:text-white"
|
||||
: "peer-checked:bg-gray-2 dark:peer-checked:bg-transparent",
|
||||
minimal && "mr-3 border-stroke dark:border-dark-3",
|
||||
radius === "md" && "rounded-md",
|
||||
)}
|
||||
>
|
||||
{!withIcon && (
|
||||
<span className="hidden size-2.5 rounded-sm bg-primary" />
|
||||
)}
|
||||
|
||||
{withIcon === "check" && (
|
||||
<CheckIcon className="hidden text-primary" />
|
||||
)}
|
||||
|
||||
{withIcon === "x" && <XIcon className="hidden text-primary" />}
|
||||
</div>
|
||||
</div>
|
||||
<span>{label}</span>
|
||||
</label>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
import { cn } from "@/lib/utils";
|
||||
import { useId } from "react";
|
||||
|
||||
type PropsType = {
|
||||
variant?: "dot" | "circle";
|
||||
label: string;
|
||||
name?: string;
|
||||
value?: string;
|
||||
minimal?: boolean;
|
||||
};
|
||||
|
||||
export function RadioInput({
|
||||
label,
|
||||
variant = "dot",
|
||||
name,
|
||||
value,
|
||||
minimal,
|
||||
}: PropsType) {
|
||||
const id = useId();
|
||||
|
||||
return (
|
||||
<div>
|
||||
<label
|
||||
htmlFor={id}
|
||||
className="flex cursor-pointer select-none items-center text-body-sm font-medium text-dark dark:text-white"
|
||||
>
|
||||
<div className="relative">
|
||||
<input
|
||||
type="radio"
|
||||
name={name}
|
||||
id={id}
|
||||
className="peer sr-only"
|
||||
value={value}
|
||||
/>
|
||||
<div
|
||||
className={cn(
|
||||
"mr-2 flex size-5 items-center justify-center rounded-full border peer-checked:[&>*]:block",
|
||||
{
|
||||
"border-primary peer-checked:border-6": variant === "circle",
|
||||
"border-dark-5 peer-checked:border-primary peer-checked:bg-gray-2 dark:border-dark-6 dark:peer-checked:bg-dark-2":
|
||||
variant === "dot",
|
||||
},
|
||||
minimal && "border-stroke dark:border-dark-3",
|
||||
)}
|
||||
>
|
||||
<span
|
||||
className={cn(
|
||||
"hidden size-2.5 rounded-full bg-primary",
|
||||
variant === "circle" && "bg-transparent",
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<span>{label}</span>
|
||||
</label>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
"use client";
|
||||
|
||||
import { ChevronUpIcon } from "@/assets/icons";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { useId, useState } from "react";
|
||||
import {
|
||||
type FieldErrors,
|
||||
type FieldValues,
|
||||
type Path,
|
||||
type UseFormRegister,
|
||||
type UseFormRegisterReturn,
|
||||
} from "react-hook-form";
|
||||
|
||||
type PropsType<T extends FieldValues> = {
|
||||
name: Path<T>;
|
||||
label: string;
|
||||
items: { value: string; label: string }[];
|
||||
prefixIcon?: React.ReactNode;
|
||||
className?: string;
|
||||
register?: UseFormRegister<T>;
|
||||
errors?: FieldErrors<T>;
|
||||
required?: boolean;
|
||||
} & (
|
||||
| { placeholder?: string; defaultValue: string }
|
||||
| { placeholder: string; defaultValue?: string }
|
||||
) &
|
||||
Partial<UseFormRegisterReturn>;
|
||||
|
||||
export function Select<T extends FieldValues>({
|
||||
items,
|
||||
label,
|
||||
defaultValue,
|
||||
placeholder,
|
||||
prefixIcon,
|
||||
className,
|
||||
name,
|
||||
register,
|
||||
errors,
|
||||
required,
|
||||
onChange,
|
||||
onBlur,
|
||||
ref,
|
||||
...props
|
||||
}: PropsType<T>) {
|
||||
const id = useId();
|
||||
|
||||
const [isOptionSelected, setIsOptionSelected] = useState(!!defaultValue);
|
||||
const error = errors && errors[name];
|
||||
|
||||
return (
|
||||
<div className={cn("space-y-3", className)}>
|
||||
<label
|
||||
htmlFor={id}
|
||||
className="block text-body-sm font-medium text-dark dark:text-white"
|
||||
>
|
||||
{label}
|
||||
{required && <span className="text-red ml-1 select-none">*</span>}
|
||||
</label>
|
||||
|
||||
<div className="relative">
|
||||
{prefixIcon && (
|
||||
<div className="absolute left-4 top-1/2 -translate-y-1/2">
|
||||
{prefixIcon}
|
||||
</div>
|
||||
)}
|
||||
|
||||
<select
|
||||
id={id}
|
||||
name={name}
|
||||
ref={ref}
|
||||
defaultValue={defaultValue || ""}
|
||||
onChange={(e) => {
|
||||
setIsOptionSelected(!!e.target.value);
|
||||
onChange?.(e);
|
||||
}}
|
||||
onBlur={onBlur}
|
||||
className={cn(
|
||||
"w-full appearance-none rounded-lg border border-stroke bg-transparent px-5.5 py-3 outline-none transition focus:border-primary active:border-primary dark:border-dark-3 dark:bg-dark-2 dark:focus:border-primary [&>option]:text-dark-5 dark:[&>option]:text-dark-6",
|
||||
isOptionSelected && "text-dark dark:text-white",
|
||||
error && "border-red focus:border-red dark:border-red",
|
||||
prefixIcon && "pl-11.5",
|
||||
)}
|
||||
>
|
||||
{placeholder && (
|
||||
<option value="" disabled hidden>
|
||||
{placeholder}
|
||||
</option>
|
||||
)}
|
||||
|
||||
{items.map((item) => (
|
||||
<option key={item.value} value={item.value}>
|
||||
{item.label}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
|
||||
<ChevronUpIcon className="pointer-events-none absolute right-4 top-1/2 -translate-y-1/2 rotate-180" />
|
||||
</div>
|
||||
|
||||
{error && (
|
||||
<p className="text-red mt-1.5 text-sm">{error.message as string}</p>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
import { CheckIcon, XIcon } from "@/assets/icons";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { useId } from "react";
|
||||
|
||||
type PropsType = {
|
||||
withIcon?: boolean;
|
||||
background?: "dark" | "light";
|
||||
backgroundSize?: "sm" | "default";
|
||||
name?: string;
|
||||
};
|
||||
|
||||
export function Switch({
|
||||
background,
|
||||
withIcon,
|
||||
backgroundSize,
|
||||
name,
|
||||
}: PropsType) {
|
||||
const id = useId();
|
||||
|
||||
return (
|
||||
<label
|
||||
htmlFor={id}
|
||||
className="flex max-w-fit cursor-pointer select-none items-center"
|
||||
>
|
||||
<div className="relative">
|
||||
<input type="checkbox" name={name} id={id} className="peer sr-only" />
|
||||
<div
|
||||
className={cn("h-8 w-14 rounded-full bg-gray-3 dark:bg-[#5A616B]", {
|
||||
"h-5": backgroundSize === "sm",
|
||||
"bg-[#212B36] dark:bg-primary": background === "dark",
|
||||
})}
|
||||
/>
|
||||
|
||||
<div
|
||||
className={cn(
|
||||
"absolute left-1 top-1 flex size-6 items-center justify-center rounded-full bg-white shadow-switch-1 transition peer-checked:right-1 peer-checked:translate-x-full peer-checked:[&_.check-icon]:block peer-checked:[&_.x-icon]:hidden",
|
||||
{
|
||||
"-top-1 left-0 size-7 shadow-switch-2": backgroundSize === "sm",
|
||||
"peer-checked:bg-primary peer-checked:dark:bg-white":
|
||||
background !== "dark",
|
||||
},
|
||||
)}
|
||||
>
|
||||
{withIcon && (
|
||||
<>
|
||||
<CheckIcon className="check-icon hidden fill-white dark:fill-dark" />
|
||||
<XIcon className="x-icon" />
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</label>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user