├── .eslintcache
├── .github
└── ISSUE_TEMPLATE
│ ├── bug_report.md
│ └── feature_request.md
├── .gitignore
├── .vscode
├── launch.json
├── settings.json
└── tasks.json
├── LICENSE
├── README.md
├── Step-By-Step.md
├── dist
├── ToastContainer.tsx
├── ToastMessage.tsx
└── ToastProvider.tsx
├── images
├── toast.PNG
└── toast.gif
├── images_tutorial
└── PrepareProjectStructure.png
├── package-lock.json
├── package.json
├── postcss.config.js
├── public
├── favicon.ico
├── index.html
├── manifest.json
└── robots.txt
├── setup.js
├── src
├── App.css
├── App.tsx
├── CSS
│ └── Tailwind.css
├── Components
│ ├── Avatar
│ │ └── Avatar.tsx
│ ├── Buttons
│ │ └── StayledButton.tsx
│ ├── Select
│ │ ├── Select.tsx
│ │ └── SelectOption.tsx
│ ├── Toast
│ │ ├── ToastContainer.tsx
│ │ ├── ToastMessage.tsx
│ │ └── ToastProvider.tsx
│ └── ToastDummyMessage.tsx
├── HomePage
│ ├── Home.tsx
│ └── Layout.tsx
├── Hooks
│ └── useOutsideClick.tsx
├── Providers.tsx
├── index.tsx
└── react-app-env.d.ts
├── tailwind.config.js
├── tsconfig.json
└── yarn.lock
/.eslintcache:
--------------------------------------------------------------------------------
1 | [{"C:\\Users\\dakupc\\Documents\\.NetDev\\Drag-Drop-File\\File-DragDropper\\src\\index.tsx":"1","C:\\Users\\dakupc\\Documents\\.NetDev\\Drag-Drop-File\\File-DragDropper\\src\\App.tsx":"2","C:\\Users\\dakupc\\Documents\\.NetDev\\Drag-Drop-File\\File-DragDropper\\src\\Components\\FileDragDrop\\FileDragDrop.tsx":"3","C:\\Users\\dakupc\\Documents\\.NetDev\\Drag-Drop-File\\File-DragDropper\\src\\Components\\constants.tsx":"4","C:\\Users\\dakupc\\Documents\\.NetDev\\Drag-Drop-File\\File-DragDropper\\src\\Components\\Progress\\ProgressBar.tsx":"5","C:\\Users\\dakupc\\Documents\\.NetDev\\Drag-Drop-File\\File-DragDropper\\src\\Components\\Buttons\\StayledButton.tsx":"6","C:\\Users\\dakupc\\Documents\\.NetDev\\Drag-Drop-File\\File-DragDropper\\src\\Components\\Buttons\\CancleButton.tsx":"7","C:\\Users\\dakupc\\Documents\\.NetDev\\React Toast Component\\src\\index.tsx":"8","C:\\Users\\dakupc\\Documents\\.NetDev\\React Toast Component\\src\\App.tsx":"9","C:\\Users\\dakupc\\Documents\\.NetDev\\React Toast Component\\src\\Providers.tsx":"10","C:\\Users\\dakupc\\Documents\\.NetDev\\React Toast Component\\src\\Components\\Toast\\ToastProvider.tsx":"11","C:\\Users\\dakupc\\Documents\\.NetDev\\React Toast Component\\src\\Components\\Toast\\ToastContainer.tsx":"12","C:\\Users\\dakupc\\Documents\\.NetDev\\React Toast Component\\src\\Components\\Toast\\ToastMessage.tsx":"13","C:\\Users\\dakupc\\Documents\\.NetDev\\React Toast Component\\src\\HomePage\\Layout.tsx":"14","C:\\Users\\dakupc\\Documents\\.NetDev\\React Toast Component\\src\\HomePage\\Home.tsx":"15","C:\\Users\\dakupc\\Documents\\.NetDev\\React Toast Component\\src\\Components\\Buttons\\StayledButton.tsx":"16","C:\\Users\\dakupc\\Documents\\.NetDev\\React Toast Component\\src\\Components\\Avatar\\Avatar.tsx":"17","C:\\Users\\dakupc\\Documents\\.NetDev\\React Toast Component\\src\\Components\\Select\\Select.tsx":"18","C:\\Users\\dakupc\\Documents\\.NetDev\\React Toast Component\\src\\Components\\Select\\SelectOption.tsx":"19","C:\\Users\\dakupc\\Documents\\.NetDev\\React Toast Component\\src\\Hooks\\useOutsideClick.tsx":"20","C:\\Users\\dakupc\\Documents\\.NetDev\\React Toast Component\\src\\Components\\ToastDummyMessage.tsx":"21"},{"size":222,"mtime":1606942061793,"results":"22","hashOfConfig":"23"},{"size":1140,"mtime":1606951527245,"results":"24","hashOfConfig":"23"},{"size":10921,"mtime":1606995507533,"results":"25","hashOfConfig":"23"},{"size":1692,"mtime":1606941509511,"results":"26","hashOfConfig":"23"},{"size":2049,"mtime":1606943849676,"results":"27","hashOfConfig":"23"},{"size":2381,"mtime":1606942321522,"results":"28","hashOfConfig":"23"},{"size":590,"mtime":1606944214496,"results":"29","hashOfConfig":"23"},{"size":222,"mtime":1606942061793,"results":"30","hashOfConfig":"31"},{"size":378,"mtime":1607297617718,"results":"32","hashOfConfig":"31"},{"size":1065,"mtime":1607376150463,"results":"33","hashOfConfig":"31"},{"size":4609,"mtime":1607375607755,"results":"34","hashOfConfig":"31"},{"size":2090,"mtime":1607376259386,"results":"35","hashOfConfig":"31"},{"size":3366,"mtime":1607283079145,"results":"36","hashOfConfig":"31"},{"size":230,"mtime":1607376097993,"results":"37","hashOfConfig":"31"},{"size":6922,"mtime":1607376067931,"results":"38","hashOfConfig":"31"},{"size":2569,"mtime":1607283076620,"results":"39","hashOfConfig":"31"},{"size":1811,"mtime":1607283077366,"results":"40","hashOfConfig":"31"},{"size":2294,"mtime":1607372826373,"results":"41","hashOfConfig":"31"},{"size":891,"mtime":1607293554585,"results":"42","hashOfConfig":"31"},{"size":457,"mtime":1607280588831,"results":"43","hashOfConfig":"31"},{"size":1481,"mtime":1607371512628,"results":"44","hashOfConfig":"31"},{"filePath":"45","messages":"46","errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":"47"},"102f6c5",{"filePath":"48","messages":"49","errorCount":0,"warningCount":1,"fixableErrorCount":0,"fixableWarningCount":0,"source":null},{"filePath":"50","messages":"51","errorCount":0,"warningCount":3,"fixableErrorCount":0,"fixableWarningCount":0,"source":null},{"filePath":"52","messages":"53","errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":"47"},{"filePath":"54","messages":"55","errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":"47"},{"filePath":"56","messages":"57","errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":"47"},{"filePath":"58","messages":"59","errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":"47"},{"filePath":"60","messages":"61","errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":"62"},"h9qkjs",{"filePath":"63","messages":"64","errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":"62"},{"filePath":"65","messages":"66","errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":"62"},{"filePath":"67","messages":"68","errorCount":0,"warningCount":4,"fixableErrorCount":0,"fixableWarningCount":0,"source":"69","usedDeprecatedRules":"62"},{"filePath":"70","messages":"71","errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":"62"},{"filePath":"72","messages":"73","errorCount":0,"warningCount":2,"fixableErrorCount":0,"fixableWarningCount":0,"source":"74","usedDeprecatedRules":"75"},{"filePath":"76","messages":"77","errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":"62"},{"filePath":"78","messages":"79","errorCount":0,"warningCount":1,"fixableErrorCount":0,"fixableWarningCount":0,"source":"80","usedDeprecatedRules":"62"},{"filePath":"81","messages":"82","errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":"62"},{"filePath":"83","messages":"84","errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":"62"},{"filePath":"85","messages":"86","errorCount":0,"warningCount":3,"fixableErrorCount":0,"fixableWarningCount":0,"source":"87","usedDeprecatedRules":"62"},{"filePath":"88","messages":"89","errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":"62"},{"filePath":"90","messages":"91","errorCount":0,"warningCount":1,"fixableErrorCount":0,"fixableWarningCount":0,"source":"92","usedDeprecatedRules":"62"},{"filePath":"93","messages":"94","errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":"62"},"C:\\Users\\dakupc\\Documents\\.NetDev\\Drag-Drop-File\\File-DragDropper\\src\\index.tsx",[],["95","96"],"C:\\Users\\dakupc\\Documents\\.NetDev\\Drag-Drop-File\\File-DragDropper\\src\\App.tsx",["97"],"C:\\Users\\dakupc\\Documents\\.NetDev\\Drag-Drop-File\\File-DragDropper\\src\\Components\\FileDragDrop\\FileDragDrop.tsx",["98","99","100"],"C:\\Users\\dakupc\\Documents\\.NetDev\\Drag-Drop-File\\File-DragDropper\\src\\Components\\constants.tsx",[],"C:\\Users\\dakupc\\Documents\\.NetDev\\Drag-Drop-File\\File-DragDropper\\src\\Components\\Progress\\ProgressBar.tsx",[],"C:\\Users\\dakupc\\Documents\\.NetDev\\Drag-Drop-File\\File-DragDropper\\src\\Components\\Buttons\\StayledButton.tsx",[],"C:\\Users\\dakupc\\Documents\\.NetDev\\Drag-Drop-File\\File-DragDropper\\src\\Components\\Buttons\\CancleButton.tsx",[],"C:\\Users\\dakupc\\Documents\\.NetDev\\React Toast Component\\src\\index.tsx",[],["101","102"],"C:\\Users\\dakupc\\Documents\\.NetDev\\React Toast Component\\src\\App.tsx",[],"C:\\Users\\dakupc\\Documents\\.NetDev\\React Toast Component\\src\\Providers.tsx",[],"C:\\Users\\dakupc\\Documents\\.NetDev\\React Toast Component\\src\\Components\\Toast\\ToastProvider.tsx",["103","104","105","106"],"/* Author: Dalibor Kundrat https://github.com/damikun */\r\n\r\nimport { IconProp } from \"@fortawesome/fontawesome-svg-core\";\r\nimport React, { useCallback, useContext, useState } from \"react\";\r\nimport ToastContainer, { ToastContainerProps } from \"./ToastContainer\";\r\nimport { Truncate } from \"./ToastMessage\";\r\n\r\n/////////////////////////////////////\r\n/// Types\r\n/////////////////////////////////////\r\n\r\nexport type ToastProviderProps = {\r\n children: React.ReactNode;\r\n} & ToastContainerProps;\r\n\r\ntype TostMessageType = \"Info\" | \"Success\" | \"Warning\" | \"Error\";\r\n\r\nexport type Toast = {\r\n id: string;\r\n lifetime: number;\r\n message: string | React.ReactNode;\r\n type?: TostMessageType;\r\n truncate?: Truncate;\r\n icon?: IconProp;\r\n header?: string;\r\n};\r\n\r\nexport type ToastContextType = {\r\n data: Array;\r\n pushError(message: string, lifetime?: number, truncate?: Truncate): void;\r\n pushWarning(message: string, lifetime?: number, truncate?: Truncate): void;\r\n pushSuccess(message: string, lifetime?: number, truncate?: Truncate): void;\r\n pushInfo(message: string, lifetime?: number, truncate?: Truncate): void;\r\n push(\r\n message: string,\r\n type: TostMessageType,\r\n lifetime?: number,\r\n truncate?: Truncate\r\n ): void;\r\n pushCustom(\r\n message: string | React.ReactNode,\r\n lifetime: number,\r\n truncate?: Truncate,\r\n icon?: IconProp | React.ReactNode\r\n ): void;\r\n remove(id: string): void;\r\n};\r\n\r\n/////////////////////////////////////\r\n/// Global and Helpers\r\n/////////////////////////////////////\r\n\r\nexport const ToastContext = React.createContext(\r\n undefined\r\n);\r\n\r\nfunction uuidv4() {\r\n return \"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx\".replace(/[xy]/g, function (c) {\r\n var r = (Math.random() * 16) | 0,\r\n v = c == \"x\" ? r : (r & 0x3) | 0x8;\r\n return v.toString(16);\r\n });\r\n}\r\n\r\nexport const useToast = () => useContext(ToastContext);\r\n\r\nconst DEFAULT_INTERVAL = 2500;\r\n\r\n/////////////////////////////////////\r\n/// Implementation\r\n/////////////////////////////////////\r\n\r\nexport default function ToastProvider({\r\n children,\r\n variant,\r\n}: ToastProviderProps) {\r\n const [data, setData] = useState>([]);\r\n\r\n const Push = useCallback(\r\n (\r\n message: string,\r\n type: TostMessageType,\r\n lifetime?: number,\r\n truncate?: Truncate\r\n ) => {\r\n if (message) {\r\n const new_item: Toast = {\r\n id: uuidv4(),\r\n message: message,\r\n type: type,\r\n lifetime: lifetime ? lifetime : DEFAULT_INTERVAL,\r\n truncate: truncate,\r\n };\r\n\r\n setData((prevState) => [...prevState, new_item]);\r\n }\r\n },\r\n [setData, data]\r\n );\r\n\r\n const PushCustom = useCallback(\r\n (\r\n message: string | React.ReactNode,\r\n lifetime?: number,\r\n truncate?: Truncate,\r\n icon?: IconProp\r\n ) => {\r\n if (message) {\r\n const new_item: Toast = {\r\n id: uuidv4(),\r\n message: message,\r\n lifetime: lifetime ? lifetime : DEFAULT_INTERVAL,\r\n truncate: truncate,\r\n icon: icon,\r\n type: undefined,\r\n };\r\n\r\n setData((prevState) => [...prevState, new_item]);\r\n }\r\n },\r\n [setData, data]\r\n );\r\n\r\n const PushError = useCallback(\r\n (message: string, lifetime?: number, truncate?: Truncate) =>\r\n Push(message, \"Error\", lifetime, truncate),\r\n [Push]\r\n );\r\n const PushWarning = useCallback(\r\n (message: string, lifetime?: number, truncate?: Truncate) =>\r\n Push(message, \"Warning\", lifetime, truncate),\r\n [Push]\r\n );\r\n const PushSuccess = useCallback(\r\n (message: string, lifetime?: number, truncate?: Truncate) =>\r\n Push(message, \"Success\", lifetime, truncate),\r\n [Push]\r\n );\r\n const PushInfo = useCallback(\r\n (message: string, lifetime?: number, truncate?: Truncate) =>\r\n Push(message, \"Info\", lifetime, truncate),\r\n [Push]\r\n );\r\n\r\n const ToastContexd = useCallback(() => {\r\n return {\r\n data: data,\r\n pushError: PushError,\r\n pushWarning: PushWarning,\r\n pushSuccess: PushSuccess,\r\n pushInfo: PushInfo,\r\n push: Push,\r\n pushCustom: PushCustom,\r\n\r\n async remove(id: string) {\r\n setData((prevState) => prevState.filter((e) => e.id != id));\r\n },\r\n };\r\n }, [\r\n data,\r\n setData,\r\n PushError,\r\n PushWarning,\r\n PushSuccess,\r\n PushInfo,\r\n Push,\r\n PushCustom,\r\n ]);\r\n\r\n return (\r\n \r\n \r\n {children}\r\n \r\n );\r\n}\r\n","C:\\Users\\dakupc\\Documents\\.NetDev\\React Toast Component\\src\\Components\\Toast\\ToastContainer.tsx",[],"C:\\Users\\dakupc\\Documents\\.NetDev\\React Toast Component\\src\\Components\\Toast\\ToastMessage.tsx",["107","108"],"/* Author: Dalibor Kundrat https://github.com/damikun */\r\n\r\nimport React, { useEffect } from \"react\";\r\nimport { Toast } from \"./ToastProvider\";\r\nimport { FontAwesomeIcon } from \"@fortawesome/react-fontawesome\";\r\nimport {\r\n faTimes,\r\n faExclamationCircle,\r\n faCheck,\r\n faInfoCircle,\r\n} from \"@fortawesome/free-solid-svg-icons\";\r\nimport clsx from \"clsx\";\r\n\r\nconst VARIANTS = {\r\n Info: {\r\n base: \"bg-white border-blue-500\",\r\n iconstyle: \"text-blue-500 \",\r\n icon: faInfoCircle,\r\n name: \"Info\",\r\n },\r\n\r\n Error: {\r\n base: \"bg-white border-red-500 \",\r\n iconstyle: \"text-red-500 \",\r\n icon: faExclamationCircle,\r\n name: \"Error\",\r\n },\r\n\r\n Warning: {\r\n base: \"bg-white border-yellow-500\",\r\n iconstyle: \"text-yellow-500 \",\r\n icon: faExclamationCircle,\r\n name: \"Warning\",\r\n },\r\n\r\n Success: {\r\n base: \"bg-white border-green-500\",\r\n iconstyle: \"text-green-500 \",\r\n icon: faCheck,\r\n name: \"Success\",\r\n },\r\n};\r\n\r\nexport type Truncate =\r\n | \"truncate-1-lines\"\r\n | \"truncate-2-lines\"\r\n | \"truncate-3-lines\";\r\n\r\nexport type ToastMessage = {\r\n id: string;\r\n lifetime?: number;\r\n variant?: keyof typeof VARIANTS | undefined;\r\n onRemove?: (id: string) => void;\r\n truncate?: Truncate;\r\n} & Toast;\r\n\r\nexport default function ToastMessage({\r\n id,\r\n header,\r\n message,\r\n lifetime,\r\n onRemove,\r\n truncate = \"truncate-1-lines\",\r\n icon,\r\n type,\r\n}: ToastMessage) {\r\n const Var = type\r\n ? VARIANTS[type]\r\n : {\r\n base: \"bg-white border-gray-600 \",\r\n iconstyle: \"\",\r\n icon: icon,\r\n name: header,\r\n };\r\n\r\n useEffect(() => {\r\n if (lifetime && onRemove) {\r\n setTimeout(() => {\r\n onRemove(id);\r\n }, lifetime);\r\n }\r\n }, [lifetime]);\r\n\r\n return (\r\n \r\n
\r\n {Var.icon && (\r\n
\r\n \r\n
\r\n )}\r\n\r\n
\r\n
{Var.name}
\r\n
\r\n {message}\r\n
\r\n
\r\n
onRemove && onRemove(id)}\r\n className={clsx(\r\n \"w-10 h-12 mr-2 items-center mx-auto\",\r\n \"text-center leading-none text-lg\"\r\n )}\r\n >\r\n \r\n
\r\n
\r\n
\r\n );\r\n}\r\n",["109","110"],"C:\\Users\\dakupc\\Documents\\.NetDev\\React Toast Component\\src\\HomePage\\Layout.tsx",[],"C:\\Users\\dakupc\\Documents\\.NetDev\\React Toast Component\\src\\HomePage\\Home.tsx",["111"],"import React, { useEffect, useState } from \"react\";\r\nimport clsx from \"clsx\";\r\nimport StayledButton from \"../Components/Buttons/StayledButton\";\r\nimport { useToast } from \"../Components/Toast/ToastProvider\";\r\nimport Select from \"../Components/Select/Select\";\r\nimport SelectOptions from \"../Components/Select/SelectOption\";\r\nimport ToastDummyMessage from \"../Components/ToastDummyMessage\";\r\nimport { useGlobalState } from \"../Providers\";\r\n\r\nconst text = `Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum`;\r\n\r\nexport default function HomePage() {\r\n const toast = useToast();\r\n const [interval, setInterval] = useState(1500);\r\n const [position, setPosition] = useState(\"top_right\");\r\n const globalState = useGlobalState();\r\n\r\n useEffect(() => {\r\n //@ts-ignore\r\n globalState?.setPosition({ variant: position });\r\n }, [position]);\r\n\r\n return (\r\n \r\n
\r\n
\r\n
\r\n
\r\n\r\n
\r\n setInterval(value as number)}\r\n >\r\n 1000 ms \r\n 2500 ms \r\n 5000 ms \r\n \r\n
\r\n
\r\n
\r\n
\r\n
\r\n setPosition(value as string)}\r\n >\r\n Top-Right \r\n Top-Middle \r\n Top-Left \r\n Bottom-Right \r\n \r\n Bottom-Middle\r\n \r\n Bottom-Left \r\n \r\n
\r\n
\r\n
\r\n
\r\n
\r\n
\r\n toast?.pushError(\"Oppps Error\", interval)}\r\n >\r\n Error\r\n \r\n \r\n toast?.pushWarning(\r\n \"Warning appear\",\r\n interval,\r\n \"truncate-2-lines\"\r\n )\r\n }\r\n >\r\n Warning\r\n \r\n toast?.pushSuccess(\"Action success\", interval)}\r\n >\r\n Success\r\n \r\n toast?.pushInfo(\"Info message\", interval)}\r\n >\r\n Info\r\n \r\n toast?.pushCustom( , interval)}\r\n >\r\n Custom\r\n \r\n
\r\n
\r\n
\r\n
\r\n
\r\n \r\n toast?.pushInfo(text, interval, \"truncate-1-lines\")\r\n }\r\n >\r\n 1 line\r\n \r\n \r\n toast?.pushInfo(text, interval, \"truncate-2-lines\")\r\n }\r\n >\r\n 2 lines\r\n \r\n \r\n toast?.pushInfo(text, interval, \"truncate-3-lines\")\r\n }\r\n >\r\n 3 lines\r\n \r\n
\r\n
\r\n
\r\n
\r\n );\r\n}\r\n\r\n/////////////////////////////////\r\n/////////////////////////////////\r\n\r\ntype SectionHeaderProps = {\r\n name: string;\r\n justify_end?: boolean;\r\n};\r\nfunction SectionHeader({ justify_end, name }: SectionHeaderProps) {\r\n return (\r\n \r\n {name}\r\n
\r\n );\r\n}\r\nfunction GetPositionText(value: string) {\r\n switch (value) {\r\n case \"top_right\":\r\n return \"Top-Right\";\r\n case \"top_left\":\r\n return \"Top-Left\";\r\n case \"top_middle\":\r\n return \"Top-Middle\";\r\n case \"bottom_right\":\r\n return \"Bottom-Right\";\r\n case \"bottom_left\":\r\n return \"Bottom-Left\";\r\n case \"bottom_middle\":\r\n return \"Bottom-Middle\";\r\n }\r\n}\r\n","C:\\Users\\dakupc\\Documents\\.NetDev\\React Toast Component\\src\\Components\\Buttons\\StayledButton.tsx",[],"C:\\Users\\dakupc\\Documents\\.NetDev\\React Toast Component\\src\\Components\\Avatar\\Avatar.tsx",[],"C:\\Users\\dakupc\\Documents\\.NetDev\\React Toast Component\\src\\Components\\Select\\Select.tsx",["112","113","114"],"/* Author: Dalibor Kundrat https://github.com/damikun */\r\n\r\nimport clsx from \"clsx\";\r\nimport React, { useCallback, useEffect, useRef } from \"react\";\r\nimport { useState } from \"react\";\r\nimport { useOutsideClick } from \"../../Hooks/useOutsideClick\";\r\n\r\ntype SelectProps = {\r\n initinal: number | string;\r\n value?: string;\r\n children: React.ReactNode;\r\n onChange?: (value: string | number) => void;\r\n justify_end?: boolean;\r\n};\r\n\r\nexport type SelectContext = {\r\n onSelect(value: string | number): void;\r\n};\r\n\r\nexport const SelectContext = React.createContext(\r\n undefined\r\n);\r\n\r\nexport default function Select({\r\n children,\r\n onChange,\r\n initinal,\r\n justify_end = false,\r\n value,\r\n}: SelectProps) {\r\n const [open, setIsOpen] = useState(false);\r\n\r\n const [selected, setSelected] = useState(initinal);\r\n\r\n useEffect(() => {\r\n onChange && onChange(selected);\r\n }, [selected, onChange]);\r\n\r\n const ref = useRef(null);\r\n\r\n function onClickOutside() {\r\n setIsOpen(false);\r\n }\r\n\r\n useOutsideClick(ref, onClickOutside);\r\n\r\n const handleSlect = useCallback((value: string | number) => {\r\n setSelected(value);\r\n setIsOpen(false);\r\n }, []);\r\n\r\n const Context = useCallback(() => {\r\n return {\r\n onSelect: handleSlect,\r\n };\r\n }, []);\r\n\r\n return (\r\n \r\n {\r\n e.preventDefault();\r\n setIsOpen((e) => !e);\r\n }}\r\n className={clsx(\r\n \"flex flex-col w-full border-2 border-transparent\",\r\n \"hover:border-gray-300 transition duration-150\",\r\n \"focus:border-blue-500 rounded-md cursor-pointer\"\r\n )}\r\n >\r\n
\r\n {value ? value : selected}\r\n
\r\n
\r\n {open == true && (\r\n
\r\n {children}\r\n
\r\n )}\r\n
\r\n
\r\n \r\n );\r\n}\r\n","C:\\Users\\dakupc\\Documents\\.NetDev\\React Toast Component\\src\\Components\\Select\\SelectOption.tsx",[],"C:\\Users\\dakupc\\Documents\\.NetDev\\React Toast Component\\src\\Hooks\\useOutsideClick.tsx",["115"],"import { useEffect } from \"react\";\r\n\r\nexport function useOutsideClick(ref: any, onEvent: () => void) {\r\n useEffect(() => {\r\n function handleClickOutside(event: any) {\r\n if (ref.current && !ref.current.contains(event.target)) {\r\n onEvent();\r\n }\r\n }\r\n\r\n document.addEventListener(\"mousedown\", handleClickOutside);\r\n\r\n return () => {\r\n document.removeEventListener(\"mousedown\", handleClickOutside);\r\n };\r\n }, [ref]);\r\n}\r\n","C:\\Users\\dakupc\\Documents\\.NetDev\\React Toast Component\\src\\Components\\ToastDummyMessage.tsx",[],{"ruleId":"116","replacedBy":"117"},{"ruleId":"118","replacedBy":"119"},{"ruleId":"120","severity":1,"message":"121","line":9,"column":10,"nodeType":"122","messageId":"123","endLine":9,"endColumn":15},{"ruleId":"124","severity":1,"message":"125","line":59,"column":3,"nodeType":"126","endLine":80,"endColumn":4},{"ruleId":"124","severity":1,"message":"127","line":246,"column":28,"nodeType":"122","endLine":246,"endColumn":35},{"ruleId":"124","severity":1,"message":"128","line":255,"column":6,"nodeType":"129","endLine":255,"endColumn":44,"suggestions":"130"},{"ruleId":"116","replacedBy":"131"},{"ruleId":"118","replacedBy":"132"},{"ruleId":"133","severity":1,"message":"134","line":60,"column":13,"nodeType":"135","messageId":"136","endLine":60,"endColumn":15},{"ruleId":"124","severity":1,"message":"137","line":98,"column":5,"nodeType":"129","endLine":98,"endColumn":20,"suggestions":"138"},{"ruleId":"124","severity":1,"message":"137","line":121,"column":5,"nodeType":"129","endLine":121,"endColumn":20,"suggestions":"139"},{"ruleId":"133","severity":1,"message":"140","line":156,"column":61,"nodeType":"135","messageId":"136","endLine":156,"endColumn":63},{"ruleId":"141","severity":1,"message":"142","line":57,"column":25,"nodeType":"122","messageId":"143","endLine":57,"endColumn":37},{"ruleId":"124","severity":1,"message":"144","line":82,"column":6,"nodeType":"129","endLine":82,"endColumn":16,"suggestions":"145"},{"ruleId":"116","replacedBy":"146"},{"ruleId":"118","replacedBy":"147"},{"ruleId":"124","severity":1,"message":"148","line":21,"column":6,"nodeType":"129","endLine":21,"endColumn":16,"suggestions":"149"},{"ruleId":"141","severity":1,"message":"150","line":20,"column":14,"nodeType":"122","messageId":"143","endLine":20,"endColumn":27},{"ruleId":"124","severity":1,"message":"151","line":56,"column":6,"nodeType":"129","endLine":56,"endColumn":8,"suggestions":"152"},{"ruleId":"133","severity":1,"message":"134","line":76,"column":17,"nodeType":"135","messageId":"136","endLine":76,"endColumn":19},{"ruleId":"124","severity":1,"message":"153","line":16,"column":6,"nodeType":"129","endLine":16,"endColumn":11,"suggestions":"154"},"no-native-reassign",["155"],"no-negated-in-lhs",["156"],"@typescript-eslint/no-unused-vars","'state' is assigned a value but never used.","Identifier","unusedVar","react-hooks/exhaustive-deps","The 'upload' function makes the dependencies of useCallback Hook (at line 218) change on every render. Move it inside the useCallback callback. Alternatively, wrap the definition of 'upload' in its own useCallback() Hook.","FunctionDeclaration","The ref value 'divRef.current' will likely have changed by the time this effect cleanup function runs. If this ref points to a node rendered by React, copy 'divRef.current' to a variable inside the effect, and use that variable in the cleanup function.","React Hook useEffect has a missing dependency: 'onDrop'. Either include it or remove the dependency array.","ArrayExpression",["157"],["155"],["156"],"eqeqeq","Expected '===' and instead saw '=='.","BinaryExpression","unexpected","React Hook useCallback has an unnecessary dependency: 'data'. Either exclude it or remove the dependency array.",["158"],["159"],"Expected '!==' and instead saw '!='.","@typescript-eslint/no-redeclare","'ToastMessage' is already defined.","redeclared","React Hook useEffect has missing dependencies: 'id' and 'onRemove'. Either include them or remove the dependency array. If 'onRemove' changes too often, find the parent component that defines it and wrap that definition in useCallback.",["160"],["155"],["156"],"React Hook useEffect has a missing dependency: 'globalState'. Either include it or remove the dependency array.",["161"],"'SelectContext' is already defined.","React Hook useCallback has a missing dependency: 'handleSlect'. Either include it or remove the dependency array.",["162"],"React Hook useEffect has a missing dependency: 'onEvent'. Either include it or remove the dependency array. If 'onEvent' changes too often, find the parent component that defines it and wrap that definition in useCallback.",["163"],"no-global-assign","no-unsafe-negation",{"desc":"164","fix":"165"},{"desc":"166","fix":"167"},{"desc":"166","fix":"168"},{"desc":"169","fix":"170"},{"desc":"171","fix":"172"},{"desc":"173","fix":"174"},{"desc":"175","fix":"176"},"Update the dependencies array to be: [onDragEnter, onDragLeave, onDragOver, onDrop]",{"range":"177","text":"178"},"Update the dependencies array to be: [setData]",{"range":"179","text":"180"},{"range":"181","text":"180"},"Update the dependencies array to be: [id, lifetime, onRemove]",{"range":"182","text":"183"},"Update the dependencies array to be: [globalState, position]",{"range":"184","text":"185"},"Update the dependencies array to be: [handleSlect]",{"range":"186","text":"187"},"Update the dependencies array to be: [onEvent, ref]",{"range":"188","text":"189"},[7063,7101],"[onDragEnter, onDragLeave, onDragOver, onDrop]",[2679,2694],"[setData]",[3225,3240],[1724,1734],"[id, lifetime, onRemove]",[1349,1359],"[globalState, position]",[1287,1289],"[handleSlect]",[445,450],"[onEvent, ref]"]
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug_report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Bug report
3 | about: Create a report to help us improve
4 | title: ''
5 | labels: ''
6 | assignees: ''
7 |
8 | ---
9 |
10 | **Describe the bug**
11 | A clear and concise description of what the bug is.
12 |
13 | **To Reproduce**
14 | Steps to reproduce the behavior:
15 | 1. Go to '...'
16 | 2. Click on '....'
17 | 3. Scroll down to '....'
18 | 4. See error
19 |
20 | **Expected behavior**
21 | A clear and concise description of what you expected to happen.
22 |
23 | **Screenshots**
24 | If applicable, add screenshots to help explain your problem.
25 |
26 | **Desktop (please complete the following information):**
27 | - OS: [e.g. iOS]
28 | - Browser [e.g. chrome, safari]
29 | - Version [e.g. 22]
30 |
31 | **Smartphone (please complete the following information):**
32 | - Device: [e.g. iPhone6]
33 | - OS: [e.g. iOS8.1]
34 | - Browser [e.g. stock browser, safari]
35 | - Version [e.g. 22]
36 |
37 | **Additional context**
38 | Add any other context about the problem here.
39 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature_request.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Feature request
3 | about: Suggest an idea for this project
4 | title: ''
5 | labels: ''
6 | assignees: ''
7 |
8 | ---
9 |
10 | **Is your feature request related to a problem? Please describe.**
11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
12 |
13 | **Describe the solution you'd like**
14 | A clear and concise description of what you want to happen.
15 |
16 | **Describe alternatives you've considered**
17 | A clear and concise description of any alternative solutions or features you've considered.
18 |
19 | **Additional context**
20 | Add any other context or screenshots about the feature request here.
21 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | ## Ignore Visual Studio temporary files, build results, and
2 | ## files generated by popular Visual Studio add-ons.
3 |
4 | ###################
5 | # compiled source #
6 | ###################
7 | *.com
8 | *.class
9 | *.dll
10 | *.exe
11 | *.pdb
12 | *.dll.config
13 | *.cache
14 | *.suo
15 | # Include dlls if they’re in the NuGet packages directory
16 | !/packages/*/lib/*.dll
17 | # Include dlls if they're in the CommonReferences directory
18 | !*CommonReferences/*.dll
19 | ####################
20 | # VS Upgrade stuff #
21 | ####################
22 | _UpgradeReport_Files/
23 | ###############
24 | # Directories #
25 | ###############
26 | bin/
27 | obj/
28 | TestResults/
29 | ###################
30 | # Web publish log #
31 | ###################
32 | *.Publish.xml
33 | #############
34 | # Resharper #
35 | #############
36 | /_ReSharper.*
37 | *.ReSharper.*
38 | ############
39 | # Packages #
40 | ############
41 | # it’s better to unpack these files and commit the raw source
42 | # git has its own built in compression methods
43 | *.7z
44 | *.dmg
45 | *.gz
46 | *.iso
47 | *.jar
48 | *.rar
49 | *.tar
50 | *.zip
51 | ######################
52 | # Logs and databases #
53 | ######################
54 | *.log
55 | *.sqlite
56 | # OS generated files #
57 | ######################
58 | .DS_Store?
59 | ehthumbs.db
60 | Icon?
61 | Thumbs.db
62 |
63 |
64 | # User-specific files
65 | *.user
66 | *.userosscache
67 | *.sln.docstates
68 |
69 | # User-specific files (MonoDevelop/Xamarin Studio)
70 | *.userprefs
71 |
72 | # Build results
73 | [Dd]ebug/
74 | [Dd]ebugPublic/
75 | [Rr]elease/
76 | [Rr]eleases/
77 | x64/
78 | x86/
79 | build/
80 | bld/
81 | [Bb]in/
82 | [Oo]bj/
83 |
84 | # Visual Studo 2015 cache/options directory
85 | .vs/
86 |
87 | # MSTest test Results
88 | [Tt]est[Rr]esult*/
89 | [Bb]uild[Ll]og.*
90 |
91 | # NUNIT
92 | *.VisualState.xml
93 | TestResult.xml
94 |
95 | # Build Results of an ATL Project
96 | [Dd]ebugPS/
97 | [Rr]eleasePS/
98 | dlldata.c
99 |
100 | # DNX
101 | project.lock.json
102 | artifacts/
103 |
104 | *_i.c
105 | *_p.c
106 | *_i.h
107 | *.ilk
108 | *.meta
109 | *.obj
110 | *.pch
111 | *.pgc
112 | *.pgd
113 | *.rsp
114 | *.sbr
115 | *.tlb
116 | *.tli
117 | *.tlh
118 | *.tmp
119 | *.tmp_proj
120 | *.vspscc
121 | *.vssscc
122 | .builds
123 | *.pidb
124 | *.svclog
125 | *.scc
126 |
127 | # Chutzpah Test files
128 | _Chutzpah*
129 |
130 | # Visual C++ cache files
131 | ipch/
132 | *.aps
133 | *.ncb
134 | *.opensdf
135 | *.sdf
136 | *.cachefile
137 |
138 | # Visual Studio profiler
139 | *.psess
140 | *.vsp
141 | *.vspx
142 |
143 | # TFS 2012 Local Workspace
144 | $tf/
145 |
146 | # Guidance Automation Toolkit
147 | *.gpState
148 |
149 | # ReSharper is a .NET coding add-in
150 | _ReSharper*/
151 | *.[Rr]e[Ss]harper
152 | *.DotSettings.user
153 |
154 | # JustCode is a .NET coding add-in
155 | .JustCode
156 |
157 | # TeamCity is a build add-in
158 | _TeamCity*
159 |
160 | # DotCover is a Code Coverage Tool
161 | *.dotCover
162 |
163 | # NCrunch
164 | _NCrunch_*
165 | .*crunch*.local.xml
166 |
167 | # MightyMoose
168 | *.mm.*
169 | AutoTest.Net/
170 |
171 | # Web workbench (sass)
172 | .sass-cache/
173 |
174 | # Installshield output folder
175 | [Ee]xpress/
176 |
177 | # DocProject is a documentation generator add-in
178 | DocProject/buildhelp/
179 | DocProject/Help/*.HxT
180 | DocProject/Help/*.HxC
181 | DocProject/Help/*.hhc
182 | DocProject/Help/*.hhk
183 | DocProject/Help/*.hhp
184 | DocProject/Help/Html2
185 | DocProject/Help/html
186 |
187 | # Click-Once directory
188 | publish/
189 |
190 | # Publish Web Output
191 | *.[Pp]ublish.xml
192 | *.azurePubxml
193 | # TODO: Comment the next line if you want to checkin your web deploy settings
194 | # but database connection strings (with potential passwords) will be unencrypted
195 | *.pubxml
196 | *.publishproj
197 |
198 | # NuGet Packages
199 | *.nupkg
200 | # The packages folder can be ignored because of Package Restore
201 | **/packages/*
202 | # except build/, which is used as an MSBuild target.
203 | !**/packages/build/
204 | # Uncomment if necessary however generally it will be regenerated when needed
205 | #!**/packages/repositories.config
206 |
207 | # Windows Azure Build Output
208 | csx/
209 | *.build.csdef
210 |
211 | # Windows Store app package directory
212 | AppPackages/
213 |
214 | # Visual Studio cache files
215 | # files ending in .cache can be ignored
216 | *.[Cc]ache
217 | # but keep track of directories ending in .cache
218 | !*.[Cc]ache/
219 |
220 | # Others
221 | ClientBin/
222 | [Ss]tyle[Cc]op.*
223 | ~$*
224 | *~
225 | *.dbmdl
226 | *.dbproj.schemaview
227 | *.pfx
228 | *.publishsettings
229 | node_modules/
230 | bower_components/
231 | orleans.codegen.cs
232 |
233 | # RIA/Silverlight projects
234 | Generated_Code/
235 |
236 | # Backup & report files from converting an old project file
237 | # to a newer Visual Studio version. Backup files are not needed,
238 | # because we have git ;-)
239 | _UpgradeReport_Files/
240 | Backup*/
241 | UpgradeLog*.XML
242 | UpgradeLog*.htm
243 |
244 | # SQL Server files
245 | *.mdf
246 | *.ldf
247 |
248 | # Business Intelligence projects
249 | *.rdl.data
250 | *.bim.layout
251 | *.bim_*.settings
252 |
253 | # Microsoft Fakes
254 | FakesAssemblies/
255 |
256 | # Node.js Tools for Visual Studio
257 | .ntvs_analysis.dat
258 |
259 | # Visual Studio 6 build log
260 | *.plg
261 |
262 | # Visual Studio 6 workspace options file
263 | *.opt
--------------------------------------------------------------------------------
/.vscode/launch.json:
--------------------------------------------------------------------------------
1 | {
2 | // Use IntelliSense to learn about possible attributes.
3 | // Hover to view descriptions of existing attributes.
4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
5 | "version": "0.2.0",
6 | "configurations": [
7 | {
8 | "name": ".NET Core Launch (web)",
9 | "type": "coreclr",
10 | "request": "launch",
11 | "preLaunchTask": "build",
12 | "program": "${workspaceFolder}/WebApi/bin/Debug/netcoreapp3.1/WebApi.dll",
13 | "args": [],
14 | "cwd": "${workspaceFolder}/WebApi",
15 | "stopAtEntry": false,
16 | "serverReadyAction": {
17 | "action": "openExternally",
18 | "pattern": "\\bNow listening on:\\s+(https?://\\S+)"
19 | },
20 | "env": {
21 | "ASPNETCORE_ENVIRONMENT": "Development"
22 | },
23 | "sourceFileMap": {
24 | "/Views": "${workspaceFolder}/Views"
25 | }
26 | },
27 | {
28 | "name": ".NET Core Attach",
29 | "type": "coreclr",
30 | "request": "attach",
31 | "processId": "${command:pickProcess}"
32 | },
33 | {
34 | "name": "Docker .NET Core Launch",
35 | "type": "docker",
36 | "request": "launch",
37 | "preLaunchTask": "docker-run: debug",
38 | "netCore": {
39 | "appProject": "${workspaceFolder}/WebApi/WebApi.csproj"
40 | }
41 | }
42 | ]
43 | }
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "markdown-todo.sortByCount": false,
3 |
4 | // "eslint.validate": [
5 | // "javascript",
6 | // "javascriptreact",
7 | // { "language": "typescript", "autoFix": true },
8 | // { "language": "typescriptreact", "autoFix": true }
9 | // ],
10 |
11 | "eslint.workingDirectories": [ "./" ],
12 | "extensions.autoCheckUpdates": false,
13 | "extensions.autoUpdate": false
14 | "omnisharp.enableEditorConfigSupport": true,
15 |
16 | "editor.formatOnSave": true,
17 |
18 | "[javascript]": {
19 | "editor.defaultFormatter": "esbenp.prettier-vscode"
20 | },
21 |
22 | "editor.semanticHighlighting.enabled": true,
23 | "csharp.semanticHighlighting.enabled": true,
24 | "omnisharp.enableEditorConfigSupport": true,
25 | "editor.formatOnType": true,
26 | "editor.formatOnPaste": true,
27 | "editor.formatOnSave": true,
28 | }
29 |
--------------------------------------------------------------------------------
/.vscode/tasks.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": "2.0.0",
3 | "tasks": [
4 | {
5 | "label": "build",
6 | "command": "dotnet",
7 | "type": "process",
8 | "args": [
9 | "build",
10 | "${workspaceFolder}/WebApi/WebApi.csproj",
11 | "/property:GenerateFullPaths=true",
12 | "/consoleloggerparameters:NoSummary"
13 | ],
14 | "problemMatcher": "$msCompile"
15 | },
16 | {
17 | "label": "publish",
18 | "command": "dotnet",
19 | "type": "process",
20 | "args": [
21 | "publish",
22 | "${workspaceFolder}/WebApi/WebApi.csproj",
23 | "/property:GenerateFullPaths=true",
24 | "/consoleloggerparameters:NoSummary"
25 | ],
26 | "problemMatcher": "$msCompile"
27 | },
28 | {
29 | "label": "watch",
30 | "command": "dotnet",
31 | "type": "process",
32 | "args": [
33 | "watch",
34 | "run",
35 | "${workspaceFolder}/WebApi/WebApi.csproj",
36 | "/property:GenerateFullPaths=true",
37 | "/consoleloggerparameters:NoSummary"
38 | ],
39 | "problemMatcher": "$msCompile"
40 | },
41 | {
42 | "type": "docker-build",
43 | "label": "docker-build: debug",
44 | "dependsOn": [
45 | "build"
46 | ],
47 | "dockerBuild": {
48 | "tag": "akl:dev",
49 | "target": "base",
50 | "dockerfile": "${workspaceFolder}/WebApi/Dockerfile",
51 | "context": "${workspaceFolder}",
52 | "pull": true
53 | },
54 | "netCore": {
55 | "appProject": "${workspaceFolder}/WebApi/WebApi.csproj"
56 | }
57 | },
58 | {
59 | "type": "docker-build",
60 | "label": "docker-build: release",
61 | "dependsOn": [
62 | "build"
63 | ],
64 | "dockerBuild": {
65 | "tag": "akl:latest",
66 | "dockerfile": "${workspaceFolder}/WebApi/Dockerfile",
67 | "context": "${workspaceFolder}",
68 | "pull": true
69 | },
70 | "netCore": {
71 | "appProject": "${workspaceFolder}/WebApi/WebApi.csproj"
72 | }
73 | },
74 | {
75 | "type": "docker-run",
76 | "label": "docker-run: debug",
77 | "dependsOn": [
78 | "docker-build: debug"
79 | ],
80 | "dockerRun": {
81 | "os": "Windows"
82 | },
83 | "netCore": {
84 | "appProject": "${workspaceFolder}/WebApi/WebApi.csproj",
85 | "enableDebugging": true
86 | }
87 | },
88 | {
89 | "type": "docker-run",
90 | "label": "docker-run: release",
91 | "dependsOn": [
92 | "docker-build: release"
93 | ],
94 | "dockerRun": {
95 | "os": "Windows"
96 | },
97 | "netCore": {
98 | "appProject": "${workspaceFolder}/WebApi/WebApi.csproj"
99 | }
100 | }
101 | ]
102 | }
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2020 Dalibor Kundrat
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
React Toast Component
8 |
11 |
12 |
13 | ### Description
14 |
15 | This is custom toast component implemented by react hooks + React Context API and stayled using tailwindCSS framework. Feel free to inspirate by implementation :) This is trim of my UI lib that i use for my projects.
16 |
17 | This componnent allow to push notifications to user screen and auto remove it after set or default time..
18 |
19 | Example implements this push-Events:
20 |
21 | - Info
22 | - Error
23 | - Warning
24 | - Success
25 | - Custom body
26 |
27 | #### Concepts
28 |
29 | - Responsive
30 | - Using Hooks and Context API
31 | - Using Tailwind and Fontawesome
32 | (All can be adjusted by endpoint user)
33 |
34 | ### Installation
35 |
36 | (!!! Package is not available on npm !!!)
37 |
38 | 1. Clone the repo
39 | ```sh
40 | git clone https://github.com/damikun/React-Toast.git
41 | ```
42 | 2. Restore packages
43 | ```
44 | yarn install
45 | ```
46 | 3. Build and run demo
47 | ```
48 | yarn run start
49 | ```
50 |
51 |
52 |
53 | ### Configuration API
54 |
55 | Toast Provider
56 |
57 | - Usually placed in Providers.tsx or on top of App.tsx
58 | - Give you access to toast
59 | - In this example toast are send from "HomePage"
60 |
61 | ```tsx
62 |
63 |
64 |
65 |
66 |
67 | ```
68 |
69 | Use hook to access toast actions
70 |
71 | ```tsx
72 | // Custom hook to access default context
73 | const toast = useToast();
74 | // OR
75 | // Use of concrete conetxt
76 | const toast = useContext(ToastContext);
77 | ```
78 |
79 | Example:
80 |
81 | ```tsx
82 | export default function HomePage() {
83 | const toast = useToast();
84 |
85 | return (
86 |
87 | toast?.pushError("Oppps Error", 5000)}
90 | >
91 | Error
92 |
93 |
94 | );
95 | }
96 | ```
97 |
98 | Various types to push
99 |
100 | ```tsx
101 | toast?.pushError("Error messgae", 5000);
102 | toast?.pushWarning("Warning message"); // Default timeValue
103 | toast?.pushSuccess("Success message");
104 | toast?.pushInfo("Info Message");
105 | toast?.push("Message", "Info", 2000);
106 | toast?.pushCustom( , 2000);
107 | toast?.pushError("Error messgae", 5000, "truncate-2-lines");
108 | ```
109 |
110 | Predefined types (can be extended)
111 |
112 | ```tsx
113 | type TostMessageType = "Info" | "Success" | "Warning" | "Error";
114 | ```
115 |
116 | Support message truncate trim
117 |
118 | ```tsx
119 | type Truncate = "truncate-1-lines" | "truncate-2-lines" | "truncate-3-lines";
120 | ```
121 |
122 | Pass any custom React.ReactNode component to body
123 |
124 | ```tsx
125 | toast?.pushCustom( , 2000);
126 | toast?.pushCustom(My custom body
, 2000);
127 | ```
128 |
129 | Various toast position
130 |
131 | ```tsx
132 |
133 |
134 | // ...
135 |
136 |
137 | type Position "top_right" | "top_middle" | "top_left" | "bottom_right" | "bottom_middle" | "bottom_left"
138 | ```
139 |
140 | ### Other
141 |
142 | For valid display check that your React _"root"_ is flexible and fulscreen to support all browsers behaviour...
143 |
144 | ```
145 | // public/index.html
146 |
147 | ```
148 |
149 | ### Doc
150 |
151 | You can read [setp-by-step](./Step-By-Step.md) tutorial if you wanna start from scratch. (Currently writting / Not finished).
152 |
153 | ## License
154 |
155 | This project is licensed with the [MIT license](LICENSE).
156 |
--------------------------------------------------------------------------------
/Step-By-Step.md:
--------------------------------------------------------------------------------
1 | ### Description
2 |
3 | Currently many web apps use some kind of notification to let user now about the events. There are various types of notification but this article is focused only on Web App Toasts. They are not fixed to OS / Framework and do not required any aditional provider, **they are simple React functions()** and they leave only under it..
4 |
5 | ##### Instrumentation:
6 |
7 | This step-by-step example will use
8 |
9 | - React
10 | - React Context API
11 | - Styling using TailwindCSS
12 |
13 | ##### Expected result:
14 |
15 | Toast component from scratch as **one Hook** all what you need to push events like:
16 |
17 | - Push-Info
18 | - Push-Error
19 | - Push-Warning
20 | - Push-Success
21 | - Push-CustomBody
22 |
23 | ##### Agenda:
24 |
25 | 1. Project initialization (will use create-react-app scripts) and init TailwindCSS
26 | 2. Describe parts and implementation of ToastProvider, ToastContainer, TastMessage
27 | 3. Explain how Toast state is handled
28 | 4. Do whatever more you want by yourself :)
29 |
30 | #### Project Init
31 |
32 | Lets use _create-react-app_ script with typescript template. If you wanna learn more about template [click hire](https://create-react-app.dev/docs/adding-typescript/).
33 |
34 | ```
35 | npx create-react-app my-app --template typescript
36 | // or
37 | yarn create react-app toast-step-by-step --template typescript
38 | ```
39 |
40 | Now wait after all gets downloaded and initialised by package-manager.
41 |
42 | Delete unnecesary files generated by templeate (tests and stuff around are not needed)
43 |
44 | 
45 |
46 | Fill App.tsx and index.tsx by content below:
47 |
48 | **App.tsx**
49 |
50 | ```
51 | import React from 'react';
52 |
53 | export default function App() {
54 | return (
55 | <>Hello World>
56 | );
57 | }
58 | ```
59 |
60 | **index.tsx**
61 |
62 | ```
63 | import React from 'react';
64 | import ReactDOM from 'react-dom';
65 | import App from './App';
66 |
67 | ReactDOM.render(
68 |
69 |
70 | ,
71 | document.getElementById('root')
72 | );
73 | ```
74 |
75 | Now try to run App using:
76 |
77 | ```
78 | yarn run start
79 | ```
80 |
81 | If all go well you shoud get default server info in terminal
82 |
83 | ```
84 | Compiled successfully!
85 |
86 | You can now view my-app in the browser.
87 |
88 | Local: http://localhost:3000
89 | On Your Network: http://172.20.10.9:3000
90 | ```
91 |
92 | And in browser you can see _Hello World_ message.
93 |
94 | #### Tailwind Init
95 |
96 | Use folowing command to install tailwind and connected packages. To read more about Tailwind instalation options [click hire](https://tailwindcss.com/docs/installation).
97 |
98 | ```
99 | yarn add tailwindcss@latest postcss@latest autoprefixer@latest
100 | // Or
101 | npm install tailwindcss@latest postcss@latest autoprefixer@latest
102 | ```
103 |
104 | Create _postcss.config.js_ under root folder.
105 |
106 | ```
107 | // In case auto init
108 | module.exports = {
109 | plugins: {
110 | tailwindcss: {},
111 | autoprefixer: {},
112 | }
113 | }
114 |
115 | // Or specify plugin path
116 | // module.exports = {
117 | // plugins: [
118 | // require("tailwindcss")("./tailwind.config.js"),
119 | // require("autoprefixer"),
120 | // ],
121 | // };
122 | ```
123 |
124 | Run Tailwind Init command
125 |
126 | ```
127 | npx tailwindcss init
128 | ```
129 |
130 | The result in console
131 |
132 | ```
133 | tailwindcss 2.0.2
134 | ✅ Created Tailwind config file: tailwind.config.js
135 | ```
136 |
137 | The Tailwind config was created and will be edited later... For now it can have this info: (This can look different between versions).
138 |
139 | _tailwind.config.js_
140 |
141 | ```
142 | module.exports = {
143 | purge: [],
144 | darkMode: false, // or 'media' or 'class'
145 | theme: {
146 | extend: {},
147 | },
148 | variants: {
149 | extend: {},
150 | },
151 | plugins: [],
152 | }
153 | ```
154 |
155 | Lets create empty CSS file _App.css_ and fill it with tailwind anotations...
156 |
157 | - You can put any custom CSS on top of @tailwind anotations
158 |
159 | ```
160 | // Any other custom CSS must be in this place !!!
161 | @tailwind base;
162 | @tailwind components;
163 | @tailwind utilities;
164 | ```
165 |
166 | Lets instal postcss-cli we need it to run postcss commands..
167 |
168 | - Install it as dev dependecy
169 |
170 | ```
171 | yarn add postcss-cli
172 | ```
173 |
174 | Lets create new script under _package.json_ called build:css
175 |
176 | - It will take our CSS from _App.css_ and Purge output of Tailwind classes to output file index.css
177 |
178 | - Purge is proces of removing unused css classes to make result file smaller.
179 |
180 | - Lets edit _start_ script to run _build:css_ bofore _react-script_
181 |
182 | ```
183 | "scripts": {
184 | "start": "yarn run build:css && react-scripts start",
185 | "build": "react-scripts build",
186 | "test": "react-scripts test",
187 | "eject": "react-scripts eject",
188 | "build:css": "postcss src/App.css -o src/index.css"
189 | },
190 | ```
191 |
192 | Lets edit our tailwind.config.js to setup purge params.
193 |
194 | ```
195 | module.exports = {
196 | purge: {
197 | preserveHtmlElements: true,
198 | enabled: true,
199 | content: ["./src/**/*.{ts,tsx}"],
200 | },
201 | darkMode: false, // or 'media' or 'class'
202 | theme: {
203 | extend: {},
204 | },
205 | variants: {
206 | extend: {},
207 | },
208 | plugins: [],
209 | }
210 | ```
211 |
212 | Now you can run build:css command
213 |
214 | ```
215 | yarn run build:css
216 | ```
217 |
218 | - The resul is that index.css was generated
219 | - Contains minimum CSS to run the app
220 | - Unused CSS was removed
221 | - Full Tailwind CSS has more that 46k of lines... Purge 450 lines..
222 |
223 | Lets import new _index.css_ to our _index.tsx_
224 |
225 | ```
226 | import React from 'react';
227 | import ReactDOM from 'react-dom';
228 | import App from './App';
229 | import "./index.css" // Added +
230 |
231 | ReactDOM.render(
232 |
233 |
234 | ,
235 | document.getElementById('root')
236 | );
237 | ```
238 |
239 | From now you can use tailwind classes.
240 |
241 | #### VS Code
242 |
243 | If you are user of VSCode you can install folowing extensions to make your life better :P
244 |
245 | - [ESLint](https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint)
246 | - [Tailwind CSS IntelliSense](https://marketplace.visualstudio.com/items?itemName=bradlc.vscode-tailwindcss)
247 |
248 | /// Todo...
249 |
--------------------------------------------------------------------------------
/dist/ToastContainer.tsx:
--------------------------------------------------------------------------------
1 | /* Author: Dalibor Kundrat https://github.com/damikun */
2 |
3 | import clsx from "clsx";
4 | import React, { useContext } from "react";
5 | import ToastMessage from "./ToastMessage";
6 | import { ToastContext } from "./ToastProvider";
7 |
8 | export type ToastContainerProps = {
9 | variant?: keyof typeof VARIANTS;
10 | };
11 |
12 | const VARIANTS = {
13 | top_left: {
14 | style: "top-0 left-0",
15 | },
16 | top_right: {
17 | style: "top-0 right-0",
18 | },
19 | bottom_right: {
20 | style: "bottom-0 right-0",
21 | },
22 | bottom_left: {
23 | style: "bottom-0 left-0",
24 | },
25 | top_middle: {
26 | style: "top-0 left-1/2 -translate-x-1/2 transform",
27 | },
28 | bottom_middle: {
29 | style: "bottom-0 left-1/2 -translate-x-1/2 transform",
30 | },
31 | undefined: {
32 | style: "top-0 right-0",
33 | },
34 | };
35 |
36 | export default function ToastContainer({
37 | variant = "top_right",
38 | }: ToastContainerProps) {
39 | const context = useContext(ToastContext);
40 |
41 | const Var = VARIANTS[variant] || VARIANTS.top_right;
42 |
43 | function handleRemove(id: string) {
44 | context?.remove(id);
45 | }
46 |
47 | return (
48 |
55 |
60 | {context?.data.map((toast) => {
61 | return (
62 |
69 |
79 |
80 | );
81 | })}
82 |
83 |
84 | );
85 | }
86 |
--------------------------------------------------------------------------------
/dist/ToastMessage.tsx:
--------------------------------------------------------------------------------
1 | /* Author: Dalibor Kundrat https://github.com/damikun */
2 |
3 | import React, { useEffect } from "react";
4 | import { Toast } from "./ToastProvider";
5 | import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
6 | import {
7 | faTimes,
8 | faExclamationCircle,
9 | faCheck,
10 | faInfoCircle,
11 | } from "@fortawesome/free-solid-svg-icons";
12 | import clsx from "clsx";
13 |
14 | const VARIANTS = {
15 | Info: {
16 | base: "bg-white border-blue-500",
17 | iconstyle: "text-blue-500 ",
18 | icon: faInfoCircle,
19 | name: "Info",
20 | },
21 |
22 | Error: {
23 | base: "bg-white border-red-500 ",
24 | iconstyle: "text-red-500 ",
25 | icon: faExclamationCircle,
26 | name: "Error",
27 | },
28 |
29 | Warning: {
30 | base: "bg-white border-yellow-500",
31 | iconstyle: "text-yellow-500 ",
32 | icon: faExclamationCircle,
33 | name: "Warning",
34 | },
35 |
36 | Success: {
37 | base: "bg-white border-green-500",
38 | iconstyle: "text-green-500 ",
39 | icon: faCheck,
40 | name: "Success",
41 | },
42 | };
43 |
44 | export type Truncate =
45 | | "truncate-1-lines"
46 | | "truncate-2-lines"
47 | | "truncate-3-lines";
48 |
49 | export type ToastMessage = {
50 | id: string;
51 | lifetime?: number;
52 | variant?: keyof typeof VARIANTS | undefined;
53 | onRemove?: (id: string) => void;
54 | truncate?: Truncate;
55 | } & Toast;
56 |
57 | export default function ToastMessage({
58 | id,
59 | header,
60 | message,
61 | lifetime,
62 | onRemove,
63 | truncate = "truncate-1-lines",
64 | icon,
65 | type,
66 | }: ToastMessage) {
67 | const Var = type
68 | ? VARIANTS[type]
69 | : {
70 | base: "bg-white border-gray-600 ",
71 | iconstyle: "",
72 | icon: icon,
73 | name: header,
74 | };
75 |
76 | useEffect(() => {
77 | if (lifetime && onRemove) {
78 | setTimeout(() => {
79 | onRemove(id);
80 | }, lifetime);
81 | }
82 | }, [lifetime]);
83 |
84 | return (
85 |
94 |
95 | {Var.icon && (
96 |
102 |
106 |
107 | )}
108 |
109 |
110 |
{Var.name}
111 |
118 | {message}
119 |
120 |
121 |
onRemove && onRemove(id)}
123 | className={clsx(
124 | "w-10 h-12 mr-2 items-center mx-auto",
125 | "text-center leading-none text-lg"
126 | )}
127 | >
128 |
135 |
136 |
137 |
138 | );
139 | }
140 |
--------------------------------------------------------------------------------
/dist/ToastProvider.tsx:
--------------------------------------------------------------------------------
1 | /* Author: Dalibor Kundrat https://github.com/damikun */
2 |
3 | import { IconProp } from "@fortawesome/fontawesome-svg-core";
4 | import React, { useCallback, useContext, useState } from "react";
5 | import ToastContainer, { ToastContainerProps } from "./ToastContainer";
6 | import { Truncate } from "./ToastMessage";
7 |
8 | /////////////////////////////////////
9 | /// Types
10 | /////////////////////////////////////
11 |
12 | export type ToastProviderProps = {
13 | children: React.ReactNode;
14 | } & ToastContainerProps;
15 |
16 | type TostMessageType = "Info" | "Success" | "Warning" | "Error";
17 |
18 | export type Toast = {
19 | id: string;
20 | lifetime: number;
21 | message: string | React.ReactNode;
22 | type?: TostMessageType;
23 | truncate?: Truncate;
24 | icon?: IconProp;
25 | header?: string;
26 | };
27 |
28 | export type ToastContextType = {
29 | data: Array;
30 | pushError(message: string, lifetime?: number, truncate?: Truncate): void;
31 | pushWarning(message: string, lifetime?: number, truncate?: Truncate): void;
32 | pushSuccess(message: string, lifetime?: number, truncate?: Truncate): void;
33 | pushInfo(message: string, lifetime?: number, truncate?: Truncate): void;
34 | push(
35 | message: string,
36 | type: TostMessageType,
37 | lifetime?: number,
38 | truncate?: Truncate
39 | ): void;
40 | pushCustom(
41 | message: string | React.ReactNode,
42 | lifetime: number,
43 | truncate?: Truncate,
44 | icon?: IconProp | React.ReactNode
45 | ): void;
46 | remove(id: string): void;
47 | };
48 |
49 | /////////////////////////////////////
50 | /// Global and Helpers
51 | /////////////////////////////////////
52 |
53 | export const ToastContext = React.createContext(
54 | undefined
55 | );
56 |
57 | function uuidv4() {
58 | return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, function (c) {
59 | var r = (Math.random() * 16) | 0,
60 | v = c == "x" ? r : (r & 0x3) | 0x8;
61 | return v.toString(16);
62 | });
63 | }
64 |
65 | export const useToast = () => useContext(ToastContext);
66 |
67 | const DEFAULT_INTERVAL = 2500;
68 |
69 | /////////////////////////////////////
70 | /// Implementation
71 | /////////////////////////////////////
72 |
73 | export default function ToastProvider({
74 | children,
75 | variant,
76 | }: ToastProviderProps) {
77 | const [data, setData] = useState>([]);
78 |
79 | const Push = useCallback(
80 | (
81 | message: string,
82 | type: TostMessageType,
83 | lifetime?: number,
84 | truncate?: Truncate
85 | ) => {
86 | if (message) {
87 | const new_item: Toast = {
88 | id: uuidv4(),
89 | message: message,
90 | type: type,
91 | lifetime: lifetime ? lifetime : DEFAULT_INTERVAL,
92 | truncate: truncate,
93 | };
94 |
95 | setData((prevState) => [...prevState, new_item]);
96 | }
97 | },
98 | [setData, data]
99 | );
100 |
101 | const PushCustom = useCallback(
102 | (
103 | message: string | React.ReactNode,
104 | lifetime?: number,
105 | truncate?: Truncate,
106 | icon?: IconProp
107 | ) => {
108 | if (message) {
109 | const new_item: Toast = {
110 | id: uuidv4(),
111 | message: message,
112 | lifetime: lifetime ? lifetime : DEFAULT_INTERVAL,
113 | truncate: truncate,
114 | icon: icon,
115 | type: undefined,
116 | };
117 |
118 | setData((prevState) => [...prevState, new_item]);
119 | }
120 | },
121 | [setData, data]
122 | );
123 |
124 | const PushError = useCallback(
125 | (message: string, lifetime?: number, truncate?: Truncate) =>
126 | Push(message, "Error", lifetime, truncate),
127 | [Push]
128 | );
129 | const PushWarning = useCallback(
130 | (message: string, lifetime?: number, truncate?: Truncate) =>
131 | Push(message, "Warning", lifetime, truncate),
132 | [Push]
133 | );
134 | const PushSuccess = useCallback(
135 | (message: string, lifetime?: number, truncate?: Truncate) =>
136 | Push(message, "Success", lifetime, truncate),
137 | [Push]
138 | );
139 | const PushInfo = useCallback(
140 | (message: string, lifetime?: number, truncate?: Truncate) =>
141 | Push(message, "Info", lifetime, truncate),
142 | [Push]
143 | );
144 |
145 | const ToastContexd = useCallback(() => {
146 | return {
147 | data: data,
148 | pushError: PushError,
149 | pushWarning: PushWarning,
150 | pushSuccess: PushSuccess,
151 | pushInfo: PushInfo,
152 | push: Push,
153 | pushCustom: PushCustom,
154 |
155 | async remove(id: string) {
156 | setData((prevState) => prevState.filter((e) => e.id != id));
157 | },
158 | };
159 | }, [
160 | data,
161 | setData,
162 | PushError,
163 | PushWarning,
164 | PushSuccess,
165 | PushInfo,
166 | Push,
167 | PushCustom,
168 | ]);
169 |
170 | return (
171 |
172 |
173 | {children}
174 |
175 | );
176 | }
177 |
--------------------------------------------------------------------------------
/images/toast.PNG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/damikun/React-Toast/82c457cd656b66beaf416331ea3ba250d0220079/images/toast.PNG
--------------------------------------------------------------------------------
/images/toast.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/damikun/React-Toast/82c457cd656b66beaf416331ea3ba250d0220079/images/toast.gif
--------------------------------------------------------------------------------
/images_tutorial/PrepareProjectStructure.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/damikun/React-Toast/82c457cd656b66beaf416331ea3ba250d0220079/images_tutorial/PrepareProjectStructure.png
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "homepage": "https://damikun.github.io/React-Toast",
3 | "name": "react-toast-notify",
4 | "version": "0.0.2",
5 | "license": "MIT",
6 | "author": "Dalibor Kundrat",
7 | "private": false,
8 | "keywords": [
9 | "react",
10 | "react-toast",
11 | "react-notify",
12 | "react-toast-component",
13 | "react-push"
14 | ],
15 | "babel": {
16 | "presets": [
17 | "@babel/preset-react"
18 | ]
19 | },
20 | "peerDependencies": {
21 | "react": "^17.0.1"
22 | },
23 | "dependencies": {
24 | "@babel/cli": "^7.12.8",
25 | "@babel/preset-react": "^7.12.7",
26 | "@types/node": "^12.19.8",
27 | "@types/react": "^16.14.2",
28 | "@types/react-dom": "^16.9.10",
29 | "autoprefixer": "^10.1.0",
30 | "react": "^17.0.1",
31 | "react-dom": "^17.0.1",
32 | "react-scripts": "4.0.1",
33 | "typescript": "^4.1.2"
34 | },
35 | "scripts": {
36 | "start": "yarn run build:css && react-scripts start",
37 | "build": " react-scripts build",
38 | "build:css": "postcss src/CSS/Tailwind.css -o src/CSS/App.css ",
39 | "predeploy": "react-scripts build",
40 | "deploy": "gh-pages -d build",
41 | "publish:npm": " mkdir dist && babel src/Components/Toast -d dist --copy-files"
42 | },
43 | "eslintConfig": {
44 | "extends": [
45 | "react-app",
46 | "react-app/jest"
47 | ]
48 | },
49 | "devDependencies": {
50 | "@fortawesome/fontawesome-free": "^5.13.0",
51 | "@fortawesome/fontawesome-svg-core": "^1.2.28",
52 | "@fortawesome/free-solid-svg-icons": "^5.13.0",
53 | "@fortawesome/react-fontawesome": "^0.1.9",
54 | "@tailwindcss/typography": "^0.2.0",
55 | "clsx": "^1.1.1",
56 | "gh-pages": "^3.1.0",
57 | "postcss": "^8.2.1",
58 | "postcss-cli": "^8.3.1",
59 | "tailwindcss": "^2.0.2",
60 | "tailwindcss-truncate-multiline": "^1.0.3"
61 | },
62 | "browserslist": {
63 | "production": [
64 | ">0.2%",
65 | "not dead",
66 | "not op_mini all"
67 | ],
68 | "development": [
69 | "last 1 chrome version",
70 | "last 1 firefox version",
71 | "last 1 safari version"
72 | ]
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/postcss.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | plugins: [
3 | require("tailwindcss")("./tailwind.config.js"),
4 | require("autoprefixer"),
5 | ],
6 | };
7 |
--------------------------------------------------------------------------------
/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/damikun/React-Toast/82c457cd656b66beaf416331ea3ba250d0220079/public/favicon.ico
--------------------------------------------------------------------------------
/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
12 |
13 |
14 | React Toast component
15 |
16 |
17 | You need to enable JavaScript to run this app.
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "React App",
3 | "name": "Create React App Sample",
4 | "icons": [
5 | {
6 | "src": "favicon.ico",
7 | "sizes": "64x64 32x32 24x24 16x16",
8 | "type": "image/x-icon"
9 | },
10 | {
11 | "src": "logo192.png",
12 | "type": "image/png",
13 | "sizes": "192x192"
14 | },
15 | {
16 | "src": "logo512.png",
17 | "type": "image/png",
18 | "sizes": "512x512"
19 | }
20 | ],
21 | "start_url": ".",
22 | "display": "standalone",
23 | "theme_color": "#000000",
24 | "background_color": "#ffffff"
25 | }
26 |
--------------------------------------------------------------------------------
/public/robots.txt:
--------------------------------------------------------------------------------
1 | # https://www.robotstxt.org/robotstxt.html
2 | User-agent: *
3 | Disallow:
4 |
--------------------------------------------------------------------------------
/setup.js:
--------------------------------------------------------------------------------
1 | const fs = require("fs");
2 | const path = require("path");
3 |
4 | const file = path.resolve("./node_modules/babel-preset-react-app/index.js");
5 | const text = fs.readFileSync(file, "utf8");
6 |
7 | if (!text.includes("babel-plugin-relay")) {
8 | if (text.includes("const plugins = [")) {
9 | text = text.replace(
10 | "const plugins = [",
11 | "const plugins = [\n require.resolve('babel-plugin-relay'),"
12 | );
13 | fs.writeFileSync(file, text, "utf8");
14 | } else {
15 | throw new Error(`Failed to inject babel-plugin-relay.`);
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/src/App.css:
--------------------------------------------------------------------------------
1 | .App {
2 | text-align: center;
3 | }
4 |
5 | .App-logo {
6 | height: 40vmin;
7 | pointer-events: none;
8 | }
9 |
10 | @media (prefers-reduced-motion: no-preference) {
11 | .App-logo {
12 | animation: App-logo-spin infinite 20s linear;
13 | }
14 | }
15 |
16 | .App-header {
17 | background-color: #282c34;
18 | min-height: 100vh;
19 | display: flex;
20 | flex-direction: column;
21 | align-items: center;
22 | justify-content: center;
23 | font-size: calc(10px + 2vmin);
24 | color: white;
25 | }
26 |
27 | .App-link {
28 | color: #61dafb;
29 | }
30 |
31 | @keyframes App-logo-spin {
32 | from {
33 | transform: rotate(0deg);
34 | }
35 | to {
36 | transform: rotate(360deg);
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/src/App.tsx:
--------------------------------------------------------------------------------
1 | /* Author: Dalibor Kundrat https://github.com/damikun */
2 | //@ts-nocheck
3 | import React from "react";
4 | import "./App.css";
5 | import HomePage from "./HomePage/Home";
6 | import Layout from "./HomePage/Layout";
7 | import Providers from "./Providers";
8 |
9 | function App() {
10 | return (
11 |
12 |
13 |
14 |
15 |
16 | );
17 | }
18 |
19 | export default App;
20 |
--------------------------------------------------------------------------------
/src/CSS/Tailwind.css:
--------------------------------------------------------------------------------
1 | /* stylelint-disable at-rule-no-unknown */
2 | html,
3 | body {
4 | width: 100%;
5 | height: 100%;
6 | }
7 | input::-ms-clear,
8 | input::-ms-reveal {
9 | display: none;
10 | }
11 | *,
12 | *::before,
13 | *::after {
14 | -webkit-box-sizing: border-box;
15 | box-sizing: border-box;
16 | }
17 | html {
18 | font-family: sans-serif;
19 | line-height: 1.15;
20 | -webkit-text-size-adjust: 100%;
21 | -ms-text-size-adjust: 100%;
22 | -ms-overflow-style: scrollbar;
23 | -webkit-tap-highlight-color: rgba(0, 0, 0, 0);
24 | }
25 | @-ms-viewport {
26 | width: device-width;
27 | }
28 | body {
29 | margin: 0;
30 | color: rgba(0, 0, 0, 0.65);
31 | font-size: 14px;
32 | font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
33 | "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji",
34 | "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
35 | font-variant: tabular-nums;
36 | line-height: 1.5715;
37 | background-color: #fff;
38 | -webkit-font-feature-settings: "tnum";
39 | font-feature-settings: "tnum";
40 | }
41 | [tabindex="-1"]:focus {
42 | outline: none !important;
43 | }
44 |
45 | @import "tailwindcss/base";
46 | @import "tailwindcss/components";
47 |
48 | @import "tailwindcss/utilities";
49 |
--------------------------------------------------------------------------------
/src/Components/Avatar/Avatar.tsx:
--------------------------------------------------------------------------------
1 | /* Author: Dalibor Kundrat https://github.com/damikun */
2 |
3 | import React, { useEffect, useRef, useState } from "react";
4 | import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
5 | import { IconProp } from "@fortawesome/fontawesome-svg-core";
6 | import clsx from "clsx";
7 |
8 | export type AvatarProps = {
9 | src?: string;
10 | lable?: string;
11 | icon?: IconProp;
12 | };
13 |
14 | export default function Avatar({ src, lable, icon }: AvatarProps) {
15 | const [loading, setLoading] = useState(true);
16 | const [error, setLoadError] = useState(false);
17 |
18 | const image = useRef(null);
19 |
20 | useEffect(() => {
21 | if (image?.current?.complete) setLoading(false);
22 | }, []);
23 |
24 | return (
25 |
33 |
37 | {icon ? (
38 |
42 | ) : (
43 |
{lable}
44 | )}
45 |
46 |
50 | {src !== undefined && src !== null && !error && (
51 |
setLoading(false)}
57 | onError={(e) => setLoadError(true)}
58 | />
59 | )}
60 |
61 |
62 | );
63 | }
64 |
--------------------------------------------------------------------------------
/src/Components/Buttons/StayledButton.tsx:
--------------------------------------------------------------------------------
1 | /* Author: Dalibor Kundrat https://github.com/damikun */
2 |
3 | import React from "react";
4 | import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
5 | import { IconProp } from "@fortawesome/fontawesome-svg-core";
6 | import clsx from "clsx";
7 |
8 | export type StayledButtonPorps = {
9 | children?: React.ReactNode;
10 | icon?: IconProp;
11 | isloading?: boolean;
12 | loadingPlaceholder?: React.ReactNode;
13 | variant?: keyof typeof VARIANTS;
14 | } & React.DetailedHTMLProps<
15 | React.ButtonHTMLAttributes,
16 | HTMLButtonElement
17 | >;
18 |
19 | const VARIANTS = {
20 | primarygray: {
21 | base: "text-white hover:bg-gray-600 bg-gray-500",
22 | },
23 |
24 | primaryred: {
25 | base: "text-white hover:bg-red-600 bg-red-500",
26 | },
27 |
28 | primaryorange: {
29 | base: "text-white hover:bg-oragne-600 bg-oragne-500",
30 | },
31 |
32 | primarygreen: {
33 | base: "text-white hover:bg-green-600 bg-green-500",
34 | },
35 |
36 | primaryblue: {
37 | base: "text-white hover:bg-blue-600 bg-blue-500",
38 | },
39 |
40 | secondaryblue: {
41 | base: "text-blue-500 hover:bg-gray-200 bg-gray-100",
42 | },
43 |
44 | invisible: {
45 | base: "",
46 | },
47 | };
48 |
49 | export default function StayledButton({
50 | children,
51 | variant = "primarygray",
52 | isloading,
53 | loadingPlaceholder,
54 | icon,
55 | disabled,
56 | onClick,
57 | ...rest
58 | }: StayledButtonPorps) {
59 | const Var = VARIANTS[variant] || VARIANTS.primarygray;
60 |
61 | function HandleClick(event: React.MouseEvent) {
62 | onClick && onClick(event);
63 | }
64 |
65 | return (
66 |
86 |
87 | {icon ? (
88 |
89 |
90 |
94 |
95 | {children && (
96 |
97 | {children ? children : null}
98 |
99 | )}
100 |
101 | ) : (
102 | children
103 | )}
104 |
105 |
106 | );
107 | }
108 |
--------------------------------------------------------------------------------
/src/Components/Select/Select.tsx:
--------------------------------------------------------------------------------
1 | /* Author: Dalibor Kundrat https://github.com/damikun */
2 |
3 | import clsx from "clsx";
4 | import React, { useCallback, useEffect, useRef } from "react";
5 | import { useState } from "react";
6 | import { useOutsideClick } from "../../Hooks/useOutsideClick";
7 |
8 | type SelectProps = {
9 | initinal: number | string;
10 | value?: string;
11 | children: React.ReactNode;
12 | onChange?: (value: string | number) => void;
13 | justify_end?: boolean;
14 | };
15 |
16 | export type SelectContext = {
17 | onSelect(value: string | number): void;
18 | };
19 |
20 | export const SelectContext = React.createContext(
21 | undefined
22 | );
23 |
24 | export default function Select({
25 | children,
26 | onChange,
27 | initinal,
28 | justify_end = false,
29 | value,
30 | }: SelectProps) {
31 | const [open, setIsOpen] = useState(false);
32 |
33 | const [selected, setSelected] = useState(initinal);
34 |
35 | useEffect(() => {
36 | onChange && onChange(selected);
37 | }, [selected, onChange]);
38 |
39 | const ref = useRef(null);
40 |
41 | function onClickOutside() {
42 | setIsOpen(false);
43 | }
44 |
45 | useOutsideClick(ref, onClickOutside);
46 |
47 | const handleSlect = useCallback((value: string | number) => {
48 | setSelected(value);
49 | setIsOpen(false);
50 | }, []);
51 |
52 | const Context = useCallback(() => {
53 | return {
54 | onSelect: handleSlect,
55 | };
56 | }, []);
57 |
58 | return (
59 |
60 | {
63 | e.preventDefault();
64 | setIsOpen((e) => !e);
65 | }}
66 | className={clsx(
67 | "flex flex-col w-full border-2 border-transparent",
68 | "hover:border-gray-300 transition duration-150",
69 | "focus:border-blue-500 rounded-md cursor-pointer"
70 | )}
71 | >
72 |
73 | {value ? value : selected}
74 |
75 |
76 | {open == true && (
77 |
84 | {children}
85 |
86 | )}
87 |
88 |
89 |
90 | );
91 | }
92 |
--------------------------------------------------------------------------------
/src/Components/Select/SelectOption.tsx:
--------------------------------------------------------------------------------
1 | /* Author: Dalibor Kundrat https://github.com/damikun */
2 |
3 | import React, { useContext } from "react";
4 | import { SelectContext } from "./Select";
5 |
6 | type SelectOptionsProps = {
7 | children: React.ReactNode;
8 | value: number | string;
9 | };
10 |
11 | export type CustomDivProps = {
12 | myval: string | number;
13 | } & React.HTMLProps;
14 |
15 | class CustomDiv extends React.Component {
16 | render() {
17 | return
;
18 | }
19 | }
20 |
21 | export default function SelectOptions({ children, value }: SelectOptionsProps) {
22 | const context = useContext(SelectContext);
23 | return (
24 | {
26 | e.preventDefault();
27 | e.stopPropagation();
28 | context?.onSelect(value);
29 | }}
30 | myval={value}
31 | className="hover:bg-gray-100 py-0.5 px-2 cursor-pointer"
32 | >
33 | {children}
34 |
35 | );
36 | }
37 |
--------------------------------------------------------------------------------
/src/Components/Toast/ToastContainer.tsx:
--------------------------------------------------------------------------------
1 | /* Author: Dalibor Kundrat https://github.com/damikun */
2 |
3 | import clsx from "clsx";
4 | import React, { useContext } from "react";
5 | import ToastMessage from "./ToastMessage";
6 | import { ToastContext } from "./ToastProvider";
7 |
8 | export type ToastContainerProps = {
9 | variant?: keyof typeof VARIANTS;
10 | };
11 |
12 | const VARIANTS = {
13 | top_left: {
14 | style: "top-0 left-0",
15 | },
16 | top_right: {
17 | style: "top-0 right-0",
18 | },
19 | bottom_right: {
20 | style: "bottom-0 right-0",
21 | },
22 | bottom_left: {
23 | style: "bottom-0 left-0",
24 | },
25 | top_middle: {
26 | style: "top-0 left-1/2 -translate-x-1/2 transform",
27 | },
28 | bottom_middle: {
29 | style: "bottom-0 left-1/2 -translate-x-1/2 transform",
30 | },
31 | undefined: {
32 | style: "top-0 right-0",
33 | },
34 | };
35 |
36 | export default function ToastContainer({
37 | variant = "top_right",
38 | }: ToastContainerProps) {
39 | const context = useContext(ToastContext);
40 |
41 | const Var = VARIANTS[variant] || VARIANTS.top_right;
42 |
43 | function handleRemove(id: string) {
44 | context?.remove(id);
45 | }
46 |
47 | return (
48 |
55 |
60 | {context?.data.map((toast) => {
61 | return (
62 |
69 |
79 |
80 | );
81 | })}
82 |
83 |
84 | );
85 | }
86 |
--------------------------------------------------------------------------------
/src/Components/Toast/ToastMessage.tsx:
--------------------------------------------------------------------------------
1 | /* Author: Dalibor Kundrat https://github.com/damikun */
2 |
3 | import React, { useEffect } from "react";
4 | import { Toast } from "./ToastProvider";
5 | import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
6 | import {
7 | faTimes,
8 | faExclamationCircle,
9 | faCheck,
10 | faInfoCircle,
11 | } from "@fortawesome/free-solid-svg-icons";
12 | import clsx from "clsx";
13 |
14 | const VARIANTS = {
15 | Info: {
16 | base: "bg-white border-blue-500",
17 | iconstyle: "text-blue-500 ",
18 | icon: faInfoCircle,
19 | name: "Info",
20 | },
21 |
22 | Error: {
23 | base: "bg-white border-red-500 ",
24 | iconstyle: "text-red-500 ",
25 | icon: faExclamationCircle,
26 | name: "Error",
27 | },
28 |
29 | Warning: {
30 | base: "bg-white border-yellow-500",
31 | iconstyle: "text-yellow-500 ",
32 | icon: faExclamationCircle,
33 | name: "Warning",
34 | },
35 |
36 | Success: {
37 | base: "bg-white border-green-500",
38 | iconstyle: "text-green-500 ",
39 | icon: faCheck,
40 | name: "Success",
41 | },
42 | };
43 |
44 | export type Truncate =
45 | | "truncate-1-lines"
46 | | "truncate-2-lines"
47 | | "truncate-3-lines";
48 |
49 | export type ToastMessage = {
50 | id: string;
51 | lifetime?: number;
52 | variant?: keyof typeof VARIANTS | undefined;
53 | onRemove?: (id: string) => void;
54 | truncate?: Truncate;
55 | } & Toast;
56 |
57 | export default function ToastMessage({
58 | id,
59 | header,
60 | message,
61 | lifetime,
62 | onRemove,
63 | truncate = "truncate-1-lines",
64 | icon,
65 | type,
66 | }: ToastMessage) {
67 | const Var = type
68 | ? VARIANTS[type]
69 | : {
70 | base: "bg-white border-gray-600 ",
71 | iconstyle: "",
72 | icon: icon,
73 | name: header,
74 | };
75 |
76 | useEffect(() => {
77 | if (lifetime && onRemove) {
78 | setTimeout(() => {
79 | onRemove(id);
80 | }, lifetime);
81 | }
82 | }, [lifetime]);
83 |
84 | return (
85 |
94 |
95 | {Var.icon && (
96 |
102 |
106 |
107 | )}
108 |
109 |
110 |
{Var.name}
111 |
118 | {message}
119 |
120 |
121 |
onRemove && onRemove(id)}
123 | className={clsx(
124 | "w-10 h-12 mr-2 items-center mx-auto",
125 | "text-center leading-none text-lg"
126 | )}
127 | >
128 |
135 |
136 |
137 |
138 | );
139 | }
140 |
--------------------------------------------------------------------------------
/src/Components/Toast/ToastProvider.tsx:
--------------------------------------------------------------------------------
1 | /* Author: Dalibor Kundrat https://github.com/damikun */
2 |
3 | import { IconProp } from "@fortawesome/fontawesome-svg-core";
4 | import React, { useCallback, useContext, useState } from "react";
5 | import ToastContainer, { ToastContainerProps } from "./ToastContainer";
6 | import { Truncate } from "./ToastMessage";
7 |
8 | /////////////////////////////////////
9 | /// Types
10 | /////////////////////////////////////
11 |
12 | export type ToastProviderProps = {
13 | children: React.ReactNode;
14 | } & ToastContainerProps;
15 |
16 | type TostMessageType = "Info" | "Success" | "Warning" | "Error";
17 |
18 | export type Toast = {
19 | id: string;
20 | lifetime: number;
21 | message: string | React.ReactNode;
22 | type?: TostMessageType;
23 | truncate?: Truncate;
24 | icon?: IconProp;
25 | header?: string;
26 | };
27 |
28 | export type ToastContextType = {
29 | data: Array;
30 | pushError(message: string, lifetime?: number, truncate?: Truncate): void;
31 | pushWarning(message: string, lifetime?: number, truncate?: Truncate): void;
32 | pushSuccess(message: string, lifetime?: number, truncate?: Truncate): void;
33 | pushInfo(message: string, lifetime?: number, truncate?: Truncate): void;
34 | push(
35 | message: string,
36 | type: TostMessageType,
37 | lifetime?: number,
38 | truncate?: Truncate
39 | ): void;
40 | pushCustom(
41 | message: string | React.ReactNode,
42 | lifetime: number,
43 | truncate?: Truncate,
44 | icon?: IconProp | React.ReactNode
45 | ): void;
46 | remove(id: string): void;
47 | };
48 |
49 | /////////////////////////////////////
50 | /// Global and Helpers
51 | /////////////////////////////////////
52 |
53 | export const ToastContext = React.createContext(
54 | undefined
55 | );
56 |
57 | function uuidv4() {
58 | return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, function (c) {
59 | var r = (Math.random() * 16) | 0,
60 | v = c == "x" ? r : (r & 0x3) | 0x8;
61 | return v.toString(16);
62 | });
63 | }
64 |
65 | export const useToast = () => useContext(ToastContext);
66 |
67 | const DEFAULT_INTERVAL = 2500;
68 |
69 | /////////////////////////////////////
70 | /// Implementation
71 | /////////////////////////////////////
72 |
73 | export default function ToastProvider({
74 | children,
75 | variant,
76 | }: ToastProviderProps) {
77 | const [data, setData] = useState>([]);
78 |
79 | const Push = useCallback(
80 | (
81 | message: string,
82 | type: TostMessageType,
83 | lifetime?: number,
84 | truncate?: Truncate
85 | ) => {
86 | if (message) {
87 | const new_item: Toast = {
88 | id: uuidv4(),
89 | message: message,
90 | type: type,
91 | lifetime: lifetime ? lifetime : DEFAULT_INTERVAL,
92 | truncate: truncate,
93 | };
94 |
95 | setData((prevState) => [...prevState, new_item]);
96 | }
97 | },
98 | [setData, data]
99 | );
100 |
101 | const PushCustom = useCallback(
102 | (
103 | message: string | React.ReactNode,
104 | lifetime?: number,
105 | truncate?: Truncate,
106 | icon?: IconProp
107 | ) => {
108 | if (message) {
109 | const new_item: Toast = {
110 | id: uuidv4(),
111 | message: message,
112 | lifetime: lifetime ? lifetime : DEFAULT_INTERVAL,
113 | truncate: truncate,
114 | icon: icon,
115 | type: undefined,
116 | };
117 |
118 | setData((prevState) => [...prevState, new_item]);
119 | }
120 | },
121 | [setData, data]
122 | );
123 |
124 | const PushError = useCallback(
125 | (message: string, lifetime?: number, truncate?: Truncate) =>
126 | Push(message, "Error", lifetime, truncate),
127 | [Push]
128 | );
129 | const PushWarning = useCallback(
130 | (message: string, lifetime?: number, truncate?: Truncate) =>
131 | Push(message, "Warning", lifetime, truncate),
132 | [Push]
133 | );
134 | const PushSuccess = useCallback(
135 | (message: string, lifetime?: number, truncate?: Truncate) =>
136 | Push(message, "Success", lifetime, truncate),
137 | [Push]
138 | );
139 | const PushInfo = useCallback(
140 | (message: string, lifetime?: number, truncate?: Truncate) =>
141 | Push(message, "Info", lifetime, truncate),
142 | [Push]
143 | );
144 |
145 | const ToastContexd = useCallback(() => {
146 | return {
147 | data: data,
148 | pushError: PushError,
149 | pushWarning: PushWarning,
150 | pushSuccess: PushSuccess,
151 | pushInfo: PushInfo,
152 | push: Push,
153 | pushCustom: PushCustom,
154 |
155 | async remove(id: string) {
156 | setData((prevState) => prevState.filter((e) => e.id != id));
157 | },
158 | };
159 | }, [
160 | data,
161 | setData,
162 | PushError,
163 | PushWarning,
164 | PushSuccess,
165 | PushInfo,
166 | Push,
167 | PushCustom,
168 | ]);
169 |
170 | return (
171 |
172 |
173 | {children}
174 |
175 | );
176 | }
177 |
--------------------------------------------------------------------------------
/src/Components/ToastDummyMessage.tsx:
--------------------------------------------------------------------------------
1 | /* Author: Dalibor Kundrat https://github.com/damikun */
2 |
3 | import clsx from "clsx";
4 | import React from "react";
5 | import Avatar from "./Avatar/Avatar";
6 |
7 | export default function ToastDummyMessage() {
8 | return (
9 |
10 |
11 |
17 |
28 |
29 |
37 | Dalibor
38 |
39 |
40 |
41 |
This is custom toast
42 |
Builded by using React hooks and context API
43 |
44 |
45 |
46 | );
47 | }
48 |
--------------------------------------------------------------------------------
/src/HomePage/Home.tsx:
--------------------------------------------------------------------------------
1 | import React, { useEffect, useState } from "react";
2 | import clsx from "clsx";
3 | import StayledButton from "../Components/Buttons/StayledButton";
4 | import { useToast } from "../Components/Toast/ToastProvider";
5 | import Select from "../Components/Select/Select";
6 | import SelectOptions from "../Components/Select/SelectOption";
7 | import ToastDummyMessage from "../Components/ToastDummyMessage";
8 | import { useGlobalState } from "../Providers";
9 |
10 | const text = `Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum`;
11 |
12 | export default function HomePage() {
13 | const toast = useToast();
14 | const [interval, setInterval] = useState(1500);
15 | const [position, setPosition] = useState("top_right");
16 | const globalState = useGlobalState();
17 |
18 | useEffect(() => {
19 | //@ts-ignore
20 | globalState?.setPosition({ variant: position });
21 | }, [position]);
22 |
23 | return (
24 |
30 |
31 |
32 |
33 |
34 |
35 |
41 | setInterval(value as number)}
44 | >
45 | 1000 ms
46 | 2500 ms
47 | 5000 ms
48 |
49 |
50 |
51 |
52 |
53 |
59 | setPosition(value as string)}
64 | >
65 | Top-Right
66 | Top-Middle
67 | Top-Left
68 | Bottom-Right
69 |
70 | Bottom-Middle
71 |
72 | Bottom-Left
73 |
74 |
75 |
76 |
77 |
78 |
79 |
85 | toast?.pushError("Oppps Error", interval)}
88 | >
89 | Error
90 |
91 |
94 | toast?.pushWarning(
95 | "Warning appear",
96 | interval,
97 | "truncate-2-lines"
98 | )
99 | }
100 | >
101 | Warning
102 |
103 | toast?.pushSuccess("Action success", interval)}
106 | >
107 | Success
108 |
109 | toast?.pushInfo("Info message", interval)}
112 | >
113 | Info
114 |
115 | toast?.pushCustom( , interval)}
118 | >
119 | Custom
120 |
121 |
122 |
123 |
124 |
125 |
131 |
134 | toast?.pushInfo(text, interval, "truncate-1-lines")
135 | }
136 | >
137 | 1 line
138 |
139 |
142 | toast?.pushInfo(text, interval, "truncate-2-lines")
143 | }
144 | >
145 | 2 lines
146 |
147 |
150 | toast?.pushInfo(text, interval, "truncate-3-lines")
151 | }
152 | >
153 | 3 lines
154 |
155 |
156 |
157 |
158 |
159 | );
160 | }
161 |
162 | /////////////////////////////////
163 | /////////////////////////////////
164 |
165 | type SectionHeaderProps = {
166 | name: string;
167 | justify_end?: boolean;
168 | };
169 | function SectionHeader({ justify_end, name }: SectionHeaderProps) {
170 | return (
171 |
177 | {name}
178 |
179 | );
180 | }
181 | function GetPositionText(value: string) {
182 | switch (value) {
183 | case "top_right":
184 | return "Top-Right";
185 | case "top_left":
186 | return "Top-Left";
187 | case "top_middle":
188 | return "Top-Middle";
189 | case "bottom_right":
190 | return "Bottom-Right";
191 | case "bottom_left":
192 | return "Bottom-Left";
193 | case "bottom_middle":
194 | return "Bottom-Middle";
195 | }
196 | }
197 |
--------------------------------------------------------------------------------
/src/HomePage/Layout.tsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 |
3 | type LayoutProps = {
4 | children: React.ReactNode;
5 | };
6 | export default function Layout({ children }: LayoutProps) {
7 | return {children}
;
8 | }
9 |
--------------------------------------------------------------------------------
/src/Hooks/useOutsideClick.tsx:
--------------------------------------------------------------------------------
1 | import { useEffect } from "react";
2 |
3 | export function useOutsideClick(ref: any, onEvent: () => void) {
4 | useEffect(() => {
5 | function handleClickOutside(event: any) {
6 | if (ref.current && !ref.current.contains(event.target)) {
7 | onEvent();
8 | }
9 | }
10 |
11 | document.addEventListener("mousedown", handleClickOutside);
12 |
13 | return () => {
14 | document.removeEventListener("mousedown", handleClickOutside);
15 | };
16 | }, [ref]);
17 | }
18 |
--------------------------------------------------------------------------------
/src/Providers.tsx:
--------------------------------------------------------------------------------
1 | import React, { useContext, useState } from "react";
2 | import { ToastContainerProps } from "./Components/Toast/ToastContainer";
3 | import ToastProvider from "./Components/Toast/ToastProvider";
4 |
5 | export type GlobalContextType = {
6 | position: ToastContainerProps;
7 | // will believe that user pass correct string:)
8 | setPosition: React.Dispatch>;
9 | };
10 |
11 | export const GlobalContext = React.createContext(
12 | undefined
13 | );
14 |
15 | export const useGlobalState = () => useContext(GlobalContext);
16 |
17 | type ProvidersProps = {
18 | children: React.ReactNode;
19 | };
20 |
21 | const Providers = ({ children }: ProvidersProps) => {
22 | const [position, setPosition] = useState({
23 | variant: "top_right",
24 | });
25 |
26 | return (
27 |
33 | {children}
34 |
35 | );
36 | };
37 |
38 | export default Providers;
39 |
--------------------------------------------------------------------------------
/src/index.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import App from './App';
4 | import "./CSS/App.css";
5 |
6 | ReactDOM.render(
7 |
8 |
9 | ,
10 | document.getElementById('root')
11 | );
12 |
13 |
--------------------------------------------------------------------------------
/src/react-app-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
--------------------------------------------------------------------------------
/tailwind.config.js:
--------------------------------------------------------------------------------
1 | const defaultTheme = require("tailwindcss/defaultTheme");
2 |
3 | const colors = require("tailwindcss/colors");
4 |
5 | module.exports = {
6 | purge: {
7 | preserveHtmlElements: true,
8 | enabled: true,
9 | content: ["./src/**/*.{ts,tsx}"],
10 | },
11 | theme: {
12 | colors: {
13 | transparent: "transparent",
14 | current: "currentColor",
15 | black: "#000",
16 | white: "#fff",
17 | gray: colors.coolGray,
18 | red: colors.red,
19 | blue: colors.blue,
20 | yellow: colors.amber,
21 | indigo: colors.indigo,
22 | oragne: colors.orange,
23 | purple: colors.purple,
24 | pink: colors.pink,
25 | green: colors.green,
26 | white: colors.white,
27 | black: colors.black,
28 | },
29 |
30 | extend: {
31 | scale: {
32 | 102: "1.02",
33 | },
34 |
35 | truncate: {
36 | lines: {
37 | 1: "1",
38 | 2: "2",
39 | 3: "3",
40 | 5: "5",
41 | 8: "8",
42 | },
43 | },
44 | },
45 | },
46 | plugins: [
47 | require("@tailwindcss/typography"),
48 | require("tailwindcss-truncate-multiline")(),
49 | ],
50 | corePlugins: {
51 | alignContent: true,
52 | gradientColorStops: true,
53 | },
54 | variants: {
55 | gradientColorStops: [
56 | "responsive",
57 | "hover",
58 | "focus",
59 | "active",
60 | "group-hover",
61 | ],
62 | boxShadow: ["responsive", "hover", "focus"],
63 | outline: ["responsive", "focus"],
64 | backgroundColor: ["responsive", "hover", "focus"],
65 | borderColor: [
66 | "responsive",
67 | "hover",
68 | "focus",
69 | "focus-within",
70 | "active",
71 | "group-hover",
72 | ],
73 | borderWidth: ["responsive", "last", "hover", "focus", "focus-within"],
74 | margin: ["responsive", "last"],
75 | alignContent: ["responsive", "hover", "focus"],
76 | },
77 | };
78 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es5",
4 | "lib": [
5 | "dom",
6 | "dom.iterable",
7 | "esnext",
8 | "es2019"
9 | ],
10 | "allowJs": true,
11 | "skipLibCheck": true,
12 | "esModuleInterop": true,
13 | "allowSyntheticDefaultImports": true,
14 | "strict": true,
15 | "forceConsistentCasingInFileNames": true,
16 | "module": "esnext",
17 | "moduleResolution": "node",
18 | "resolveJsonModule": true,
19 | "isolatedModules": true,
20 | "noEmit": true,
21 | "jsx": "react-jsx",
22 | "strictNullChecks": true,
23 | "suppressImplicitAnyIndexErrors": true,
24 | "noUnusedLocals": true,
25 | "noFallthroughCasesInSwitch": true
26 | },
27 | "include": [
28 | "src",
29 | "src/Components/Groups/GroupListView.tsx",
30 | "babel.config.js"
31 | ],
32 | "exclude": [
33 | "node_modules",
34 | "build",
35 | "scripts",
36 | "acceptance-tests",
37 | "webpack",
38 | "jest",
39 | "src/setupTests.ts"
40 | ]
41 | }
42 |
--------------------------------------------------------------------------------