Updates
This commit is contained in:
		
							parent
							
								
									f0850146be
								
							
						
					
					
						commit
						442ad5aa32
					
				
							
								
								
									
										0
									
								
								components/lib/editors/AceEditor.tsx
									
									
									
									
									
										
										
										Executable file → Normal file
									
								
							
							
						
						
									
										0
									
								
								components/lib/editors/AceEditor.tsx
									
									
									
									
									
										
										
										Executable file → Normal file
									
								
							@ -17,7 +17,7 @@ export default function Border({ spacing, ...props }: TWUI_BORDER_PROPS) {
 | 
				
			|||||||
        <div
 | 
					        <div
 | 
				
			||||||
            {...props}
 | 
					            {...props}
 | 
				
			||||||
            className={twMerge(
 | 
					            className={twMerge(
 | 
				
			||||||
                "relative flex items-center gap-2 border rounded",
 | 
					                "relative flex items-center gap-2 border border-solid rounded",
 | 
				
			||||||
                "border-slate-300 dark:border-white/10",
 | 
					                "border-slate-300 dark:border-white/10",
 | 
				
			||||||
                spacing
 | 
					                spacing
 | 
				
			||||||
                    ? spacing == "normal"
 | 
					                    ? spacing == "normal"
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										0
									
								
								components/lib/elements/Breadcrumbs.tsx
									
									
									
									
									
										
										
										Executable file → Normal file
									
								
							
							
						
						
									
										0
									
								
								components/lib/elements/Breadcrumbs.tsx
									
									
									
									
									
										
										
										Executable file → Normal file
									
								
							@ -1,6 +1,19 @@
 | 
				
			|||||||
import React, { DetailedHTMLProps, HTMLAttributes } from "react";
 | 
					import React, { DetailedHTMLProps, HTMLAttributes } from "react";
 | 
				
			||||||
import { twMerge } from "tailwind-merge";
 | 
					import { twMerge } from "tailwind-merge";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type Props = DetailedHTMLProps<
 | 
				
			||||||
 | 
					    HTMLAttributes<HTMLDivElement>,
 | 
				
			||||||
 | 
					    HTMLDivElement
 | 
				
			||||||
 | 
					> & {
 | 
				
			||||||
 | 
					    variant?: "normal";
 | 
				
			||||||
 | 
					    href?: string;
 | 
				
			||||||
 | 
					    linkProps?: DetailedHTMLProps<
 | 
				
			||||||
 | 
					        React.AnchorHTMLAttributes<HTMLAnchorElement>,
 | 
				
			||||||
 | 
					        HTMLAnchorElement
 | 
				
			||||||
 | 
					    >;
 | 
				
			||||||
 | 
					    noHover?: boolean;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * # General Card
 | 
					 * # General Card
 | 
				
			||||||
 * @className twui-card
 | 
					 * @className twui-card
 | 
				
			||||||
@ -13,22 +26,18 @@ export default function Card({
 | 
				
			|||||||
    href,
 | 
					    href,
 | 
				
			||||||
    variant,
 | 
					    variant,
 | 
				
			||||||
    linkProps,
 | 
					    linkProps,
 | 
				
			||||||
 | 
					    noHover,
 | 
				
			||||||
    ...props
 | 
					    ...props
 | 
				
			||||||
}: DetailedHTMLProps<HTMLAttributes<HTMLDivElement>, HTMLDivElement> & {
 | 
					}: Props) {
 | 
				
			||||||
    variant?: "normal";
 | 
					 | 
				
			||||||
    href?: string;
 | 
					 | 
				
			||||||
    linkProps?: DetailedHTMLProps<
 | 
					 | 
				
			||||||
        React.AnchorHTMLAttributes<HTMLAnchorElement>,
 | 
					 | 
				
			||||||
        HTMLAnchorElement
 | 
					 | 
				
			||||||
    >;
 | 
					 | 
				
			||||||
}) {
 | 
					 | 
				
			||||||
    const component = (
 | 
					    const component = (
 | 
				
			||||||
        <div
 | 
					        <div
 | 
				
			||||||
            {...props}
 | 
					            {...props}
 | 
				
			||||||
            className={twMerge(
 | 
					            className={twMerge(
 | 
				
			||||||
                "flex flex-row items-center p-4 rounded bg-white dark:bg-white/10",
 | 
					                "flex flex-row items-center p-4 rounded bg-white dark:bg-white/10",
 | 
				
			||||||
                "border border-slate-200 dark:border-white/10 border-solid",
 | 
					                "border border-slate-200 dark:border-white/10 border-solid",
 | 
				
			||||||
                href
 | 
					                noHover
 | 
				
			||||||
 | 
					                    ? ""
 | 
				
			||||||
 | 
					                    : href
 | 
				
			||||||
                    ? "hover:bg-slate-100 dark:hover:bg-white/30 hover:border-slate-400 dark:hover:border-white/20"
 | 
					                    ? "hover:bg-slate-100 dark:hover:bg-white/30 hover:border-slate-400 dark:hover:border-white/20"
 | 
				
			||||||
                    : "",
 | 
					                    : "",
 | 
				
			||||||
                "twui-card",
 | 
					                "twui-card",
 | 
				
			||||||
 | 
				
			|||||||
@ -24,6 +24,7 @@ export type TWUI_DROPDOWN_PROPS = PropsWithChildren &
 | 
				
			|||||||
        >;
 | 
					        >;
 | 
				
			||||||
        debounce?: number;
 | 
					        debounce?: number;
 | 
				
			||||||
        hoverOpen?: boolean;
 | 
					        hoverOpen?: boolean;
 | 
				
			||||||
 | 
					        above?: boolean;
 | 
				
			||||||
        position?: (typeof TWUIDropdownContentPositions)[number];
 | 
					        position?: (typeof TWUIDropdownContentPositions)[number];
 | 
				
			||||||
        topOffset?: number;
 | 
					        topOffset?: number;
 | 
				
			||||||
        externalSetOpen?: React.Dispatch<React.SetStateAction<boolean>>;
 | 
					        externalSetOpen?: React.Dispatch<React.SetStateAction<boolean>>;
 | 
				
			||||||
@ -41,6 +42,7 @@ export default function Dropdown({
 | 
				
			|||||||
    contentWrapperProps,
 | 
					    contentWrapperProps,
 | 
				
			||||||
    targetWrapperProps,
 | 
					    targetWrapperProps,
 | 
				
			||||||
    hoverOpen,
 | 
					    hoverOpen,
 | 
				
			||||||
 | 
					    above,
 | 
				
			||||||
    debounce = 500,
 | 
					    debounce = 500,
 | 
				
			||||||
    target,
 | 
					    target,
 | 
				
			||||||
    position = "center",
 | 
					    position = "center",
 | 
				
			||||||
@ -122,6 +124,7 @@ export default function Dropdown({
 | 
				
			|||||||
                        : position == "right"
 | 
					                        : position == "right"
 | 
				
			||||||
                        ? "right-0"
 | 
					                        ? "right-0"
 | 
				
			||||||
                        : "",
 | 
					                        : "",
 | 
				
			||||||
 | 
					                    above ? "-translate-y-[120%]" : "",
 | 
				
			||||||
                    open ? "flex" : "hidden",
 | 
					                    open ? "flex" : "hidden",
 | 
				
			||||||
                    "twui-dropdown-content",
 | 
					                    "twui-dropdown-content",
 | 
				
			||||||
                    contentWrapperProps?.className
 | 
					                    contentWrapperProps?.className
 | 
				
			||||||
 | 
				
			|||||||
@ -15,6 +15,7 @@ export type TWUI_TOGGLE_PROPS = PropsWithChildren &
 | 
				
			|||||||
    > & {
 | 
					    > & {
 | 
				
			||||||
        color?: "normal" | "secondary" | "error" | "success" | "gray";
 | 
					        color?: "normal" | "secondary" | "error" | "success" | "gray";
 | 
				
			||||||
        variant?: "normal" | "outlined" | "ghost";
 | 
					        variant?: "normal" | "outlined" | "ghost";
 | 
				
			||||||
 | 
					        href?: string;
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
@ -25,13 +26,15 @@ export default function Tag({
 | 
				
			|||||||
    color,
 | 
					    color,
 | 
				
			||||||
    variant,
 | 
					    variant,
 | 
				
			||||||
    children,
 | 
					    children,
 | 
				
			||||||
 | 
					    href,
 | 
				
			||||||
    ...props
 | 
					    ...props
 | 
				
			||||||
}: TWUI_TOGGLE_PROPS) {
 | 
					}: TWUI_TOGGLE_PROPS) {
 | 
				
			||||||
    return (
 | 
					    const mainComponent = (
 | 
				
			||||||
        <div
 | 
					        <div
 | 
				
			||||||
            {...props}
 | 
					            {...props}
 | 
				
			||||||
            className={twMerge(
 | 
					            className={twMerge(
 | 
				
			||||||
                "text-xs px-2 py-0.5 rounded-full outline outline-0",
 | 
					                "text-xs px-2 py-0.5 rounded-full outline outline-0",
 | 
				
			||||||
 | 
					                "text-center flex items-center justify-center",
 | 
				
			||||||
                color == "secondary"
 | 
					                color == "secondary"
 | 
				
			||||||
                    ? "bg-violet-600 outline-violet-600"
 | 
					                    ? "bg-violet-600 outline-violet-600"
 | 
				
			||||||
                    : color == "success"
 | 
					                    : color == "success"
 | 
				
			||||||
@ -63,7 +66,7 @@ export default function Tag({
 | 
				
			|||||||
                          : color == "gray"
 | 
					                          : color == "gray"
 | 
				
			||||||
                          ? "text-slate-700 dark:text-white/80"
 | 
					                          ? "text-slate-700 dark:text-white/80"
 | 
				
			||||||
                          : "text-blue-600")
 | 
					                          : "text-blue-600")
 | 
				
			||||||
                    : "",
 | 
					                    : "text-white",
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                "twui-tag",
 | 
					                "twui-tag",
 | 
				
			||||||
                props.className
 | 
					                props.className
 | 
				
			||||||
@ -72,4 +75,14 @@ export default function Tag({
 | 
				
			|||||||
            {children}
 | 
					            {children}
 | 
				
			||||||
        </div>
 | 
					        </div>
 | 
				
			||||||
    );
 | 
					    );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (href) {
 | 
				
			||||||
 | 
					        return (
 | 
				
			||||||
 | 
					            <a href={href} className={twMerge("hover:opacity-80")}>
 | 
				
			||||||
 | 
					                {mainComponent}
 | 
				
			||||||
 | 
					            </a>
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return mainComponent;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -27,6 +27,7 @@ export default function Form<T extends object = { [key: string]: any }>({
 | 
				
			|||||||
                const formData = new FormData(formEl);
 | 
					                const formData = new FormData(formEl);
 | 
				
			||||||
                const data = Object.fromEntries(formData.entries()) as T;
 | 
					                const data = Object.fromEntries(formData.entries()) as T;
 | 
				
			||||||
                props.submitHandler?.(e, data);
 | 
					                props.submitHandler?.(e, data);
 | 
				
			||||||
 | 
					                props.onSubmit?.(e);
 | 
				
			||||||
            }}
 | 
					            }}
 | 
				
			||||||
        >
 | 
					        >
 | 
				
			||||||
            {props.children}
 | 
					            {props.children}
 | 
				
			||||||
 | 
				
			|||||||
@ -14,7 +14,9 @@ type ImageUploadProps = DetailedHTMLProps<
 | 
				
			|||||||
    React.HTMLAttributes<HTMLDivElement>,
 | 
					    React.HTMLAttributes<HTMLDivElement>,
 | 
				
			||||||
    HTMLDivElement
 | 
					    HTMLDivElement
 | 
				
			||||||
> & {
 | 
					> & {
 | 
				
			||||||
    onChange?: (imgData: ImageInputToBase64FunctionReturn | undefined) => any;
 | 
					    onChangeHandler?: (
 | 
				
			||||||
 | 
					        imgData: ImageInputToBase64FunctionReturn | undefined
 | 
				
			||||||
 | 
					    ) => any;
 | 
				
			||||||
    fileInputProps?: DetailedHTMLProps<
 | 
					    fileInputProps?: DetailedHTMLProps<
 | 
				
			||||||
        React.InputHTMLAttributes<HTMLInputElement>,
 | 
					        React.InputHTMLAttributes<HTMLInputElement>,
 | 
				
			||||||
        HTMLInputElement
 | 
					        HTMLInputElement
 | 
				
			||||||
@ -35,8 +37,11 @@ type ImageUploadProps = DetailedHTMLProps<
 | 
				
			|||||||
    disablePreview?: boolean;
 | 
					    disablePreview?: boolean;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * @note use the `onChangeHandler` prop to grab the parsed base64 image object
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
export default function ImageUpload({
 | 
					export default function ImageUpload({
 | 
				
			||||||
    onChange,
 | 
					    onChangeHandler,
 | 
				
			||||||
    fileInputProps,
 | 
					    fileInputProps,
 | 
				
			||||||
    placeHolderWrapper,
 | 
					    placeHolderWrapper,
 | 
				
			||||||
    previewImageWrapperProps,
 | 
					    previewImageWrapperProps,
 | 
				
			||||||
@ -60,7 +65,7 @@ export default function ImageUpload({
 | 
				
			|||||||
                onChange={(e) => {
 | 
					                onChange={(e) => {
 | 
				
			||||||
                    imageInputToBase64({ imageInput: e.target }).then((res) => {
 | 
					                    imageInputToBase64({ imageInput: e.target }).then((res) => {
 | 
				
			||||||
                        setSrc(res.imageBase64Full);
 | 
					                        setSrc(res.imageBase64Full);
 | 
				
			||||||
                        onChange?.(res);
 | 
					                        onChangeHandler?.(res);
 | 
				
			||||||
                        fileInputProps?.onChange?.(e);
 | 
					                        fileInputProps?.onChange?.(e);
 | 
				
			||||||
                    });
 | 
					                    });
 | 
				
			||||||
                }}
 | 
					                }}
 | 
				
			||||||
@ -88,7 +93,7 @@ export default function ImageUpload({
 | 
				
			|||||||
                        className="absolute p-2 top-2 right-2 z-20"
 | 
					                        className="absolute p-2 top-2 right-2 z-20"
 | 
				
			||||||
                        onClick={(e) => {
 | 
					                        onClick={(e) => {
 | 
				
			||||||
                            setSrc(undefined);
 | 
					                            setSrc(undefined);
 | 
				
			||||||
                            onChange?.(undefined);
 | 
					                            onChangeHandler?.(undefined);
 | 
				
			||||||
                        }}
 | 
					                        }}
 | 
				
			||||||
                    >
 | 
					                    >
 | 
				
			||||||
                        <X className="text-slate-950 dark:text-white" />
 | 
					                        <X className="text-slate-950 dark:text-white" />
 | 
				
			||||||
 | 
				
			|||||||
@ -107,6 +107,7 @@ export type InputProps<KeyType extends string> = DetailedHTMLProps<
 | 
				
			|||||||
        validationFunction?: (value: string) => Promise<boolean>;
 | 
					        validationFunction?: (value: string) => Promise<boolean>;
 | 
				
			||||||
        autoComplete?: (typeof autocompleteOptions)[number];
 | 
					        autoComplete?: (typeof autocompleteOptions)[number];
 | 
				
			||||||
        name?: KeyType;
 | 
					        name?: KeyType;
 | 
				
			||||||
 | 
					        valueUpdate?: string;
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
@ -130,11 +131,16 @@ export default function Input<KeyType extends string>({
 | 
				
			|||||||
    autoComplete,
 | 
					    autoComplete,
 | 
				
			||||||
    validationFunction,
 | 
					    validationFunction,
 | 
				
			||||||
    validationRegex,
 | 
					    validationRegex,
 | 
				
			||||||
 | 
					    valueUpdate,
 | 
				
			||||||
    ...props
 | 
					    ...props
 | 
				
			||||||
}: InputProps<KeyType>) {
 | 
					}: InputProps<KeyType>) {
 | 
				
			||||||
    const [focus, setFocus] = React.useState(false);
 | 
					    const [focus, setFocus] = React.useState(false);
 | 
				
			||||||
    const [value, setValue] = React.useState(
 | 
					    const [value, setValue] = React.useState(
 | 
				
			||||||
        props.defaultValue ? String(props.defaultValue) : ""
 | 
					        props.value
 | 
				
			||||||
 | 
					            ? String(props.value)
 | 
				
			||||||
 | 
					            : props.defaultValue
 | 
				
			||||||
 | 
					            ? String(props.defaultValue)
 | 
				
			||||||
 | 
					            : ""
 | 
				
			||||||
    );
 | 
					    );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    delete props.defaultValue;
 | 
					    delete props.defaultValue;
 | 
				
			||||||
@ -163,6 +169,11 @@ export default function Input<KeyType extends string>({
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
    }, [value]);
 | 
					    }, [value]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    React.useEffect(() => {
 | 
				
			||||||
 | 
					        if (!props.value) return;
 | 
				
			||||||
 | 
					        setValue(String(props.value));
 | 
				
			||||||
 | 
					    }, [props.value]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const targetComponent = istextarea ? (
 | 
					    const targetComponent = istextarea ? (
 | 
				
			||||||
        <textarea
 | 
					        <textarea
 | 
				
			||||||
            {...props}
 | 
					            {...props}
 | 
				
			||||||
@ -189,7 +200,10 @@ export default function Input<KeyType extends string>({
 | 
				
			|||||||
        <input
 | 
					        <input
 | 
				
			||||||
            {...props}
 | 
					            {...props}
 | 
				
			||||||
            className={twMerge(
 | 
					            className={twMerge(
 | 
				
			||||||
                "w-full outline-none bg-transparent",
 | 
					                "w-full outline-none bg-transparent border-none",
 | 
				
			||||||
 | 
					                "hover:border-none hover:outline-none focus:border-none focus:outline-none",
 | 
				
			||||||
 | 
					                "dark:bg-transparent dark:outline-none dark:border-none",
 | 
				
			||||||
 | 
					                "p-0",
 | 
				
			||||||
                "twui-input",
 | 
					                "twui-input",
 | 
				
			||||||
                props.className
 | 
					                props.className
 | 
				
			||||||
            )}
 | 
					            )}
 | 
				
			||||||
@ -220,7 +234,7 @@ export default function Input<KeyType extends string>({
 | 
				
			|||||||
                    : "border-slate-300 dark:border-white/20",
 | 
					                    : "border-slate-300 dark:border-white/20",
 | 
				
			||||||
                focus && isValid
 | 
					                focus && isValid
 | 
				
			||||||
                    ? "outline-slate-700 dark:outline-white/50"
 | 
					                    ? "outline-slate-700 dark:outline-white/50"
 | 
				
			||||||
                    : "outline-transparent",
 | 
					                    : "outline-slate-300 dark:outline-white/20",
 | 
				
			||||||
                variant == "warning" &&
 | 
					                variant == "warning" &&
 | 
				
			||||||
                    isValid &&
 | 
					                    isValid &&
 | 
				
			||||||
                    "border-yellow-500 dark:border-yellow-300 outline-yellow-500 dark:outline-yellow-300",
 | 
					                    "border-yellow-500 dark:border-yellow-300 outline-yellow-500 dark:outline-yellow-300",
 | 
				
			||||||
@ -246,6 +260,7 @@ export default function Input<KeyType extends string>({
 | 
				
			|||||||
                    className={twMerge(
 | 
					                    className={twMerge(
 | 
				
			||||||
                        "text-xs absolute -top-2.5 left-2 text-slate-500 bg-white px-1.5 rounded-t",
 | 
					                        "text-xs absolute -top-2.5 left-2 text-slate-500 bg-white px-1.5 rounded-t",
 | 
				
			||||||
                        "dark:text-white/60 dark:bg-black",
 | 
					                        "dark:text-white/60 dark:bg-black",
 | 
				
			||||||
 | 
					                        "twui-input-label",
 | 
				
			||||||
                        labelProps?.className
 | 
					                        labelProps?.className
 | 
				
			||||||
                    )}
 | 
					                    )}
 | 
				
			||||||
                >
 | 
					                >
 | 
				
			||||||
 | 
				
			|||||||
@ -16,24 +16,7 @@ type SelectOptionObject = {
 | 
				
			|||||||
    default?: boolean;
 | 
					    default?: boolean;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type SelectOption = SelectOptionObject | SelectOptionObject[];
 | 
					type SelectProps = DetailedHTMLProps<
 | 
				
			||||||
 | 
					 | 
				
			||||||
/**
 | 
					 | 
				
			||||||
 * # Select Element
 | 
					 | 
				
			||||||
 * @className twui-select-wrapper
 | 
					 | 
				
			||||||
 * @className twui-select
 | 
					 | 
				
			||||||
 * @className twui-select-dropdown-icon
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
export default function Select({
 | 
					 | 
				
			||||||
    label,
 | 
					 | 
				
			||||||
    options,
 | 
					 | 
				
			||||||
    componentRef,
 | 
					 | 
				
			||||||
    labelProps,
 | 
					 | 
				
			||||||
    wrapperProps,
 | 
					 | 
				
			||||||
    showLabel,
 | 
					 | 
				
			||||||
    iconProps,
 | 
					 | 
				
			||||||
    ...props
 | 
					 | 
				
			||||||
}: DetailedHTMLProps<
 | 
					 | 
				
			||||||
    SelectHTMLAttributes<HTMLSelectElement>,
 | 
					    SelectHTMLAttributes<HTMLSelectElement>,
 | 
				
			||||||
    HTMLSelectElement
 | 
					    HTMLSelectElement
 | 
				
			||||||
> & {
 | 
					> & {
 | 
				
			||||||
@ -50,7 +33,26 @@ export default function Select({
 | 
				
			|||||||
    >;
 | 
					    >;
 | 
				
			||||||
    componentRef?: RefObject<HTMLSelectElement>;
 | 
					    componentRef?: RefObject<HTMLSelectElement>;
 | 
				
			||||||
    iconProps?: LucideProps;
 | 
					    iconProps?: LucideProps;
 | 
				
			||||||
}) {
 | 
					    changeHandler?: (value: SelectProps["options"][number]["value"]) => void;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * # Select Element
 | 
				
			||||||
 | 
					 * @className twui-select-wrapper
 | 
				
			||||||
 | 
					 * @className twui-select
 | 
				
			||||||
 | 
					 * @className twui-select-dropdown-icon
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					export default function Select({
 | 
				
			||||||
 | 
					    label,
 | 
				
			||||||
 | 
					    options,
 | 
				
			||||||
 | 
					    componentRef,
 | 
				
			||||||
 | 
					    labelProps,
 | 
				
			||||||
 | 
					    wrapperProps,
 | 
				
			||||||
 | 
					    showLabel,
 | 
				
			||||||
 | 
					    iconProps,
 | 
				
			||||||
 | 
					    changeHandler,
 | 
				
			||||||
 | 
					    ...props
 | 
				
			||||||
 | 
					}: SelectProps) {
 | 
				
			||||||
    return (
 | 
					    return (
 | 
				
			||||||
        <div
 | 
					        <div
 | 
				
			||||||
            {...wrapperProps}
 | 
					            {...wrapperProps}
 | 
				
			||||||
@ -64,8 +66,9 @@ export default function Select({
 | 
				
			|||||||
                    htmlFor={props.name}
 | 
					                    htmlFor={props.name}
 | 
				
			||||||
                    {...labelProps}
 | 
					                    {...labelProps}
 | 
				
			||||||
                    className={twMerge(
 | 
					                    className={twMerge(
 | 
				
			||||||
                        "text-xs absolute -top-2 left-4 text-slate-600 bg-white px-2",
 | 
					                        "text-xs absolute -top-2.5 left-2 text-slate-500 bg-white px-1.5 rounded-t",
 | 
				
			||||||
                        "dark:text-white/60 dark:bg-black",
 | 
					                        "dark:text-white/60 dark:bg-black",
 | 
				
			||||||
 | 
					                        "twui-input-label",
 | 
				
			||||||
                        labelProps?.className
 | 
					                        labelProps?.className
 | 
				
			||||||
                    )}
 | 
					                    )}
 | 
				
			||||||
                >
 | 
					                >
 | 
				
			||||||
@ -90,6 +93,12 @@ export default function Select({
 | 
				
			|||||||
                    options.flat().find((opt) => opt.default)?.value ||
 | 
					                    options.flat().find((opt) => opt.default)?.value ||
 | 
				
			||||||
                    undefined
 | 
					                    undefined
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
 | 
					                onChange={(e) => {
 | 
				
			||||||
 | 
					                    changeHandler?.(
 | 
				
			||||||
 | 
					                        e.target.value as (typeof options)[number]["value"]
 | 
				
			||||||
 | 
					                    );
 | 
				
			||||||
 | 
					                    props.onChange?.(e);
 | 
				
			||||||
 | 
					                }}
 | 
				
			||||||
            >
 | 
					            >
 | 
				
			||||||
                {options.flat().map((option, index) => {
 | 
					                {options.flat().map((option, index) => {
 | 
				
			||||||
                    return (
 | 
					                    return (
 | 
				
			||||||
 | 
				
			|||||||
@ -25,10 +25,9 @@ export const WebSocketEventNames = ["wsDataEvent", "wsMessageEvent"] as const;
 | 
				
			|||||||
 *      console.log(e.detail.message) // type string
 | 
					 *      console.log(e.detail.message) // type string
 | 
				
			||||||
 * })
 | 
					 * })
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
export default function useWebSocket<T>({
 | 
					export default function useWebSocket<
 | 
				
			||||||
    url,
 | 
					    T extends { [key: string]: any } = { [key: string]: any }
 | 
				
			||||||
    debounce,
 | 
					>({ url, debounce }: UseWebsocketHookParams) {
 | 
				
			||||||
}: UseWebsocketHookParams) {
 | 
					 | 
				
			||||||
    const DEBOUNCE = debounce || 200;
 | 
					    const DEBOUNCE = debounce || 200;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const [socket, setSocket] = React.useState<WebSocket | undefined>(
 | 
					    const [socket, setSocket] = React.useState<WebSocket | undefined>(
 | 
				
			||||||
@ -75,6 +74,8 @@ export default function useWebSocket<T>({
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        ws.onclose = (ev) => {
 | 
					        ws.onclose = (ev) => {
 | 
				
			||||||
            console.log("Websocket closed ... Attempting to reconnect ...");
 | 
					            console.log("Websocket closed ... Attempting to reconnect ...");
 | 
				
			||||||
 | 
					            console.log("URL:", url);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            reconnectInterval = setInterval(() => {
 | 
					            reconnectInterval = setInterval(() => {
 | 
				
			||||||
                if (tries >= 3) {
 | 
					                if (tries >= 3) {
 | 
				
			||||||
                    return window.clearInterval(reconnectInterval);
 | 
					                    return window.clearInterval(reconnectInterval);
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										39
									
								
								components/lib/hooks/useWebSocketEventHandler.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								components/lib/hooks/useWebSocketEventHandler.tsx
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,39 @@
 | 
				
			|||||||
 | 
					import React from "react";
 | 
				
			||||||
 | 
					import { WebSocketEventNames } from "./useWebSocket";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type Param = {
 | 
				
			||||||
 | 
					    listener?: (typeof WebSocketEventNames)[number];
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * # Use Websocket Data Event Handler Hook
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					export default function useWebSocketEventHandler<
 | 
				
			||||||
 | 
					    T extends { [key: string]: any } = { [key: string]: any }
 | 
				
			||||||
 | 
					>(param?: Param) {
 | 
				
			||||||
 | 
					    const [data, setData] = React.useState<T | undefined>(undefined);
 | 
				
			||||||
 | 
					    const [message, setMessage] = React.useState<string | undefined>(undefined);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    React.useEffect(() => {
 | 
				
			||||||
 | 
					        const dataEventListenerCallback = (e: Event) => {
 | 
				
			||||||
 | 
					            const customEvent = e as CustomEvent;
 | 
				
			||||||
 | 
					            const data = customEvent.detail.data as T | undefined;
 | 
				
			||||||
 | 
					            const message = customEvent.detail.message as string | undefined;
 | 
				
			||||||
 | 
					            if (data) setData(data);
 | 
				
			||||||
 | 
					            if (message) setMessage(message);
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const messageEventName: (typeof WebSocketEventNames)[number] =
 | 
				
			||||||
 | 
					            param?.listener || "wsDataEvent";
 | 
				
			||||||
 | 
					        window.addEventListener(messageEventName, dataEventListenerCallback);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return function () {
 | 
				
			||||||
 | 
					            window.removeEventListener(
 | 
				
			||||||
 | 
					                messageEventName,
 | 
				
			||||||
 | 
					                dataEventListenerCallback
 | 
				
			||||||
 | 
					            );
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					    }, []);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return { data, message };
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -8,6 +8,36 @@ import {
 | 
				
			|||||||
import { twMerge } from "tailwind-merge";
 | 
					import { twMerge } from "tailwind-merge";
 | 
				
			||||||
import Loading from "../elements/Loading";
 | 
					import Loading from "../elements/Loading";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export type TWUIButtonProps = DetailedHTMLProps<
 | 
				
			||||||
 | 
					    ButtonHTMLAttributes<HTMLButtonElement>,
 | 
				
			||||||
 | 
					    HTMLButtonElement
 | 
				
			||||||
 | 
					> & {
 | 
				
			||||||
 | 
					    variant?: "normal" | "ghost" | "outlined";
 | 
				
			||||||
 | 
					    color?:
 | 
				
			||||||
 | 
					        | "primary"
 | 
				
			||||||
 | 
					        | "secondary"
 | 
				
			||||||
 | 
					        | "accent"
 | 
				
			||||||
 | 
					        | "gray"
 | 
				
			||||||
 | 
					        | "error"
 | 
				
			||||||
 | 
					        | "warning"
 | 
				
			||||||
 | 
					        | "success";
 | 
				
			||||||
 | 
					    size?: "small" | "smaller" | "normal" | "large" | "larger";
 | 
				
			||||||
 | 
					    loadingIconSize?: React.ComponentProps<typeof Loading>["size"];
 | 
				
			||||||
 | 
					    href?: string;
 | 
				
			||||||
 | 
					    target?: HTMLAttributeAnchorTarget;
 | 
				
			||||||
 | 
					    loading?: boolean;
 | 
				
			||||||
 | 
					    linkProps?: DetailedHTMLProps<
 | 
				
			||||||
 | 
					        AnchorHTMLAttributes<HTMLAnchorElement>,
 | 
				
			||||||
 | 
					        HTMLAnchorElement
 | 
				
			||||||
 | 
					    >;
 | 
				
			||||||
 | 
					    beforeIcon?: React.ReactNode;
 | 
				
			||||||
 | 
					    afterIcon?: React.ReactNode;
 | 
				
			||||||
 | 
					    buttonContentProps?: DetailedHTMLProps<
 | 
				
			||||||
 | 
					        HTMLAttributes<HTMLDivElement>,
 | 
				
			||||||
 | 
					        HTMLDivElement
 | 
				
			||||||
 | 
					    >;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * # Buttons
 | 
					 * # Buttons
 | 
				
			||||||
 * @className twui-button-general
 | 
					 * @className twui-button-general
 | 
				
			||||||
@ -36,35 +66,9 @@ export default function Button({
 | 
				
			|||||||
    beforeIcon,
 | 
					    beforeIcon,
 | 
				
			||||||
    afterIcon,
 | 
					    afterIcon,
 | 
				
			||||||
    loading,
 | 
					    loading,
 | 
				
			||||||
 | 
					    loadingIconSize,
 | 
				
			||||||
    ...props
 | 
					    ...props
 | 
				
			||||||
}: DetailedHTMLProps<
 | 
					}: TWUIButtonProps) {
 | 
				
			||||||
    ButtonHTMLAttributes<HTMLButtonElement>,
 | 
					 | 
				
			||||||
    HTMLButtonElement
 | 
					 | 
				
			||||||
> & {
 | 
					 | 
				
			||||||
    variant?: "normal" | "ghost" | "outlined";
 | 
					 | 
				
			||||||
    color?:
 | 
					 | 
				
			||||||
        | "primary"
 | 
					 | 
				
			||||||
        | "secondary"
 | 
					 | 
				
			||||||
        | "accent"
 | 
					 | 
				
			||||||
        | "gray"
 | 
					 | 
				
			||||||
        | "error"
 | 
					 | 
				
			||||||
        | "warning"
 | 
					 | 
				
			||||||
        | "success";
 | 
					 | 
				
			||||||
    size?: "small" | "smaller" | "normal" | "large" | "larger";
 | 
					 | 
				
			||||||
    href?: string;
 | 
					 | 
				
			||||||
    target?: HTMLAttributeAnchorTarget;
 | 
					 | 
				
			||||||
    loading?: boolean;
 | 
					 | 
				
			||||||
    linkProps?: DetailedHTMLProps<
 | 
					 | 
				
			||||||
        AnchorHTMLAttributes<HTMLAnchorElement>,
 | 
					 | 
				
			||||||
        HTMLAnchorElement
 | 
					 | 
				
			||||||
    >;
 | 
					 | 
				
			||||||
    beforeIcon?: React.ReactNode;
 | 
					 | 
				
			||||||
    afterIcon?: React.ReactNode;
 | 
					 | 
				
			||||||
    buttonContentProps?: DetailedHTMLProps<
 | 
					 | 
				
			||||||
        HTMLAttributes<HTMLDivElement>,
 | 
					 | 
				
			||||||
        HTMLDivElement
 | 
					 | 
				
			||||||
    >;
 | 
					 | 
				
			||||||
}) {
 | 
					 | 
				
			||||||
    const finalClassName: string = (() => {
 | 
					    const finalClassName: string = (() => {
 | 
				
			||||||
        if (variant == "normal" || !variant) {
 | 
					        if (variant == "normal" || !variant) {
 | 
				
			||||||
            if (color == "primary" || !color)
 | 
					            if (color == "primary" || !color)
 | 
				
			||||||
@ -194,6 +198,7 @@ export default function Button({
 | 
				
			|||||||
                <Loading
 | 
					                <Loading
 | 
				
			||||||
                    className="absolute"
 | 
					                    className="absolute"
 | 
				
			||||||
                    size={(() => {
 | 
					                    size={(() => {
 | 
				
			||||||
 | 
					                        if (loadingIconSize) return loadingIconSize;
 | 
				
			||||||
                        switch (size) {
 | 
					                        switch (size) {
 | 
				
			||||||
                            case "small":
 | 
					                            case "small":
 | 
				
			||||||
                                return "small";
 | 
					                                return "small";
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										26
									
								
								components/lib/utils/fetch/fetchApi.ts
									
									
									
									
									
										
										
										Executable file → Normal file
									
								
							
							
						
						
									
										26
									
								
								components/lib/utils/fetch/fetchApi.ts
									
									
									
									
									
										
										
										Executable file → Normal file
									
								
							@ -1,6 +1,6 @@
 | 
				
			|||||||
import _ from "lodash";
 | 
					import _ from "lodash";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type FetchApiOptions = {
 | 
					type FetchApiOptions<T extends { [k: string]: any } = { [k: string]: any }> = {
 | 
				
			||||||
    method:
 | 
					    method:
 | 
				
			||||||
        | "POST"
 | 
					        | "POST"
 | 
				
			||||||
        | "GET"
 | 
					        | "GET"
 | 
				
			||||||
@ -12,7 +12,7 @@ type FetchApiOptions = {
 | 
				
			|||||||
        | "delete"
 | 
					        | "delete"
 | 
				
			||||||
        | "put"
 | 
					        | "put"
 | 
				
			||||||
        | "patch";
 | 
					        | "patch";
 | 
				
			||||||
    body?: object | string;
 | 
					    body?: T | string;
 | 
				
			||||||
    headers?: FetchHeader;
 | 
					    headers?: FetchHeader;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -30,13 +30,23 @@ export type FetchApiReturn = {
 | 
				
			|||||||
/**
 | 
					/**
 | 
				
			||||||
 * # Fetch API
 | 
					 * # Fetch API
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
export default async function fetchApi(
 | 
					export default async function fetchApi<
 | 
				
			||||||
 | 
					    T extends { [k: string]: any } = { [k: string]: any },
 | 
				
			||||||
 | 
					    R extends any = any
 | 
				
			||||||
 | 
					>(
 | 
				
			||||||
    url: string,
 | 
					    url: string,
 | 
				
			||||||
    options?: FetchApiOptions,
 | 
					    options?: FetchApiOptions<T>,
 | 
				
			||||||
    csrf?: boolean,
 | 
					    csrf?: boolean,
 | 
				
			||||||
    /** Key to use to grab local Storage csrf value. */
 | 
					    /**
 | 
				
			||||||
    localStorageCSRFKey?: string
 | 
					     * Key to use to grab local Storage csrf value.
 | 
				
			||||||
): Promise<any> {
 | 
					     */
 | 
				
			||||||
 | 
					    localStorageCSRFKey?: string,
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Key with which to set the request header csrf
 | 
				
			||||||
 | 
					     * value
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    csrfHeaderKey?: string
 | 
				
			||||||
 | 
					): Promise<R> {
 | 
				
			||||||
    let data;
 | 
					    let data;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const csrfValue = localStorage.getItem(localStorageCSRFKey || "csrf");
 | 
					    const csrfValue = localStorage.getItem(localStorageCSRFKey || "csrf");
 | 
				
			||||||
@ -46,7 +56,7 @@ export default async function fetchApi(
 | 
				
			|||||||
    } as FetchHeader;
 | 
					    } as FetchHeader;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (csrf && csrfValue) {
 | 
					    if (csrf && csrfValue) {
 | 
				
			||||||
        finalHeaders[`'${csrfValue.replace(/\"/g, "")}'`] = "true";
 | 
					        finalHeaders[csrfHeaderKey || "x-csrf-key"] = csrfValue;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (typeof options === "string") {
 | 
					    if (typeof options === "string") {
 | 
				
			||||||
 | 
				
			|||||||
@ -88,6 +88,17 @@ export const skills = {
 | 
				
			|||||||
                image: "/images/work/devops/server-management.png",
 | 
					                image: "/images/work/devops/server-management.png",
 | 
				
			||||||
                technologies: ["Node JS", "Bun JS", "Shell Script", "Python"],
 | 
					                technologies: ["Node JS", "Bun JS", "Shell Script", "Python"],
 | 
				
			||||||
            },
 | 
					            },
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                title: "API Development and Integration",
 | 
				
			||||||
 | 
					                description:
 | 
				
			||||||
 | 
					                    "Developing custom APIs and integrating existing APIs from external services",
 | 
				
			||||||
 | 
					                technologies: [
 | 
				
			||||||
 | 
					                    "API",
 | 
				
			||||||
 | 
					                    "REST",
 | 
				
			||||||
 | 
					                    "API Development",
 | 
				
			||||||
 | 
					                    "Data Fetching",
 | 
				
			||||||
 | 
					                ],
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                title: "React JS",
 | 
					                title: "React JS",
 | 
				
			||||||
                description: "Development of React JS applications for the web",
 | 
					                description: "Development of React JS applications for the web",
 | 
				
			||||||
 | 
				
			|||||||
@ -45,6 +45,13 @@ export const work = {
 | 
				
			|||||||
                    "NGINX Reverse Proxy",
 | 
					                    "NGINX Reverse Proxy",
 | 
				
			||||||
                ],
 | 
					                ],
 | 
				
			||||||
            },
 | 
					            },
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                title: "Ifuekosa LLC",
 | 
				
			||||||
 | 
					                description:
 | 
				
			||||||
 | 
					                    "Tax Preparation, Notary and Business Consulting Services in New Jersey",
 | 
				
			||||||
 | 
					                href: "https://ifuekosallc.com/",
 | 
				
			||||||
 | 
					                technologies: ["Wordpress", "Docker", "Email Server"],
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
        ],
 | 
					        ],
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    Devops: {
 | 
					    Devops: {
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
		Reference in New Issue
	
	Block a user