| 1 | /** |
| 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. |
| 3 | * |
| 4 | * This source code is licensed under the MIT license found in the |
| 5 | * LICENSE file in the root directory of this source tree. |
| 6 | */ |
| 7 | |
| 8 | import { |
| 9 | BanIcon, |
| 10 | ExclamationIcon, |
| 11 | InformationCircleIcon, |
| 12 | XIcon, |
| 13 | } from '@heroicons/react/solid'; |
| 14 | import {CustomContentProps, SnackbarContent, useSnackbar} from 'notistack'; |
| 15 | import {forwardRef} from 'react'; |
| 16 | import {MessageLevel, MessageSource} from '../lib/stores'; |
| 17 | |
| 18 | // https://notistack.com/examples/advanced/custom-component#custom-variant-(typescript) |
| 19 | declare module 'notistack' { |
| 20 | interface VariantOverrides { |
| 21 | message: { |
| 22 | title: string; |
| 23 | level: MessageLevel; |
| 24 | codeframe: string | undefined; |
| 25 | }; |
| 26 | } |
| 27 | } |
| 28 | |
| 29 | interface MessageProps extends CustomContentProps { |
| 30 | title: string; |
| 31 | level: MessageLevel; |
| 32 | source: MessageSource; |
| 33 | codeframe: string | undefined; |
| 34 | } |
| 35 | |
| 36 | const Message = forwardRef<HTMLDivElement, MessageProps>( |
| 37 | ({id, title, level, source, codeframe}, ref) => { |
| 38 | const {closeSnackbar} = useSnackbar(); |
| 39 | const isDismissible = source !== MessageSource.Playground; |
| 40 | |
| 41 | return ( |
| 42 | <SnackbarContent |
| 43 | ref={ref} |
| 44 | className="flex items-start justify-between gap-3 px-4 py-3 text-sm bg-white border rounded-md shadow w-toast"> |
| 45 | <div className="flex gap-3 w-toast-body"> |
| 46 | {level === MessageLevel.Warning ? ( |
| 47 | <div className="flex items-center justify-center flex-none rounded-md w-7 h-7 bg-amber-100"> |
| 48 | <ExclamationIcon className="w-5 h-5 text-amber-600" /> |
| 49 | </div> |
| 50 | ) : level === MessageLevel.Error ? ( |
| 51 | <div className="flex items-center justify-center flex-none bg-red-100 rounded-md w-7 h-7"> |
| 52 | <BanIcon className="w-5 h-5 text-red-600" /> |
| 53 | </div> |
| 54 | ) : ( |
| 55 | <div className="flex items-center justify-center flex-none rounded-md bg-sky-100 w-7 h-7"> |
| 56 | <InformationCircleIcon className="w-5 h-5 text-sky-600" /> |
| 57 | </div> |
| 58 | )} |
| 59 | <div className="flex flex-col justify-center gap-1 w-toast-title"> |
| 60 | <p className="w-full">{title}</p> |
| 61 | {codeframe ? ( |
| 62 | <pre className="overflow-x-auto break-words whitespace-pre-wrap"> |
| 63 | <code className="text-xs">{codeframe}</code> |
| 64 | </pre> |
| 65 | ) : null} |
| 66 | </div> |
| 67 | </div> |
| 68 | {isDismissible ? ( |
| 69 | <button |
| 70 | className="flex items-center justify-center flex-none transition-colors duration-150 ease-in rounded-md justify-self-end group w-7 h-7 hover:bg-gray-200" |
| 71 | onClick={() => closeSnackbar(id)}> |
| 72 | <XIcon className="w-5 h-5 fill-gray-500 group-hover:fill-gray-800" /> |
| 73 | </button> |
| 74 | ) : null} |
| 75 | </SnackbarContent> |
| 76 | ); |
| 77 | }, |
| 78 | ); |
| 79 | |
| 80 | Message.displayName = 'MessageComponent'; |
| 81 | |
| 82 | export default Message; |