diff --git a/docs/blocks/authentication/Login.mdx b/docs/blocks/authentication/Login.mdx index 76c8acb2..48736a09 100644 --- a/docs/blocks/authentication/Login.mdx +++ b/docs/blocks/authentication/Login.mdx @@ -1,7 +1,9 @@ import { Meta, Unstyled } from '@storybook/addon-docs'; import { Login } from './Login'; -import { Register } from './Register'; -import { ForgotPassword } from './ForgotPassword'; +import { LoginBasic } from './LoginBasic'; +import { LoginPassword } from './LoginPassword'; +import { LoginSocial } from './LoginSocial'; +import { LoginFull } from './LoginFull'; import { Tabs, TabPanel, TabList, Tab } from '../../../src/layout/Tabs'; @@ -27,13 +29,10 @@ The Login block is the gateway for users to access their accounts, providing a s ```tsx -import { Input } from '../../../src/form/Input'; -import { Button } from '../../../src/elements/Button'; -import { Card } from '../../../src/layout/Card'; -import { Block } from '../../../src/layout/Block'; -import { useForm, Controller } from 'react-hook-form'; +import { Block, Button, Card, Divider, Input } from 'reablocks'; import { motion } from 'framer-motion'; -import { Divider } from '../../../src/layout'; +import { useForm, Controller } from 'react-hook-form'; + import logo from '../../assets/reaviz.svg'; export const Login = () => { @@ -50,10 +49,10 @@ export const Login = () => { className="mt-10 sm:mx-auto sm:w-full sm:max-w-sm md:max-w-[600px]" > -
+
Logo

- Login or create an account + Log In or create account

Welcome to Reablocks, powered by Good Code @@ -64,26 +63,22 @@ export const Login = () => { ( + render={({ field }) => ( )} /> - + +
+ By signing in, you agree to our + + terms of service + + and + + privacy policy + +
+ + + + ); +}; +```` + + + +
+ + + + + Demo + + + Code + + + +
+ +
+
+ +```tsx +import { Block, Button, Card, Divider, Input } from 'reablocks'; +import { motion } from 'framer-motion'; +import { useForm, Controller } from 'react-hook-form'; + +import logo from '../../assets/reaviz.svg'; + +export const LoginBasic = () => { + const { + control, + handleSubmit, + formState: { isSubmitting } + } = useForm(); + + return ( + + +
+ Logo +

+ Log In or create account +

+ + Welcome to Reablocks, powered by Good Code + +
+
console.log('values', values))}> + + ( + + )} + /> + + + + -
+
By signing in, you agree to our - + and -
+ + + + ); +}; +```` + + + +
+ + + + + Demo + + + Code + + + +
+ +
+
+ +```tsx +import { Block, Button, Card, Checkbox, Divider, Input, Stack } from 'reablocks'; +import { motion } from 'framer-motion'; +import { useForm, Controller } from 'react-hook-form'; + +import logo from '../../assets/reaviz.svg'; + +export const LoginPassword = () => { + const { + control, + handleSubmit, + setValue, + formState: { isSubmitting } + } = useForm(); + + return ( + + +
+ Logo +

+ Log In or create account +

+ + Welcome to Reablocks, powered by Good Code + +
+
console.log('values', values))}> + + ( + + )} + /> + + + ( + + )} + /> + + + ( + setValue('remember', val)} + /> + )} + /> + + Forgot your password? + + + + +
+ Don't have an account? + + Sign up + +
+ +
+
+ ); +}; +``` +
+
+
+
+ + + + + Demo + + + Code + + + +
+ +
+
+ +```tsx +import { Block, Button, Card, Divider, Input, Stack } from 'reablocks'; +import { motion } from 'framer-motion'; +import { useForm, Controller } from 'react-hook-form'; + +import logo from '../../assets/reaviz.svg'; + +export const LoginSocial = () => { + const { + control, + handleSubmit, + formState: { isSubmitting } + } = useForm(); + + return ( + + +
+ Logo +

+ Log In or create account +

+ + Welcome to Reablocks, powered by Good Code + +
+
console.log('values', values))}> + + ( + + )} + /> + + + + + + or with + + + +
+ + + +
+
+ By signing in, you agree to our + + terms of service + + and + + privacy policy +
); }; - ``` +``` +
+
+
+
+ + + + + Demo + + + Code + + + +
+ +
+
+ +```tsx +import { Block, Button, Card, Divider, Input, Stack } from 'reablocks'; +import { motion } from 'framer-motion'; +import { useForm, Controller } from 'react-hook-form'; + +import logo from '../../assets/reaviz.svg'; +import bg from '../../assets/bg.png'; + +export const LoginFull = () => { + const { + control, + handleSubmit, + formState: { isSubmitting } + } = useForm(); + + return ( + + +
+ Logo +

+ Log In or create account +

+ + Welcome to Reablocks, powered by Good Code + +
console.log('values', values))} + > + + ( + + )} + /> + + + + + + + + or with + + + +
+ + + +
+
+ By signing in, you agree to our + + terms of service + + and + + privacy policy + +
+
+
+
+
+ +
+
+ ); +}; +```
diff --git a/docs/blocks/authentication/Login.tsx b/docs/blocks/authentication/Login.tsx index 9a3f5e37..9fcb9415 100644 --- a/docs/blocks/authentication/Login.tsx +++ b/docs/blocks/authentication/Login.tsx @@ -1,10 +1,11 @@ -import { Input } from '../../../src/form/Input'; +import { Block } from '../../../src/layout/Block'; import { Button } from '../../../src/elements/Button'; import { Card } from '../../../src/layout/Card'; -import { Block } from '../../../src/layout/Block'; -import { useForm, Controller } from 'react-hook-form'; -import { motion } from 'framer-motion'; import { Divider } from '../../../src/layout'; +import { Input } from '../../../src/form/Input'; +import { motion } from 'framer-motion'; +import { useForm, Controller } from 'react-hook-form'; + import logo from '../../assets/reaviz.svg'; export const Login = () => { @@ -21,10 +22,10 @@ export const Login = () => { className="mt-10 sm:mx-auto sm:w-full sm:max-w-sm md:max-w-[600px]" > -
+
Logo

- Login or create an account + Log In or create account

Welcome to Reablocks, powered by Good Code @@ -35,26 +36,22 @@ export const Login = () => { ( + render={({ field }) => ( )} /> - + -
+
By signing in, you agree to our - + and - +
diff --git a/docs/blocks/authentication/LoginBasic.tsx b/docs/blocks/authentication/LoginBasic.tsx new file mode 100644 index 00000000..2d00bdac --- /dev/null +++ b/docs/blocks/authentication/LoginBasic.tsx @@ -0,0 +1,117 @@ +import { Block } from '../../../src/layout/Block'; +import { Button } from '../../../src/elements/Button'; +import { Card } from '../../../src/layout/Card'; +import { Divider } from '../../../src/layout'; +import { Input } from '../../../src/form/Input'; +import { motion } from 'framer-motion'; +import { useForm, Controller } from 'react-hook-form'; + +import logo from '../../assets/reaviz.svg'; + +export const LoginBasic = () => { + const { + control, + handleSubmit, + formState: { isSubmitting } + } = useForm(); + + return ( + + +
+ Logo +

+ Log In or create account +

+ + Welcome to Reablocks, powered by Good Code + +
+
console.log('values', values))}> + + ( + + )} + /> + + + + +
+ By signing in, you agree to our + + terms of service + + and + + privacy policy + +
+ +
+
+ ); +}; diff --git a/docs/blocks/authentication/LoginFull.tsx b/docs/blocks/authentication/LoginFull.tsx new file mode 100644 index 00000000..3e8ca7ea --- /dev/null +++ b/docs/blocks/authentication/LoginFull.tsx @@ -0,0 +1,167 @@ +import { Block } from '../../../src/layout/Block'; +import { Button } from '../../../src/elements/Button'; +import { Card } from '../../../src/layout/Card'; +import { Divider } from '../../../src/layout/Divider'; +import { Input } from '../../../src/form/Input'; +import { Stack } from '../../../src/layout/Stack'; +import { motion } from 'framer-motion'; +import { useForm, Controller } from 'react-hook-form'; + +import logo from '../../assets/reaviz.svg'; +import bg from '../../assets/bg.png'; + +export const LoginFull = () => { + const { + control, + handleSubmit, + formState: { isSubmitting } + } = useForm(); + + return ( + + +
+ Logo +

+ Log In or create account +

+ + Welcome to Reablocks, powered by Good Code + +
console.log('values', values))} + > + + ( + + )} + /> + + + + + + + + or with + + + +
+ + + +
+
+ By signing in, you agree to our + + terms of service + + and + + privacy policy + +
+
+
+
+
+ +
+
+ ); +}; diff --git a/docs/blocks/authentication/LoginPassword.tsx b/docs/blocks/authentication/LoginPassword.tsx new file mode 100644 index 00000000..73a2d36c --- /dev/null +++ b/docs/blocks/authentication/LoginPassword.tsx @@ -0,0 +1,132 @@ +import { Block } from '../../../src/layout/Block'; +import { Button } from '../../../src/elements/Button'; +import { Card } from '../../../src/layout/Card'; +import { Checkbox } from '../../../src/form/Checkbox'; +import { Divider } from '../../../src/layout/Divider'; +import { Input } from '../../../src/form/Input'; +import { Stack } from '../../../src/layout/Stack'; +import { motion } from 'framer-motion'; +import { useForm, Controller } from 'react-hook-form'; + +import logo from '../../assets/reaviz.svg'; + +export const LoginPassword = () => { + const { + control, + handleSubmit, + setValue, + formState: { isSubmitting } + } = useForm(); + + return ( + + +
+ Logo +

+ Log In or create account +

+ + Welcome to Reablocks, powered by Good Code + +
+
console.log('values', values))}> + + ( + + )} + /> + + + ( + + )} + /> + + + ( + setValue('remember', val)} + /> + )} + /> + + Forgot your password? + + + + +
+ Don't have an account? + + Sign up + +
+ +
+
+ ); +}; diff --git a/docs/blocks/authentication/LoginSocial.tsx b/docs/blocks/authentication/LoginSocial.tsx new file mode 100644 index 00000000..44c946ee --- /dev/null +++ b/docs/blocks/authentication/LoginSocial.tsx @@ -0,0 +1,160 @@ +import { Block } from '../../../src/layout/Block'; +import { Button } from '../../../src/elements/Button'; +import { Card } from '../../../src/layout/Card'; +import { Divider } from '../../../src/layout/Divider'; +import { Input } from '../../../src/form/Input'; +import { Stack } from '../../../src/layout/Stack'; +import { motion } from 'framer-motion'; +import { useForm, Controller } from 'react-hook-form'; + +import logo from '../../assets/reaviz.svg'; + +export const LoginSocial = () => { + const { + control, + handleSubmit, + formState: { isSubmitting } + } = useForm(); + + return ( + + +
+ Logo +

+ Log In or create account +

+ + Welcome to Reablocks, powered by Good Code + +
+
console.log('values', values))}> + + ( + + )} + /> + + + + + + or with + + + +
+ + + +
+
+ By signing in, you agree to our + + terms of service + + and + + privacy policy + +
+
+
+
+ ); +}; diff --git a/docs/components/layers/Notification.mdx b/docs/components/layers/Notification.mdx index 3a55ffe4..35ad8910 100644 --- a/docs/components/layers/Notification.mdx +++ b/docs/components/layers/Notification.mdx @@ -46,7 +46,45 @@ const Component = () => { }; ``` -## Example +Use these `Notifications` props to customize your notifications at a global level + +### API + + +## NotificationOptions +Pass in options when calling the `notify` function to customize the notification. + +```tsx +const options: NotificationOptions = { + body: "This is the body of the notification", + timeout: 5000, +} + +notify("This is a notification", options) +``` + +### API +```jsx +type NotificationVariants = + | 'default' + | 'success' + | 'warning' + | 'error' + | 'info'; + +interface NotificationOptions { + title?: string | React.JSX.Element | React.JSX.Element[]; + body?: string | React.JSX.Element | React.JSX.Element[]; + timeout?: number; + showClose?: boolean; + variant?: NotificationVariants; + className?: string; + icon?: string | React.JSX.Element | React.JSX.Element[]; + action?: string | React.JSX.Element | React.JSX.Element[]; +} +``` + +## Examples ### Notification for interface essential scenarios ### Custom styled notitications @@ -60,6 +98,3 @@ This component uses the following default theme: Learn more about how to customize in the [Theme documentation](?path=/docs/docs-theme-getting-started--docs). - -## API - diff --git a/package.json b/package.json index 920440f2..cb20e617 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "reablocks", - "version": "7.3.4", + "version": "7.3.7", "description": "Component library for React", "scripts": { "build": "npm run build:js && npm run build:styles", diff --git a/src/assets/icons/check_circle.svg b/src/assets/icons/check_circle.svg new file mode 100644 index 00000000..ae765629 --- /dev/null +++ b/src/assets/icons/check_circle.svg @@ -0,0 +1,5 @@ + + + \ No newline at end of file diff --git a/src/assets/icons/error_circle.svg b/src/assets/icons/error_circle.svg new file mode 100644 index 00000000..37bd7274 --- /dev/null +++ b/src/assets/icons/error_circle.svg @@ -0,0 +1,5 @@ + + + \ No newline at end of file diff --git a/src/assets/icons/info.svg b/src/assets/icons/info.svg new file mode 100644 index 00000000..f2b1ae08 --- /dev/null +++ b/src/assets/icons/info.svg @@ -0,0 +1,12 @@ + + + + + + + + + + \ No newline at end of file diff --git a/src/assets/icons/warning.svg b/src/assets/icons/warning.svg new file mode 100644 index 00000000..d832f93b --- /dev/null +++ b/src/assets/icons/warning.svg @@ -0,0 +1,5 @@ + + + \ No newline at end of file diff --git a/src/elements/Avatar/Avatar.story.tsx b/src/elements/Avatar/Avatar.story.tsx index 279e1e88..82f5c7fd 100644 --- a/src/elements/Avatar/Avatar.story.tsx +++ b/src/elements/Avatar/Avatar.story.tsx @@ -16,6 +16,14 @@ Default.args = { rounded: false }; +export const Outline = Template.bind({}); +Outline.args = { + name: 'John Doe', + size: 50, + rounded: false, + variant: 'outline' +}; + export const RoundedWithImage = Template.bind({}); RoundedWithImage.args = { src: 'https://goodcode.us/static/austin-d1a2c5249336c31662b8ee6d4e169b2b.jpg', diff --git a/src/elements/Avatar/Avatar.tsx b/src/elements/Avatar/Avatar.tsx index 08e6cb16..7d209370 100644 --- a/src/elements/Avatar/Avatar.tsx +++ b/src/elements/Avatar/Avatar.tsx @@ -21,6 +21,11 @@ export interface AvatarProps extends React.HTMLAttributes { */ size?: number; + /** + * Style variant for the avatar. + */ + variant?: 'filled' | 'outline'; + /** * Whether the avatar is rounded. */ @@ -53,6 +58,7 @@ export const Avatar = React.forwardRef( src, color, size, + variant, rounded, className, colorOptions, @@ -88,7 +94,12 @@ export const Avatar = React.forwardRef( height: `${size}px`, fontSize: `${fontSize}px`, backgroundImage: src ? `url(${src})` : 'none', - backgroundColor + backgroundColor, + ...(variant === 'outline' && { + backgroundColor: 'transparent', + border: `solid 1px ${backgroundColor}`, + color: backgroundColor + }) }} ref={ref} > @@ -100,5 +111,6 @@ export const Avatar = React.forwardRef( Avatar.defaultProps = { size: 24, - rounded: true + rounded: true, + variant: 'filled' }; diff --git a/src/form/Input/InlineInput/InlineInput.tsx b/src/form/Input/InlineInput/InlineInput.tsx index 9551fe9e..efc22f1e 100644 --- a/src/form/Input/InlineInput/InlineInput.tsx +++ b/src/form/Input/InlineInput/InlineInput.tsx @@ -1,6 +1,5 @@ -import React, { FC, forwardRef, Ref, InputHTMLAttributes } from 'react'; +import { FC, forwardRef, Ref, InputHTMLAttributes } from 'react'; import AutosizeInput from 'react-18-input-autosize'; -import { InputRef } from '../Input'; import { twMerge } from 'tailwind-merge'; import { InputTheme } from '../InputTheme'; import { useComponentTheme } from '../../../utils'; @@ -46,7 +45,7 @@ export const InlineInput: FC = forwardRef( theme: customTheme, ...rest }, - ref: Ref + ref: Ref ) => { const theme: InputTheme = useComponentTheme('input', customTheme); diff --git a/src/form/Select/Select.tsx b/src/form/Select/Select.tsx index 7ab135a5..1072b1a4 100644 --- a/src/form/Select/Select.tsx +++ b/src/form/Select/Select.tsx @@ -203,6 +203,16 @@ export interface SelectProps { * The options for the Fuse.js search algorithm. */ searchOptions?: Fuse.IFuseOptions; + + /** + * When menu is opened + */ + onOpenMenu?: () => void; + + /** + * When menu is closed + */ + onCloseMenu?: () => void; } export const Select: FC> = ({ @@ -239,7 +249,9 @@ export const Select: FC> = ({ onInputKeyUp, onOptionsChange, onInputChange, - searchOptions + searchOptions, + onOpenMenu, + onCloseMenu }) => { const overlayRef = useRef(null); const inputRef = useRef(null); @@ -651,7 +663,8 @@ export const Select: FC> = ({ } resetSelect(); - }, [createable, keyword, resetSelect, toggleSelectedOption]); + onCloseMenu?.(); + }, [createable, keyword, onCloseMenu, resetSelect, toggleSelectedOption]); return ( > = ({ reference={inputRef?.current?.containerRef} ref={overlayRef} onClose={onOverlayClose} + onOpen={onOpenMenu} content={() => ( element={menu} diff --git a/src/layers/Notification/Notification.story.tsx b/src/layers/Notification/Notification.story.tsx index 41b1f558..df90193f 100644 --- a/src/layers/Notification/Notification.story.tsx +++ b/src/layers/Notification/Notification.story.tsx @@ -101,6 +101,7 @@ export const Variants = () => ( notifySuccess, notifyError, notifyWarning, + notifyInfo, clearAllNotifications: clearAll }) => ( @@ -125,6 +126,12 @@ export const Variants = () => ( > Warning +

