Repository: timolins/react-hot-toast Branch: main Commit: f339d7105c90 Files: 55 Total size: 118.4 KB Directory structure: gitextract_zguhd_ms/ ├── .github/ │ └── workflows/ │ ├── main.yml │ └── size.yml ├── .gitignore ├── LICENSE ├── README.md ├── jest.config.js ├── package.json ├── site/ │ ├── .gitignore │ ├── README.md │ ├── components/ │ │ ├── code.tsx │ │ ├── docs-layout.tsx │ │ ├── emoji-button.tsx │ │ └── sections/ │ │ ├── footer.tsx │ │ ├── splitbee-counter.tsx │ │ ├── toast-example.tsx │ │ └── toaster-example.tsx │ ├── next-env.d.ts │ ├── next.config.mjs │ ├── package.json │ ├── pages/ │ │ ├── _app.tsx │ │ ├── docs/ │ │ │ ├── index.mdx │ │ │ ├── multi-toaster.mdx │ │ │ ├── styling.mdx │ │ │ ├── toast-bar.mdx │ │ │ ├── toast.mdx │ │ │ ├── toaster.mdx │ │ │ ├── use-toaster-store.mdx │ │ │ ├── use-toaster.mdx │ │ │ └── version-2.mdx │ │ └── index.tsx │ ├── postcss.config.js │ ├── styles/ │ │ ├── main.css │ │ ├── prism-theme.css │ │ └── tailwind-utils.css │ ├── tailwind.config.js │ ├── tsconfig.json │ └── types/ │ ├── mdx.d.ts │ └── svg.d.ts ├── src/ │ ├── components/ │ │ ├── checkmark.tsx │ │ ├── error.tsx │ │ ├── loader.tsx │ │ ├── toast-bar.tsx │ │ ├── toast-icon.tsx │ │ └── toaster.tsx │ ├── core/ │ │ ├── store.ts │ │ ├── toast.ts │ │ ├── types.ts │ │ ├── use-toaster.ts │ │ └── utils.ts │ ├── headless/ │ │ └── index.ts │ └── index.ts ├── test/ │ ├── setup.ts │ └── toast.test.tsx ├── tsconfig.json └── tsup.config.ts ================================================ FILE CONTENTS ================================================ ================================================ FILE: .github/workflows/main.yml ================================================ name: CI on: [push] jobs: build: name: Build & test runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: pnpm/action-setup@v4 - name: Use Node.js ${{ matrix.node-version }} uses: actions/setup-node@v4 with: node-version: ${{ matrix.node-version }} cache: 'pnpm' - name: Install dependencies run: pnpm install - name: Build package run: pnpm build - name: Test run: pnpm run test --ci --coverage ================================================ FILE: .github/workflows/size.yml ================================================ name: size on: [pull_request] jobs: size: runs-on: ubuntu-latest env: CI_JOB_NUMBER: 1 steps: - uses: actions/checkout@v4 - uses: pnpm/action-setup@v4 - uses: actions/setup-node@v4 with: node-version: 20.x cache: 'pnpm' - run: pnpm install - uses: andresz1/size-limit-action@v1 with: github_token: ${{ secrets.GITHUB_TOKEN }} ================================================ FILE: .gitignore ================================================ *.log .DS_Store node_modules .cache coverage dist /headless .vscode .vercel ================================================ FILE: LICENSE ================================================ MIT License Copyright (c) 2020 Timo Lins Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: README.md ================================================ react-hot-toast - Try it out
NPM Version minzipped size Build Status

Smoking hot Notifications for React.
Lightweight, customizable and beautiful by default.

Website · Documentation · Twitter

Cooked by Timo Lins 👨‍🍳

