Update Toaster position

Change-Id: I474e29f277b3e2638ac758448fe20a56b5b0f3bf
This commit is contained in:
Roman Jaroš 2023-08-27 11:55:20 +02:00
parent 315756790e
commit 931979033b
9 changed files with 217 additions and 119 deletions

View file

@ -7,6 +7,7 @@ import MenuIcon from '@prokyon/components/Icons/Menu';
import RightToBracketIcon from '@prokyon/components/Icons/RightToBracket'; import RightToBracketIcon from '@prokyon/components/Icons/RightToBracket';
import Modals from '@prokyon/components/Modal/components/Modals'; import Modals from '@prokyon/components/Modal/components/Modals';
import { Toasters } from '@prokyon/components/Toaster'; import { Toasters } from '@prokyon/components/Toaster';
import { ToastersPosition } from '@prokyon/components/Toaster/components/Toasters';
import { SIZES, useMediaQuery } from '@prokyon/hooks/useMediaQuery'; import { SIZES, useMediaQuery } from '@prokyon/hooks/useMediaQuery';
import { isNilOrEmpty } from '@prokyon/utils'; import { isNilOrEmpty } from '@prokyon/utils';
@ -24,10 +25,11 @@ export type SkeletonProps = {
top?: MenuItem[]; top?: MenuItem[];
user?: MenuItem[]; user?: MenuItem[];
}; };
toasterPosition?: ToastersPosition;
}; };
export const Skeleton: React.FC<SkeletonProps> = (props) => { export const Skeleton: React.FC<SkeletonProps> = (props) => {
const { children, components = {}, items } = props; const { children, components = {}, items, toasterPosition = ToastersPosition.TopRight } = props;
const { footer } = components; const { footer } = components;
const [, setLocation] = useLocation(); const [, setLocation] = useLocation();
@ -120,7 +122,7 @@ export const Skeleton: React.FC<SkeletonProps> = (props) => {
</div> </div>
</div> </div>
<Modals /> <Modals />
<Toasters /> <Toasters position={toasterPosition} />
</div> </div>
); );
}; };

View file

