diff --git a/packages/alert/.storybook/main.js b/packages/alert/.storybook/main.js deleted file mode 100644 index f406892..0000000 --- a/packages/alert/.storybook/main.js +++ /dev/null @@ -1,22 +0,0 @@ -import { nxViteTsPaths } from '@nx/vite/plugins/nx-tsconfig-paths.plugin'; -import { mergeConfig } from 'vite'; - -const config = { - stories: ['../src/lib/**/*.stories.@(js|jsx|ts|tsx|mdx)'], - addons: ['@storybook/addon-essentials'], - framework: { - name: '@storybook/react-vite', - options: {}, - }, - - viteFinal: async (config) => - mergeConfig(config, { - plugins: [nxViteTsPaths()], - }), -}; - -export default config; - -// To customize your Vite configuration you can use the viteFinal field. -// Check https://storybook.js.org/docs/react/builders/vite#configuration -// and https://nx.dev/recipes/storybook/custom-builder-configs diff --git a/packages/alert/.storybook/preview.js b/packages/alert/.storybook/preview.js deleted file mode 100644 index e69de29..0000000 diff --git a/packages/alert/src/lib/alert.tsx b/packages/alert/src/lib/alert.tsx index 90418eb..58153b8 100644 --- a/packages/alert/src/lib/alert.tsx +++ b/packages/alert/src/lib/alert.tsx @@ -7,6 +7,7 @@ export interface AlertProps { title?: string; description?: string | ReactNode; actions?: React.ReactNode; + border?: "left" | "right" | "all" | "none" link?: { url: string; text: string; @@ -14,30 +15,37 @@ export interface AlertProps { dismissButton?: boolean; } +const borders = { + none: '', + all: 'border', + left: "border-l-4", + right: "border-r-4" +} + const classes = { warning: { - bg: 'bg-yellow-50', + bg: 'bg-yellow-50 border-yellow-400', text: 'text-yellow-800', description: 'text-yellow-700', link: 'text-yellow-700 hover:text-yellow-600', dismissBtn: 'bg-yellow-50 text-yellow-500 hover:bg-yellow-100 focus:outline-none focus:ring-2 focus:ring-yellow-600 focus:ring-offset-2 focus:ring-offset-yellow-50', }, success: { - bg: 'bg-green-50', + bg: 'bg-green-50 border-green-400', text: 'text-green-800', description: 'text-green-700', link: 'text-green-700 hover:text-green-600', dismissBtn: 'bg-green-50 text-green-500 hover:bg-green-100 focus:outline-none focus:ring-2 focus:ring-green-600 focus:ring-offset-2 focus:ring-offset-green-50', }, error: { - bg: 'bg-red-50', + bg: 'bg-red-50 border-red-400', text: 'text-red-800', description: 'text-red-700', link: 'text-red-700 hover:text-red-600', dismissBtn: 'bg-red-50 text-red-500 hover:bg-red-100 focus:outline-none focus:ring-2 focus:ring-red-600 focus:ring-offset-2 focus:ring-offset-red-50', }, info: { - bg: 'border-l-4 border-blue-400 bg-blue-50', + bg: 'border-blue-400 bg-blue-50', text: 'text-blue-800', description: 'text-blue-700', link: 'text-blue-700 hover:text-blue-600', @@ -46,11 +54,12 @@ const classes = { } export const Alert: React.FC = ({ - variant, + variant = "info", title, icon, description, actions, + border = "none", link, dismissButton = false, }) => { @@ -95,7 +104,7 @@ export const Alert: React.FC = ({ return (
{icon && ( diff --git a/packages/alert/src/stories/Alert.stories.tsx b/packages/alert/src/stories/Alert.stories.tsx index eb2df73..fa41ad6 100644 --- a/packages/alert/src/stories/Alert.stories.tsx +++ b/packages/alert/src/stories/Alert.stories.tsx @@ -1,7 +1,6 @@ import React from 'react'; import { Meta, StoryObj } from '@storybook/react'; -import Title from '@bootwind/title'; import Alert, { AlertProps } from '../index'; export default { @@ -50,6 +49,15 @@ export default { type: 'select', } }, + border: { + description: 'Whether to add border on the alert', + type: "string", + options: ["left", "right", "all", "none"], + defaultValue: "none", + control: { + type: 'select', + } + }, }, } as Meta; type Story = StoryObj; @@ -58,6 +66,7 @@ export const WithTitle: Story = { args: { variant: 'info', title: "With Title", + border: "left", description: "Lorem ipsum dolor sit amet consectetur adipisicing elit. Numquam rerum sequi, aperiam nihil harum sapiente veniam rem soluta quasi fugit voluptatem voluptate tempora consectetur vitae dignissimos at ipsum perspiciatis ab!" } } @@ -96,4 +105,15 @@ export const Link: Story = { text: 'Open link' } } +} + +export const Variants = (args: AlertProps) => { + return ( +
+ + + + +
+ ) } \ No newline at end of file diff --git a/packages/alert/tsconfig.json b/packages/alert/tsconfig.json index 69aaf3a..a05f623 100644 --- a/packages/alert/tsconfig.json +++ b/packages/alert/tsconfig.json @@ -11,9 +11,6 @@ "references": [ { "path": "./tsconfig.lib.json" - }, - { - "path": "./tsconfig.storybook.json" } ], "extends": "../../tsconfig.base.json" diff --git a/packages/alert/tsconfig.storybook.json b/packages/alert/tsconfig.storybook.json deleted file mode 100644 index bbacec9..0000000 --- a/packages/alert/tsconfig.storybook.json +++ /dev/null @@ -1,30 +0,0 @@ -{ - "extends": "./tsconfig.json", - "compilerOptions": { - "experimentalDecorators": true, - "emitDecoratorMetadata": true, - "outDir": "" - }, - "files": [ - "../../node_modules/@nx/react/typings/styled-jsx.d.ts", - "../../node_modules/@nx/react/typings/cssmodule.d.ts", - "../../node_modules/@nx/react/typings/image.d.ts" - ], - "exclude": [ - "src/**/*.spec.ts", - "src/**/*.test.ts", - "src/**/*.spec.js", - "src/**/*.test.js", - "src/**/*.spec.tsx", - "src/**/*.test.tsx", - "src/**/*.spec.jsx", - "src/**/*.test.js" - ], - "include": [ - "src/**/*.stories.ts", - "src/**/*.stories.js", - "src/**/*.stories.jsx", - "src/**/*.stories.tsx", - "src/**/*.stories.mdx", - ] -} diff --git a/packages/toast/.babelrc b/packages/toast/.babelrc new file mode 100644 index 0000000..1ea870e --- /dev/null +++ b/packages/toast/.babelrc @@ -0,0 +1,12 @@ +{ + "presets": [ + [ + "@nx/react/babel", + { + "runtime": "automatic", + "useBuiltIns": "usage" + } + ] + ], + "plugins": [] +} diff --git a/packages/toast/.eslintrc.json b/packages/toast/.eslintrc.json new file mode 100644 index 0000000..a39ac5d --- /dev/null +++ b/packages/toast/.eslintrc.json @@ -0,0 +1,18 @@ +{ + "extends": ["plugin:@nx/react", "../../.eslintrc.json"], + "ignorePatterns": ["!**/*"], + "overrides": [ + { + "files": ["*.ts", "*.tsx", "*.js", "*.jsx"], + "rules": {} + }, + { + "files": ["*.ts", "*.tsx"], + "rules": {} + }, + { + "files": ["*.js", "*.jsx"], + "rules": {} + } + ] +} diff --git a/packages/toast/README.md b/packages/toast/README.md new file mode 100644 index 0000000..4dcd294 --- /dev/null +++ b/packages/toast/README.md @@ -0,0 +1,7 @@ +# toast + +This library was generated with [Nx](https://nx.dev). + +## Running unit tests + +Run `nx test toast` to execute the unit tests via [Vitest](https://vitest.dev/). diff --git a/packages/toast/package.json b/packages/toast/package.json new file mode 100644 index 0000000..9964ad0 --- /dev/null +++ b/packages/toast/package.json @@ -0,0 +1,12 @@ +{ + "name": "toast", + "version": "0.0.1", + "main": "./index.js", + "types": "./index.d.ts", + "exports": { + ".": { + "import": "./index.mjs", + "require": "./index.js" + } + } +} diff --git a/packages/toast/project.json b/packages/toast/project.json new file mode 100644 index 0000000..87d6e7a --- /dev/null +++ b/packages/toast/project.json @@ -0,0 +1,32 @@ +{ + "name": "toast", + "$schema": "../../node_modules/nx/schemas/project-schema.json", + "sourceRoot": "packages/toast/src", + "projectType": "library", + "tags": [], + "targets": { + "lint": { + "executor": "@nx/eslint:lint", + "outputs": ["{options.outputFile}"], + "options": { + "lintFilePatterns": ["packages/toast/**/*.{ts,tsx,js,jsx}"] + } + }, + "build": { + "executor": "@nx/vite:build", + "outputs": ["{options.outputPath}"], + "defaultConfiguration": "production", + "options": { + "outputPath": "dist/packages/toast" + }, + "configurations": { + "development": { + "mode": "development" + }, + "production": { + "mode": "production" + } + } + } + } +} diff --git a/packages/toast/src/index.ts b/packages/toast/src/index.ts new file mode 100644 index 0000000..1baa2a2 --- /dev/null +++ b/packages/toast/src/index.ts @@ -0,0 +1,2 @@ +export * from './lib/toast'; +export * from './lib/ToastProvider'; diff --git a/packages/toast/src/lib/ToastProvider.tsx b/packages/toast/src/lib/ToastProvider.tsx new file mode 100644 index 0000000..2e48e47 --- /dev/null +++ b/packages/toast/src/lib/ToastProvider.tsx @@ -0,0 +1,92 @@ +import { ReactNode, createContext, useContext, useId, useReducer, useRef, useState } from "react"; +import { Toast, ToastProps } from ".."; + +interface IToastProvider { + toasts: ToastProps[] + add: (props: ToastProps) => void + close: (toastId: string) => void +} +interface ToastProviderInterface { + children?: any + // Duration in milliseconds + duration?: number + position?: "top-left" | "top-right" | "bottom-left" | "bottom-right" +} + +interface State { + toasts: ToastProps[] +} + +const positionClasses = { + "top-left": "top-4 left-4", + "bottom-left": "bottom-4 left-4", + "top-right": "top-4 right-4", + "bottom-right": "bottom-4 right-4", +} +const ToastContext = createContext({} as IToastProvider) + +type Action = + | { + type: "push" + toast: ToastProps + } + | { + type: "remove" + toastId?: string + } + +const reducer = (state: State, action: Action): State => { + switch (action.type) { + case 'push': + return { toasts: [action.toast, ...state.toasts] } + case 'remove': + if(action.toastId === undefined) { + return { + toasts: [] + } + } + return { + toasts: state.toasts.filter(t => t.id !== action.toastId) + } + } +} +let count = 0 +export function ToastProvider({ children, position = "bottom-right", duration = 2000 }: ToastProviderInterface) { + function genId() { + count = (count + 1) % Number.MAX_SAFE_INTEGER + return count.toString() + } + const [state, dispatch] = useReducer(reducer, { toasts: [] }) + + const add = (newToast: ToastProps) => { + newToast.id = genId() + dispatch({ + type: "push", + toast: newToast + }) + + setTimeout(() => { + close(newToast.id!) + }, duration) + } + + const close = (toastId: string) => { + dispatch({ + toastId: toastId, + type: "remove" + }) + } + + return ( + + {children} + {/* Render the toasts */} +
+ {state.toasts.map(toast => ( + + ))} +
+
+ ) +} +export const useToastContext = () => useContext(ToastContext) \ No newline at end of file diff --git a/packages/toast/src/lib/toast.tsx b/packages/toast/src/lib/toast.tsx new file mode 100644 index 0000000..369b7ae --- /dev/null +++ b/packages/toast/src/lib/toast.tsx @@ -0,0 +1,15 @@ +/* eslint-disable-next-line */ +import Alert, { AlertProps } from "@bootwind/alert" + +export interface ToastProps extends AlertProps { + id?: string +} +export function Toast(props: ToastProps) { + return ( +
+ +
+ ); +} + +export default Toast; diff --git a/packages/toast/src/lib/useToast.tsx b/packages/toast/src/lib/useToast.tsx new file mode 100644 index 0000000..165b395 --- /dev/null +++ b/packages/toast/src/lib/useToast.tsx @@ -0,0 +1,11 @@ +import { useToastContext } from "./ToastProvider" + +export const useToast = () => { + const ctx = useToastContext() + const toast = ctx.add + const closeToast = ctx.close + return { + toast, + closeToast + } +} \ No newline at end of file diff --git a/packages/toast/src/stories/Toast.stories.tsx b/packages/toast/src/stories/Toast.stories.tsx new file mode 100644 index 0000000..70e17f4 --- /dev/null +++ b/packages/toast/src/stories/Toast.stories.tsx @@ -0,0 +1,91 @@ +import { Toast, ToastProps } from ".."; +import { Meta, StoryObj } from '@storybook/react'; +import { ToastProvider, useToastContext } from "../lib/ToastProvider"; +import { Button } from "@bootwind/button" +import { useToast } from "../lib/useToast"; +import { useEffect } from "react"; +import { AlertProps } from "@bootwind/alert"; + +export default { + tags: ['autodocs'], + title: 'Components/Toast', + component: Toast, + decorators: [ + (Story: any) => ( + +
+ +
+
+ ) + ], + args: { + title: "Hi There!", + description: 'Welcome back, User!', + dismissButton: false, + }, + argTypes: { + description: { + description: 'The title text', + type: "string", + control: { + type: 'text' + } + }, + dismissButton: { + description: 'Whether to show dismiss button', + type: "boolean", + control: { + type: 'boolean', + } + }, + link: { + description: 'Add link to the alert', + type: "string", + control: { + type: 'object', + } + }, + icon: { + description: 'Alert icon', + type: "boolean", + control: { + type: 'boolean', + } + }, + variant: { + description: 'The color type of the alert', + type: "string", + options: ['warning', 'error', 'success', 'info'], + control: { + type: 'select', + } + }, + border: { + description: 'Whether to add border on the alert', + type: "string", + options: ["left", "right", "all", "none"], + defaultValue: "none", + control: { + type: 'select', + } + }, + }, + } as Meta; +type Story = StoryObj; + +const randomVariants = () => { + return ['success', 'error', 'info', 'warning'][Math.floor(Math.random() * 4)] +} + +export const Basic = (args: ToastProps) => { + const { toasts, add } = useToastContext() + const showToast = () => { + add(args) + } + return ( + <> + + + ) +} \ No newline at end of file diff --git a/packages/toast/tsconfig.json b/packages/toast/tsconfig.json new file mode 100644 index 0000000..6734c59 --- /dev/null +++ b/packages/toast/tsconfig.json @@ -0,0 +1,18 @@ +{ + "compilerOptions": { + "jsx": "react-jsx", + "allowJs": false, + "esModuleInterop": false, + "allowSyntheticDefaultImports": true, + "strict": true, + "types": ["vite/client"] + }, + "files": [], + "include": [], + "references": [ + { + "path": "./tsconfig.lib.json" + } + ], + "extends": "../../tsconfig.base.json" +} diff --git a/packages/toast/tsconfig.lib.json b/packages/toast/tsconfig.lib.json new file mode 100644 index 0000000..8d6bbf7 --- /dev/null +++ b/packages/toast/tsconfig.lib.json @@ -0,0 +1,23 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "../../dist/out-tsc", + "types": [ + "node", + "@nx/react/typings/cssmodule.d.ts", + "@nx/react/typings/image.d.ts", + "vite/client" + ] + }, + "exclude": [ + "**/*.spec.ts", + "**/*.test.ts", + "**/*.spec.tsx", + "**/*.test.tsx", + "**/*.spec.js", + "**/*.test.js", + "**/*.spec.jsx", + "**/*.test.jsx" + ], + "include": ["src/**/*.js", "src/**/*.jsx", "src/**/*.ts", "src/**/*.tsx"] +} diff --git a/packages/toast/vite.config.ts b/packages/toast/vite.config.ts new file mode 100644 index 0000000..f5ffd86 --- /dev/null +++ b/packages/toast/vite.config.ts @@ -0,0 +1,43 @@ +/// +import { defineConfig } from 'vite'; +import react from '@vitejs/plugin-react'; +import dts from 'vite-plugin-dts'; +import * as path from 'path'; +import { nxViteTsPaths } from '@nx/vite/plugins/nx-tsconfig-paths.plugin'; + +export default defineConfig({ + cacheDir: '../../node_modules/.vite/toast', + + plugins: [ + react(), + nxViteTsPaths(), + dts({ + entryRoot: 'src', + tsConfigFilePath: path.join(__dirname, 'tsconfig.lib.json'), + skipDiagnostics: true, + }), + ], + + // Uncomment this if you are using workers. + // worker: { + // plugins: [ nxViteTsPaths() ], + // }, + + // Configuration for building your library. + // See: https://vitejs.dev/guide/build.html#library-mode + build: { + lib: { + // Could also be a dictionary or array of multiple entry points. + entry: 'src/index.ts', + name: 'toast', + fileName: 'index', + // Change this to the formats you want to support. + // Don't forget to update your package.json as well. + formats: ['es', 'cjs'], + }, + rollupOptions: { + // External packages that should not be bundled into your library. + external: ['react', 'react-dom', 'react/jsx-runtime'], + }, + }, +}); diff --git a/packages/ui/tsconfig.storybook.json b/packages/ui/tsconfig.storybook.json index 991db31..b3e3945 100644 --- a/packages/ui/tsconfig.storybook.json +++ b/packages/ui/tsconfig.storybook.json @@ -27,6 +27,7 @@ "src/**/*.stories.tsx", "src/**/*.stories.mdx", ".storybook/*.js", - ".storybook/*.ts" + ".storybook/*.ts", + ".storybook/preview.tsx" ] } diff --git a/tsconfig.base.json b/tsconfig.base.json index 1b0fdb9..cf71463 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -17,10 +17,11 @@ "@bootwind/common": ["./packages/common/src/index.ts"], "@bootwind/core": ["packages/core/src/index.ts"], "@bootwind/forms": ["packages/forms/src/index.ts"], + "@bootwind/pagination": ["packages/pagination/src/index.ts"], "@bootwind/title": ["./packages/title/src/index.ts"], "@bootwind/typography": ["packages/typography/src/index.ts"], "@bootwind/ui": ["./packages/ui/src/index.ts"], - "@bootwind/pagination": ["packages/pagination/src/index.ts"] + "@bootwind/toast": ["./packages/toast/src/index.ts"] } } }