diff --git a/demo/components_list.py b/demo/components_list.py
index e7ecd0c5..8132fca1 100644
--- a/demo/components_list.py
+++ b/demo/components_list.py
@@ -282,6 +282,20 @@ class Delivery(BaseModel):
],
class_name='border-top mt-3 pt-1',
),
+ c.Div(
+ components=[
+ c.Heading(text='Button and Toast', level=2),
+ c.Paragraph(text='The button below will open a toast.'),
+ c.Button(text='Show Toast', on_click=PageEvent(name='show-toast')),
+ c.Toast(
+ title='Toast',
+ body=[c.Paragraph(text='This is a toast.')],
+ open_trigger=PageEvent(name='show-toast'),
+ position='bottom-end',
+ ),
+ ],
+ class_name='border-top mt-3 pt-1',
+ ),
title='Components',
)
diff --git a/demo/main.py b/demo/main.py
index cdba7e22..00cee5c7 100644
--- a/demo/main.py
+++ b/demo/main.py
@@ -34,6 +34,7 @@ def api_index() -> list[AnyComponent]:
* `Image` - example [here](/components#image)
* `Iframe` - example [here](/components#iframe)
* `Video` - example [here](/components#video)
+* `Toast` - example [here](/components#toast)
* `Table` — See [cities table](/table/cities) and [users table](/table/users)
* `Pagination` — See the bottom of the [cities table](/table/cities)
* `ModelForm` — See [forms](/forms/login)
diff --git a/src/npm-fastui-bootstrap/src/index.tsx b/src/npm-fastui-bootstrap/src/index.tsx
index 2a9f01e5..3341cb6a 100644
--- a/src/npm-fastui-bootstrap/src/index.tsx
+++ b/src/npm-fastui-bootstrap/src/index.tsx
@@ -6,6 +6,7 @@ import { Modal } from './modal'
import { Navbar } from './navbar'
import { Pagination } from './pagination'
import { Footer } from './footer'
+import { Toast } from './toast'
export const customRender: CustomRender = (props) => {
const { type } = props
@@ -18,6 +19,8 @@ export const customRender: CustomRender = (props) => {
return () =>
case 'Pagination':
return () =>
+ case 'Toast':
+ return () =>
}
}
diff --git a/src/npm-fastui-bootstrap/src/toast.tsx b/src/npm-fastui-bootstrap/src/toast.tsx
new file mode 100644
index 00000000..989d6949
--- /dev/null
+++ b/src/npm-fastui-bootstrap/src/toast.tsx
@@ -0,0 +1,25 @@
+import { FC } from 'react'
+import { components, events, renderClassName, EventContextProvider, models } from 'fastui'
+import BootstrapToast from 'react-bootstrap/Toast'
+import BootstrapToastContainer from 'react-bootstrap/ToastContainer'
+
+export const Toast: FC = (props) => {
+ const { className, title, body, position, openTrigger, openContext } = props
+
+ const { eventContext, fireId, clear } = events.usePageEventListen(openTrigger, openContext)
+
+ return (
+
+
+
+
+ {title}
+
+
+
+
+
+
+
+ )
+}
diff --git a/src/npm-fastui/src/components/index.tsx b/src/npm-fastui/src/components/index.tsx
index d0398a75..d82b5754 100644
--- a/src/npm-fastui/src/components/index.tsx
+++ b/src/npm-fastui/src/components/index.tsx
@@ -41,6 +41,7 @@ import { FireEventComp } from './FireEvent'
import { ErrorComp } from './error'
import { SpinnerComp } from './spinner'
import { CustomComp } from './Custom'
+import { ToastComp } from './toast'
// TODO some better way to export components
export {
@@ -77,6 +78,7 @@ export {
SpinnerComp,
CustomComp,
LinkRender,
+ ToastComp,
}
export type FastClassNameProps = Exclude
@@ -166,6 +168,8 @@ export const AnyComp: FC = (props) => {
return
case 'Custom':
return
+ case 'Toast':
+ return
default:
unreachable('Unexpected component type', type, props)
return
diff --git a/src/npm-fastui/src/components/toast.tsx b/src/npm-fastui/src/components/toast.tsx
new file mode 100644
index 00000000..01eb1658
--- /dev/null
+++ b/src/npm-fastui/src/components/toast.tsx
@@ -0,0 +1,22 @@
+import { FC, useEffect } from 'react'
+
+import type { Toast } from '../models'
+
+import { usePageEventListen } from '../events'
+
+export const ToastComp: FC = (props) => {
+ const { title, openTrigger, openContext } = props
+
+ const { fireId, clear } = usePageEventListen(openTrigger, openContext)
+
+ useEffect(() => {
+ if (fireId) {
+ setTimeout(() => {
+ alert(`${title}\n\nNote: modals are not implemented by pure FastUI, implement a component for 'ToastProps'.`)
+ clear()
+ })
+ }
+ }, [fireId, title, clear])
+
+ return <>>
+}
diff --git a/src/npm-fastui/src/models.d.ts b/src/npm-fastui/src/models.d.ts
index a0ca91cd..9877b2ca 100644
--- a/src/npm-fastui/src/models.d.ts
+++ b/src/npm-fastui/src/models.d.ts
@@ -40,6 +40,7 @@ export type FastProps =
| FormFieldSelect
| FormFieldSelectSearch
| ModelForm
+ | Toast
export type ClassName =
| string
| ClassName[]
@@ -460,3 +461,21 @@ export interface ModelForm {
| FormFieldSelectSearch
)[]
}
+export interface Toast {
+ title: string
+ body: FastProps[]
+ position?:
+ | 'top-start'
+ | 'top-center'
+ | 'top-end'
+ | 'middle-start'
+ | 'middle-center'
+ | 'middle-end'
+ | 'bottom-start'
+ | 'bottom-center'
+ | 'bottom-end'
+ openTrigger?: PageEvent
+ openContext?: ContextType
+ className?: ClassName
+ type: 'Toast'
+}
diff --git a/src/python-fastui/fastui/components/__init__.py b/src/python-fastui/fastui/components/__init__.py
index f2d3f423..6594863e 100644
--- a/src/python-fastui/fastui/components/__init__.py
+++ b/src/python-fastui/fastui/components/__init__.py
@@ -303,6 +303,29 @@ class Spinner(_p.BaseModel, extra='forbid'):
type: _t.Literal['Spinner'] = 'Spinner'
+class Toast(_p.BaseModel, extra='forbid'):
+ title: str
+ body: '_t.List[AnyComponent]'
+ position: _t.Union[
+ _t.Literal[
+ 'top-start',
+ 'top-center',
+ 'top-end',
+ 'middle-start',
+ 'middle-center',
+ 'middle-end',
+ 'bottom-start',
+ 'bottom-center',
+ 'bottom-end',
+ ],
+ None,
+ ] = None
+ open_trigger: _t.Union[events.PageEvent, None] = _p.Field(default=None, serialization_alias='openTrigger')
+ open_context: _t.Union[events.ContextType, None] = _p.Field(default=None, serialization_alias='openContext')
+ class_name: _class_name.ClassNameField = None
+ type: _t.Literal['Toast'] = 'Toast'
+
+
class Custom(_p.BaseModel, extra='forbid'):
data: _types.JsonData
sub_type: str = _p.Field(serialization_alias='subType')
@@ -343,6 +366,7 @@ class Custom(_p.BaseModel, extra='forbid'):
Form,
FormField,
ModelForm,
+ Toast,
],
_p.Field(discriminator='type'),
]