@ -7,21 +7,21 @@ import Message from '../../Message';
import { useToaster } from '../hooks'; import { useToaster } from '../hooks';
export enum ToastersPosition { export enum ToastersPosition {
topLeft, BottomCenter = 'bottomCenter',
topCenter, BottomLeft = 'bottomLeft',
topRight, BottomRight = 'bottomRight',
rightCenter, LeftCenter = 'leftCenter',
bottomRight, RightCenter = 'rightCenter',
bottomCenter, TopCenter = 'topCenter',
bottomLeft, TopLeft = 'topLeft',
leftCenter, TopRight = 'topRight',
} }
export type ToastersProps = { export type ToastersProps = {
position: ToastersPosition; position: ToastersPosition;
}; };
export function Toasters({ position = ToastersPosition.topRight }) { export function Toasters({ position }) {
const { toasters } = useToaster(); const { toasters } = useToaster();
if (isNilOrEmpty(toasters)) { if (isNilOrEmpty(toasters)) {
@ -31,14 +31,14 @@ export function Toasters({ position = ToastersPosition.topRight }) {
return ( return (
<div <div
className={clsx('toasters', { className={clsx('toasters', {
'toasters--topLeft': ToastersPosition.topLeft === position, 'toasters--topLeft': ToastersPosition.TopLeft === position,
'toasters--topCenter': ToastersPosition.topCenter === position, 'toasters--topCenter': ToastersPosition.TopCenter === position,
'toasters--topRight': ToastersPosition.topRight === position, 'toasters--topRight': ToastersPosition.TopRight === position,
'toasters--rightCenter': ToastersPosition.rightCenter === position, 'toasters--rightCenter': ToastersPosition.RightCenter === position,
'toasters--bottomRight': ToastersPosition.bottomRight === position, 'toasters--bottomRight': ToastersPosition.BottomRight === position,
'toasters--bottomCenter': ToastersPosition.bottomCenter === position, 'toasters--bottomCenter': ToastersPosition.BottomCenter === position,
'toasters--bottomLeft': ToastersPosition.bottomLeft === position, 'toasters--bottomLeft': ToastersPosition.BottomLeft === position,
'toasters--leftCenter': ToastersPosition.leftCenter === position, 'toasters--leftCenter': ToastersPosition.LeftCenter === position,
})}> })}>
{Object.keys(toasters).map((toasterId) => { {Object.keys(toasters).map((toasterId) => {
const toaster = toasters[toasterId]; const toaster = toasters[toasterId];

View file

@ -18,7 +18,7 @@ type ToasterContextType = {
toasters: Record<ToasterId, Toaster>; toasters: Record<ToasterId, Toaster>;
}; };
const DefaultTimeout = 6000; const DefaultTimeout = 60000;
const timeouts: { [key: ToasterId]: any } = []; const timeouts: { [key: ToasterId]: any } = [];
export const ToasterContext = createContext<ToasterContextType>({ export const ToasterContext = createContext<ToasterContextType>({

View file

@ -8,35 +8,39 @@ const toaster = (theme) => ({
zIndex: 10, zIndex: 10,
'&--topLeft': { '&--topLeft': {
top: '3rem', top: '3rem',
right: '3rem', left: '3rem',
}, },
'&--topCenter': { '&--topCenter': {
top: '3rem', top: '3rem',
right: '3rem', left: '50%',
transform: 'translate(-50%, 0%)',
}, },
'&--topRight': { '&--topRight': {
top: '3rem', top: '3rem',
right: '3rem', right: '3rem',
}, },
'&--rightCenter': { '&--rightCenter': {
top: '3rem', top: '50%',
transform: 'translate(0%, -50%)',
right: '3rem', right: '3rem',
}, },
'&--bottomRight': { '&--bottomRight': {
top: '3rem', bottom: '3rem',
right: '3rem', right: '3rem',
}, },
'&--bottomCenter': { '&--bottomCenter': {
top: '3rem', bottom: '3rem',
right: '3rem', left: '50%',
transform: 'translate(-50%, 0%)',
}, },
'&--bottomLeft': { '&--bottomLeft': {
top: '3rem', bottom: '3rem',
right: '3rem', left: '3rem',
}, },
'&--leftCenter': { '&--leftCenter': {
top: '3rem', top: '50%',
right: '3rem', transform: 'translate(0%, -50%)',
left: '3rem',
}, },
[`@media (max-width: ${theme('screens.sm-m.max')})`]: { [`@media (max-width: ${theme('screens.sm-m.max')})`]: {
left: '2rem', left: '2rem',

View file

@ -42,76 +42,76 @@ All icons are from [reactsvgicons.com](https://reactsvgicons.com/).
<IconItem name="AngleLeft"> <IconItem name="AngleLeft">
<AngleLeft /> <AngleLeft />
</IconItem> </IconItem>
<IconItem name="ExclamationCircle">
<ExclamationCircle />
</IconItem>
<IconItem name="RightToBracket">
<RightToBracket />
</IconItem>
<IconItem name="AngleRight"> <IconItem name="AngleRight">
<AngleRight /> <AngleRight />
</IconItem> </IconItem>
<IconItem name="ExclamationTriangle">
<ExclamationTriangle />
</IconItem>
<IconItem name="Settings">
<Settings />
</IconItem>
<IconItem name="BuildingStore"> <IconItem name="BuildingStore">
<BuildingStore /> <BuildingStore />
</IconItem> </IconItem>
<IconItem name="External">
<External />
</IconItem>
<IconItem name="Square">
<Square />
</IconItem>
<IconItem name="CheckCircle"> <IconItem name="CheckCircle">
<CheckCircle /> <CheckCircle />
</IconItem> </IconItem>
<IconItem name="File">
<File />
</IconItem>
<IconItem name="Today">
<Today />
</IconItem>
<IconItem name="CheckSquare"> <IconItem name="CheckSquare">
<CheckSquare /> <CheckSquare />
</IconItem> </IconItem>
<IconItem name="Folder"> <IconItem name="Circle">
<Folder /> <Circle />
</IconItem>
<IconItem name="Trash">
<Trash />
</IconItem> </IconItem>
<IconItem name="CircleFill"> <IconItem name="CircleFill">
<CircleFill /> <CircleFill />
</IconItem> </IconItem>
<IconItem name="HappyMessage">
<HappyMessage />
</IconItem>
<IconItem name="UserTie">
<UserTie />
</IconItem>
<IconItem name="Circle">
<Circle />
</IconItem>
<IconItem name="InfoCircle">
<InfoCircle />
</IconItem>
<IconItem name="User">
<User />
</IconItem>
<IconItem name="Cross"> <IconItem name="Cross">
<Cross /> <Cross />
</IconItem> </IconItem>
<IconItem name="Menu">
<Menu />
</IconItem>
<IconItem name="Dashboard"> <IconItem name="Dashboard">
<Dashboard /> <Dashboard />
</IconItem> </IconItem>
<IconItem name="ExclamationCircle">
<ExclamationCircle />
</IconItem>
<IconItem name="ExclamationTriangle">
<ExclamationTriangle />
</IconItem>
<IconItem name="External">
<External />
</IconItem>
<IconItem name="File">
<File />
</IconItem>
<IconItem name="Folder">
<Folder />
</IconItem>
<IconItem name="HappyMessage">
<HappyMessage />
</IconItem>
<IconItem name="InfoCircle">
<InfoCircle />
</IconItem>
<IconItem name="Menu">
<Menu />
</IconItem>
<IconItem name="RightFromBracket"> <IconItem name="RightFromBracket">
<RightFromBracket /> <RightFromBracket />
</IconItem> </IconItem>
<IconItem name="RightToBracket">
<RightToBracket />
</IconItem>
<IconItem name="Settings">
<Settings />
</IconItem>
<IconItem name="Square">
<Square />
</IconItem>
<IconItem name="Today">
<Today />
</IconItem>
<IconItem name="Trash">
<Trash />
</IconItem>
<IconItem name="User">
<User />
</IconItem>
<IconItem name="UserTie">
<UserTie />
</IconItem>
</IconGallery> </IconGallery>

View file

@ -9,7 +9,7 @@ import { ModalWrapper, openModal as openOutsideReactModal } from '@prokyon/compo
import { useModal } from '@prokyon/components/Modal/hooks'; import { useModal } from '@prokyon/components/Modal/hooks';
import { TextField } from '@prokyon/components/TextField'; import { TextField } from '@prokyon/components/TextField';
type Story = StoryObj<typeof AddModalButton>; type Story = StoryObj<typeof Modals>;
export default { export default {
component: Modals, component: Modals,

View file

@ -1,41 +0,0 @@
import { Title, Unstyled } from '@storybook/blocks';
import { Button } from '@prokyon/components/Button';
import { Toasters } from '@prokyon/components/Toaster';
import { showToaster, ToasterWrapper } from '@prokyon/components/Toaster/context';
import { StatusEnum } from '@prokyon/types/common';
<Title>Toaster</Title>
`import Toaster sfrom '@prokyon/components/Toaster/components/Toasters';`
```tsx
import { useToaster } from '@prokyon/components/Toaster/hooks';
const { showToaster } = useToaster();
showToaster({ id: 'id', status: STATUS.none, message: 'message', title: 'title' });
```
Or you can call outside of React component `showToaster` from `@prokyon/components/Toaster/context`.
## Example
<Unstyled>
<ToasterWrapper>
<Button
label="Open Toaster"
status={StatusEnum.info}
onClick={() => {
showToaster({
id: 'none',
title: 'Show toaster',
label: 'Show toaster',
message: 'Toaster message.',
status: StatusEnum.info,
});
}}
/>
<Toasters />
</ToasterWrapper>
</Unstyled>

View file

@ -0,0 +1,44 @@
import { Canvas, Meta } from '@storybook/blocks';
import * as ToastersStories from './Toasters.stories';
<Meta of={ToastersStories} />
# Toasters
## Before start
### In root application use
```tsx
import { Toasters } from '@prokyon/components/Toaster';
import { ToasterWrapper } from '@prokyon/components/Toaster/context';
return (
<ToasterWrapper>
...
<Toasters />
</ToasterWrapper>
);
```
## Use hook `useToaster()`
```tsx
import { useToaster } from '@prokyon/components/Toaster/hooks';
const { showToaster } = useToaster();
showToaster({ id: 'id', status: STATUS.none, message: 'message', title: 'title' });
```
<Canvas of={ToastersStories.Hook} withToolbar />
## Use function `showToaster()`
```tsx
import { showToaster } from '@prokyon/components/Toaster/context';
showToaster({ id: 'id', status: STATUS.none, message: 'message', title: 'title' });
```
<Canvas of={ToastersStories.OutsideReact} withToolbar />

View file

@ -0,0 +1,89 @@
import React from 'react';
import { Meta, StoryObj } from '@storybook/react';
import { Button } from '@prokyon/components/Button';
import { Toasters } from '@prokyon/components/Toaster';
import { ToastersPosition } from '@prokyon/components/Toaster/components/Toasters';
import { showToaster, ToasterWrapper } from '@prokyon/components/Toaster/context';
import { useToaster } from '@prokyon/components/Toaster/hooks';
import { StatusEnum } from '@prokyon/types/common';
type Story = StoryObj<typeof Toasters>;
export default {
component: Toasters,
argTypes: {
position: {
options: [
ToastersPosition.TopLeft,
ToastersPosition.TopCenter,
ToastersPosition.TopRight,
ToastersPosition.RightCenter,
ToastersPosition.BottomLeft,
ToastersPosition.BottomCenter,
ToastersPosition.BottomRight,
ToastersPosition.LeftCenter,
],
control: { type: 'radio' },
},
},
decorators: [
(Story) => (
<div className="h-80">
<ToasterWrapper>
<Story />
</ToasterWrapper>
</div>
),
],
} as Meta;
export const Hook: Story = {
name: 'Hook',
render: (args) => {
const Comp = () => {
const { showToaster } = useToaster();
return (
<Button
label="Open Toaster message"
onClick={() =>
showToaster({
id: 'toaster1',
title: 'Title',
message: 'Message',
status: StatusEnum.info,
})
}
/>
);
};
return (
<>
<Comp />
<Toasters {...args} />
</>
);
},
};
export const OutsideReact: Story = {
name: 'Function',
render: (args) => {
return (
<>
<Button
label="Open Toaster message"
onClick={() =>
showToaster({
id: 'toaster2',
title: 'Title',
message: 'Message',
status: StatusEnum.success,
})
}
/>
<Toasters {...args} />
</>
);
},
};