@@ -157,6 +164,83 @@ export const FloodPrevention = () => ( ); +export const CustomIcon = () => ( + + + {({ notify, clearAllNotifications: clearAll }) => ( + + +
+
+ +
+ )} +
+
+); + export const CustomComponent = () => ( ( ); + +export const WithAction = () => ( + + + {({ notify, clearAllNotifications }) => ( + + + }) + } + > + Show + +
+
+ +
+ )} +
+
+); diff --git a/src/layers/Notification/Notification.tsx b/src/layers/Notification/Notification.tsx index 39ad5146..dbaf264e 100644 --- a/src/layers/Notification/Notification.tsx +++ b/src/layers/Notification/Notification.tsx @@ -20,6 +20,8 @@ export const Notification: FC = ({ timeout, className, variant, + icon, + action, component, onClose, theme: customTheme @@ -57,12 +59,23 @@ export const Notification: FC = ({
- {title &&
{title}
} + {title && ( +
+ {icon && ( +
+ {icon} +
+ )} + {title} +
+ )} {body && (
{typeof body === 'string' ? ( @@ -73,6 +86,7 @@ export const Notification: FC = ({
)}
+ {action &&
{action}
}
{showClose && (
); +export const Sizes = () => ( +
+ + + Tab 1 + Tab 2 + Tab 3 + + This is content for small tab 1 + This is content for small tab 2 + This is content for small tab 3 + + + + Tab 1 + Tab 2 + Tab 3 + + This is content for medium tab 1 + This is content for medium tab 2 + This is content for medium tab 3 + + + + Tab 1 + Tab 2 + Tab 3 + + This is content for large tab 1 + This is content for large tab 2 + This is content for large tab 3 + +
+); + export const DefaultIndex = () => ( diff --git a/src/layout/Tabs/Tabs.tsx b/src/layout/Tabs/Tabs.tsx index 7ddd9eba..1a0d9b15 100644 --- a/src/layout/Tabs/Tabs.tsx +++ b/src/layout/Tabs/Tabs.tsx @@ -46,6 +46,11 @@ export interface TabsProps extends PropsWithChildren { */ variant?: 'primary' | 'secondary'; + /** + * The size of the tabs. + */ + size?: 'small' | 'medium' | 'large'; + /** * The callback to be called when a tab is selected. */ @@ -62,6 +67,7 @@ export const Tabs: FC = ({ className, style, variant = 'primary', + size = 'medium', direction = 'ltr', defaultIndex = 0, selectedIndex, @@ -100,6 +106,7 @@ export const Tabs: FC = ({