## Features - 🔥 **Hot by default** - 🔩 **Easily Customizable** - ⏳ **Promise API** - _Automatic loader from a promise_ - 🕊 **Lightweight** - _less than 5kb including styles_ - ✅ **Accessible** - 🤯 **Headless Hooks** - _Create your own with [`useToaster()`](https://react-hot-toast.com/docs/use-toaster)_ ## Installation #### With pnpm ```sh pnpm add react-hot-toast ``` #### With NPM ```sh npm install react-hot-toast ``` ## Getting Started Add the Toaster to your app first. It will take care of rendering all notifications emitted. Now you can trigger `toast()` from anywhere! ```jsx import toast, { Toaster } from 'react-hot-toast'; const notify = () => toast('Here is your toast.'); const App = () => { return (
); }; ``` ## Documentation Find the full API reference on [official documentation](https://react-hot-toast.com/docs). ================================================ FILE: jest.config.js ================================================ /** @type {import('ts-jest/dist/types').InitialOptionsTsJest} */ module.exports = { preset: 'ts-jest', testEnvironment: 'jsdom', setupFilesAfterEnv: ['/test/setup.ts'], }; ================================================ FILE: package.json ================================================ { "name": "react-hot-toast", "description": "Smoking hot React Notifications. Lightweight, customizable and beautiful by default.", "version": "2.6.0", "author": "Timo Lins", "license": "MIT", "repository": "timolins/react-hot-toast", "keywords": [ "react", "notifications", "toast", "snackbar" ], "main": "dist/index.js", "types": "dist/index.d.ts", "exports": { "./package.json": "./package.json", ".": { "types": "./dist/index.d.ts", "import": "./dist/index.mjs", "require": "./dist/index.js" }, "./headless": { "types": "./headless/index.d.ts", "import": "./headless/index.mjs", "require": "./headless/index.js" } }, "files": [ "headless", "dist", "src" ], "engines": { "node": ">=10" }, "scripts": { "start": "tsup --watch", "build": "tsup", "test": "jest --runInBand", "setup": "pnpm i && cd site && pnpm i && cd .. && pnpm run link", "link": "pnpm link ./site/node_modules/react && pnpm link ./site/node_modules/react-dom", "size": "size-limit" }, "husky": { "hooks": { "pre-commit": "prettier src --ignore-unknown --write" } }, "prettier": { "printWidth": 80, "semi": true, "singleQuote": true, "arrowParens": "always", "trailingComma": "es5" }, "size-limit": [ { "path": "dist/index.js", "limit": "5.5 KB" }, { "path": "dist/index.mjs", "limit": "5.5 KB" }, { "path": "headless/index.js", "limit": "2.5 KB" }, { "path": "headless/index.mjs", "limit": "2.5 KB" } ], "devDependencies": { "@jest/types": "^29.6.3", "@size-limit/preset-small-lib": "^7.0.8", "@testing-library/jest-dom": "^6.6.3", "@testing-library/react": "^16.1.0", "@types/jest": "^29.5.14", "@types/react": "^18.3.18", "@types/react-dom": "^18.3.5", "esbuild-minify-templates": "^0.13.1", "jest": "^29.7.0", "jest-environment-jsdom": "^29.7.0", "prettier": "^2.8.8", "react": "^18.3.1", "react-dom": "^18.3.1", "size-limit": "^7.0.8", "ts-jest": "^29.2.5", "tslib": "^2.8.1", "tsup": "^6.7.0", "typescript": "^5.7.2" }, "dependencies": { "csstype": "^3.1.3", "goober": "^2.1.16" }, "peerDependencies": { "react": ">=16", "react-dom": ">=16" }, "packageManager": "pnpm@9.15.4+sha512.b2dc20e2fc72b3e18848459b37359a32064663e5627a51e4c74b2c29dd8e8e0491483c3abb40789cfd578bf362fb6ba8261b05f0387d76792ed6e23ea3b1b6a0" } ================================================ FILE: site/.gitignore ================================================ # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. # dependencies /node_modules /.pnp .pnp.js # testing /coverage # next.js /.next/ /out/ # production /build # misc .DS_Store *.pem # debug npm-debug.log* yarn-debug.log* yarn-error.log* # local env files .env.local .env.development.local .env.test.local .env.production.local # vercel .vercel ================================================ FILE: site/README.md ================================================ This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app). ## Getting Started First, run the development server: ```bash npm run dev # or yarn dev ``` Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. You can start editing the page by modifying `pages/index.js`. The page auto-updates as you edit the file. ## Learn More To learn more about Next.js, take a look at the following resources: - [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API. - [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial. You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome! ## Deploy on Vercel The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/import?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js. Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details. ================================================ FILE: site/components/code.tsx ================================================ import clsx from 'clsx'; import Highlight, { defaultProps, Language, PrismTheme, } from 'prism-react-renderer'; const theme: PrismTheme = { plain: { backgroundColor: '#351e11', color: '#d6ceff', }, styles: [ { types: ['comment', 'prolog', 'doctype', 'cdata', 'punctuation'], style: { color: '#6c6783', }, }, { types: ['namespace'], style: { opacity: 0.7, }, }, { types: ['tag', 'operator', 'number', 'module'], style: { color: '#e09142', }, }, { types: ['property', 'function'], style: { color: '#9a86fd', }, }, { types: ['tag-id', 'selector', 'atrule-id'], style: { color: '#eeebff', }, }, { types: ['attr-name'], style: { color: '#c4b9fe', }, }, { types: [ 'boolean', 'string', 'entity', 'url', 'attr-value', 'keyword', 'control', 'directive', 'unit', 'statement', 'regex', 'at-rule', 'placeholder', 'variable', ], style: { color: '#ffcc99', }, }, { types: ['deleted'], style: { textDecorationLine: 'line-through', }, }, { types: ['inserted'], style: { textDecorationLine: 'underline', }, }, { types: ['italic'], style: { fontStyle: 'italic', }, }, { types: ['important', 'bold'], style: { fontWeight: 'bold', }, }, { types: ['important'], style: { color: '#c4b9fe', }, }, ], }; export const Code: React.FC<{ snippet: string; language?: Language; className?: string; }> = (props) => { const language = props.language || 'jsx'; return ( {({ className, style, tokens, getLineProps, getTokenProps }) => (
          {tokens.map((line, i) => {
            if (tokens.length - 1 === i && line[0].empty) {
              return null;
            }

            return (
              
{line.map((token, key) => ( ))}
); })}
)}
); }; ================================================ FILE: site/components/docs-layout.tsx ================================================ import * as React from 'react'; import { Toaster } from 'react-hot-toast'; import { NextSeo } from 'next-seo'; import Link from 'next/link'; import { Footer } from './sections/footer'; import Logo from '../assets/logo-small.svg'; const TableItem: React.FC<{ href: string; children?: React.ReactNode; }> = ({ children, href }) => ( {children} ); const TableHeader: React.FC<{ children?: React.ReactNode; }> = ({ children }) => ( {children} ); export default function DocsLayout({ meta, children }) { return (
GitHub
{children}
); } ================================================ FILE: site/components/emoji-button.tsx ================================================ export const EmojiButton: React.FC<{ onClick: () => void; emoji: string | React.ReactElement; children?: React.ReactNode; }> = ({ onClick, children, emoji }) => ( ); ================================================ FILE: site/components/sections/footer.tsx ================================================ import React from 'react'; import Link from 'next/link'; export function Footer({ noBadge }: { noBadge?: boolean }) { return ( ); } ================================================ FILE: site/components/sections/splitbee-counter.tsx ================================================ import React from 'react'; import clsx from 'clsx'; export const useSplitbeeCount = ( event: T, token: string ): number => { const [data, setData] = React.useState(0); const socket = React.useRef(null); React.useEffect(() => { if (typeof window !== undefined) { socket.current = new WebSocket('wss://realtime.react-hot-toast.com/'); socket.current.onopen = (e) => { socket.current.send( JSON.stringify({ type: 'subscribe', data: { token: token, events: [event], }, }) ); }; socket.current.onmessage = (e) => { const d = JSON.parse(e.data); setData(d.count); }; return () => {}; } }, []); return data; }; export const SplitbeeCounter = () => { const count = useSplitbeeCount('Trigger Toast', 'NTV7AYBLEXW3'); const letters = count.toString().split(''); return (
Toasts made on this website so far
{letters.map((l, i) => (
{l}
))}
⚡️ Real-time analytics by{' '} Splitbee
); }; ================================================ FILE: site/components/sections/toast-example.tsx ================================================ import React, { useState } from 'react'; import toast from 'react-hot-toast'; import { EmojiButton } from '../emoji-button'; import { Code } from '../code'; const examples: Array<{ title: string; action: () => void; emoji: string; snippet: string; }> = [ { title: 'Success', emoji: '✅', snippet: "toast.success('Successfully toasted!')", action: () => { toast.success('Successfully toasted!'); }, }, { title: 'Error', emoji: '❌', snippet: `toast.error("This didn't work.")`, action: () => { toast.error("This didn't work."); }, }, { title: 'Promise', emoji: '⏳', snippet: `toast.promise( saveSettings(settings), { loading: 'Saving...', success: Settings saved!, error: Could not save., } );`, action: () => { const promise = new Promise((res, rej) => { setTimeout(Math.random() > 0.5 ? res : rej, 1000); }); toast.promise( promise, { loading: 'Saving...', success: Settings saved!, error: Could not save., }, { style: { width: '200px', paddingRight: '10px', }, } ); }, }, { title: 'Multi Line', emoji: '↕️', snippet: `toast( "This toast is super big. I don't think anyone could eat it in one bite.\\n\\nIt's larger than you expected. You eat it but it does not seem to get smaller.", { duration: 6000, } );`, action: () => { toast( "This toast is super big. I don't think anyone could eat it in one bite.\n\n It's larger than you expected. You eat it but it does not seem to get smaller.", { duration: 6000, } ); }, }, { title: 'Emoji', emoji: '👏', snippet: `toast('Good Job!', { icon: '👏', });`, action: () => { toast('Good Job!', { icon: '👏', }); }, }, { title: 'Dark Mode', emoji: '🌚', snippet: `toast('Hello Darkness!', { icon: '👏', style: { borderRadius: '10px', background: '#333', color: '#fff', }, } );`, action: () => { toast('Hello Darkness!', { icon: '👏', style: { borderRadius: '200px', background: '#333', color: '#fff', }, }); }, }, { title: 'JSX Content', emoji: '🔩', snippet: `toast((t) => ( Custom and bold ));`, action: () => { toast((t) => ( Custom and bold )); }, }, { title: 'Themed', emoji: '🎨', snippet: `toast.success('Look at my styles.', { style: { border: '1px solid #713200', padding: '16px', color: '#713200', }, iconTheme: { primary: '#713200', secondary: '#FFFAEE', }, });`, action: () => { toast.success('Look at my styles.', { style: { border: '1px solid #713200', padding: '16px', color: '#713200', }, iconTheme: { primary: '#713200', secondary: '#FFFAEE', }, }); }, }, { title: 'Custom Position', emoji: '⬆️', snippet: `toast.success('Always at the bottom.', { position: "bottom-center" })`, action: () => { toast.success('Always at the bottom.', { position: 'bottom-center', duration: 10000, }); }, }, { title: 'TailwindCSS', emoji: '️💨', snippet: `toast.custom((t) => (

Emilia Gates

Sure! 8:30pm works great!

))`, action: () => { // toast.custom(); toast.custom( (t) => (

Emilia Gates

Sure! 8:30pm works great!

), { duration: 10000, } ); }, }, ]; export const ToastExample = () => { const [snippet, setSnippet] = useState(examples[0].snippet); return (
{examples.map((e) => ( { if (e.snippet) { setSnippet(e.snippet); } (window as any).splitbee?.track('Trigger Toast', { example: e.title, }); e.action(); }} > {e.title} ))}
); }; ================================================ FILE: site/components/sections/toaster-example.tsx ================================================ import clsx from 'clsx'; import toast, { ToastPosition } from 'react-hot-toast'; import Arrow from '../../assets/arrow.svg'; import { Code } from '../code'; import { EmojiButton } from '../emoji-button'; export const positions: Array = [ 'top-left', 'top-center', 'top-right', 'bottom-left', 'bottom-center', 'bottom-right', ]; export const ToasterExample: React.FC<{ position: ToastPosition; onPosition: (pos: ToastPosition) => void; reverse: boolean; onReverse: (rev: boolean) => void; }> = ({ position, onPosition, reverse, onReverse }) => { const reverseIt = () => { setTimeout(() => { toast('Notification 1', { icon: '1️⃣', id: 'reverse-1', }); }, 10); setTimeout( () => toast('Notification 2', { icon: '2️⃣', id: 'reverse-2', }), 250 ); setTimeout( () => toast('Notification 3', { icon: '3️⃣', id: 'reverse-3', }), 500 ); setTimeout( () => toast('Notification 4', { icon: '4️⃣', id: 'reverse-4', }), 750 ); (window as any).splitbee?.track('Change Order', { reverseOrder: !reverse, }); onReverse(!reverse); }; const renderPosition = (p: ToastPosition) => ( ); return (
`} />
{positions.map((p) => renderPosition(p))}
} onClick={reverseIt} > Toggle Direction
); }; ================================================ FILE: site/next-env.d.ts ================================================ /// /// // NOTE: This file should not be edited // see https://nextjs.org/docs/basic-features/typescript for more information. ================================================ FILE: site/next.config.mjs ================================================ import rehypeSlug from 'rehype-slug'; import remarkGfm from 'remark-gfm'; import nextMdx from '@next/mdx'; const withMDX = nextMdx({ extension: /\.mdx?$/, options: { rehypePlugins: [rehypeSlug], remarkPlugins: [remarkGfm], providerImportSource: '@mdx-js/react', }, }); /** @type {import('next').NextConfig} */ const nextConfig = { pageExtensions: ['ts', 'tsx', 'md', 'mdx'], webpack(config) { config.module.rules.push({ test: /\.svg$/, use: ['@svgr/webpack'], }); return config; }, async rewrites() { return [ { source: '/bee.js', destination: 'https://cdn.splitbee.io/sb.js', }, { source: '/_hive/:slug', destination: 'https://hive.splitbee.io/:slug', }, ]; }, }; export default withMDX(nextConfig); ================================================ FILE: site/package.json ================================================ { "name": "site", "scripts": { "dev": "next dev", "build": "next build", "start": "next start" }, "dependencies": { "@mdx-js/loader": "^2.3.0", "@mdx-js/react": "^2.3.0", "@next/mdx": "^12.3.4", "@svgr/webpack": "^6.5.1", "@types/prismjs": "^1.26.5", "@vercel/analytics": "^0.1.11", "clsx": "^1.1.1", "next": "^12.3.4", "next-seo": "^5.15.0", "postcss": "^8.4.49", "prism-react-renderer": "^1.3.5", "react": "^18.3.1", "react-dom": "^18.3.1", "react-hot-toast": "link:../", "rehype-slug": "^5.1.0" }, "devDependencies": { "@tailwindcss/typography": "^0.5.15", "@types/node": "^18.19.68", "@types/react": "^18.3.18", "@types/react-dom": "^18.3.5", "autoprefixer": "^10.4.20", "remark-gfm": "^3.0.1", "tailwindcss": "^3.4.17", "typescript": "^4.9.5" } } ================================================ FILE: site/pages/_app.tsx ================================================ import '../styles/tailwind-utils.css'; import '../styles/main.css'; import * as React from 'react'; import Link from 'next/link'; import Head from 'next/head'; import { Analytics } from '@vercel/analytics/react'; import { MDXProvider } from '@mdx-js/react'; import { Code } from '../components/code'; const components = { a: (props) => ( ), h1: (props) => { const id = props.id || ''; return (

{props.children}

); }, h2: (props) => { const id = props.id || ''; return (

{props.children}

); }, h3: (props) => { const id = props.id || ''; return (

{props.children}

); }, code: (props) => props.className ? ( ) : ( ), }; function MyApp({ Component, pageProps }) { return ( <> {process.browser && (