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
	 Benjamin Toby
						Benjamin Toby