Skip to content

Commit 5469907

Browse files
authored
Add toast component (#109)
1 parent d0779c7 commit 5469907

File tree

8 files changed

+112
-0
lines changed

8 files changed

+112
-0
lines changed

demo/components_list.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -282,6 +282,20 @@ class Delivery(BaseModel):
282282
],
283283
class_name='border-top mt-3 pt-1',
284284
),
285+
c.Div(
286+
components=[
287+
c.Heading(text='Button and Toast', level=2),
288+
c.Paragraph(text='The button below will open a toast.'),
289+
c.Button(text='Show Toast', on_click=PageEvent(name='show-toast')),
290+
c.Toast(
291+
title='Toast',
292+
body=[c.Paragraph(text='This is a toast.')],
293+
open_trigger=PageEvent(name='show-toast'),
294+
position='bottom-end',
295+
),
296+
],
297+
class_name='border-top mt-3 pt-1',
298+
),
285299
title='Components',
286300
)
287301

demo/main.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ def api_index() -> list[AnyComponent]:
3434
* `Image` - example [here](/components#image)
3535
* `Iframe` - example [here](/components#iframe)
3636
* `Video` - example [here](/components#video)
37+
* `Toast` - example [here](/components#toast)
3738
* `Table` — See [cities table](/table/cities) and [users table](/table/users)
3839
* `Pagination` — See the bottom of the [cities table](/table/cities)
3940
* `ModelForm` — See [forms](/forms/login)

src/npm-fastui-bootstrap/src/index.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { Modal } from './modal'
66
import { Navbar } from './navbar'
77
import { Pagination } from './pagination'
88
import { Footer } from './footer'
9+
import { Toast } from './toast'
910

1011
export const customRender: CustomRender = (props) => {
1112
const { type } = props
@@ -18,6 +19,8 @@ export const customRender: CustomRender = (props) => {
1819
return () => <Modal {...props} />
1920
case 'Pagination':
2021
return () => <Pagination {...props} />
22+
case 'Toast':
23+
return () => <Toast {...props} />
2124
}
2225
}
2326

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import { FC } from 'react'
2+
import { components, events, renderClassName, EventContextProvider, models } from 'fastui'
3+
import BootstrapToast from 'react-bootstrap/Toast'
4+
import BootstrapToastContainer from 'react-bootstrap/ToastContainer'
5+
6+
export const Toast: FC<models.Toast> = (props) => {
7+
const { className, title, body, position, openTrigger, openContext } = props
8+
9+
const { eventContext, fireId, clear } = events.usePageEventListen(openTrigger, openContext)
10+
11+
return (
12+
<EventContextProvider context={eventContext}>
13+
<BootstrapToastContainer position={position} className="position-fixed bottom-0 end-0 p-3">
14+
<BootstrapToast className={renderClassName(className)} show={!!fireId} onClose={clear}>
15+
<BootstrapToast.Header>
16+
<strong className="me-auto">{title}</strong>
17+
</BootstrapToast.Header>
18+
<BootstrapToast.Body>
19+
<components.AnyCompList propsList={body} />
20+
</BootstrapToast.Body>
21+
</BootstrapToast>
22+
</BootstrapToastContainer>
23+
</EventContextProvider>
24+
)
25+
}

src/npm-fastui/src/components/index.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ import { FireEventComp } from './FireEvent'
4141
import { ErrorComp } from './error'
4242
import { SpinnerComp } from './spinner'
4343
import { CustomComp } from './Custom'
44+
import { ToastComp } from './toast'
4445

4546
// TODO some better way to export components
4647
export {
@@ -77,6 +78,7 @@ export {
7778
SpinnerComp,
7879
CustomComp,
7980
LinkRender,
81+
ToastComp,
8082
}
8183

8284
export type FastClassNameProps = Exclude<FastProps, Text | Display | ServerLoad | PageTitle | FireEvent>
@@ -166,6 +168,8 @@ export const AnyComp: FC<FastProps> = (props) => {
166168
return <SpinnerComp {...props} />
167169
case 'Custom':
168170
return <CustomComp {...props} />
171+
case 'Toast':
172+
return <ToastComp {...props} />
169173
default:
170174
unreachable('Unexpected component type', type, props)
171175
return <DisplayError title="Invalid Server Response" description={`Unknown component type: "${type}"`} />
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import { FC, useEffect } from 'react'
2+
3+
import type { Toast } from '../models'
4+
5+
import { usePageEventListen } from '../events'
6+
7+
export const ToastComp: FC<Toast> = (props) => {
8+
const { title, openTrigger, openContext } = props
9+
10+
const { fireId, clear } = usePageEventListen(openTrigger, openContext)
11+
12+
useEffect(() => {
13+
if (fireId) {
14+
setTimeout(() => {
15+
alert(`${title}\n\nNote: modals are not implemented by pure FastUI, implement a component for 'ToastProps'.`)
16+
clear()
17+
})
18+
}
19+
}, [fireId, title, clear])
20+
21+
return <></>
22+
}

src/npm-fastui/src/models.d.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ export type FastProps =
4040
| FormFieldSelect
4141
| FormFieldSelectSearch
4242
| ModelForm
43+
| Toast
4344
export type ClassName =
4445
| string
4546
| ClassName[]
@@ -460,3 +461,21 @@ export interface ModelForm {
460461
| FormFieldSelectSearch
461462
)[]
462463
}
464+
export interface Toast {
465+
title: string
466+
body: FastProps[]
467+
position?:
468+
| 'top-start'
469+
| 'top-center'
470+
| 'top-end'
471+
| 'middle-start'
472+
| 'middle-center'
473+
| 'middle-end'
474+
| 'bottom-start'
475+
| 'bottom-center'
476+
| 'bottom-end'
477+
openTrigger?: PageEvent
478+
openContext?: ContextType
479+
className?: ClassName
480+
type: 'Toast'
481+
}

src/python-fastui/fastui/components/__init__.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -303,6 +303,29 @@ class Spinner(_p.BaseModel, extra='forbid'):
303303
type: _t.Literal['Spinner'] = 'Spinner'
304304

305305

306+
class Toast(_p.BaseModel, extra='forbid'):
307+
title: str
308+
body: '_t.List[AnyComponent]'
309+
position: _t.Union[
310+
_t.Literal[
311+
'top-start',
312+
'top-center',
313+
'top-end',
314+
'middle-start',
315+
'middle-center',
316+
'middle-end',
317+
'bottom-start',
318+
'bottom-center',
319+
'bottom-end',
320+
],
321+
None,
322+
] = None
323+
open_trigger: _t.Union[events.PageEvent, None] = _p.Field(default=None, serialization_alias='openTrigger')
324+
open_context: _t.Union[events.ContextType, None] = _p.Field(default=None, serialization_alias='openContext')
325+
class_name: _class_name.ClassNameField = None
326+
type: _t.Literal['Toast'] = 'Toast'
327+
328+
306329
class Custom(_p.BaseModel, extra='forbid'):
307330
data: _types.JsonData
308331
sub_type: str = _p.Field(serialization_alias='subType')
@@ -343,6 +366,7 @@ class Custom(_p.BaseModel, extra='forbid'):
343366
Form,
344367
FormField,
345368
ModelForm,
369+
Toast,
346370
],
347371
_p.Field(discriminator='type'),
348372
]

0 commit comments

Comments
 (0)