From feb74b1bd4ee65456ba4fe6722b226cc61b8aff6 Mon Sep 17 00:00:00 2001 From: Chaoying Date: Tue, 19 Dec 2023 22:10:43 +0800 Subject: [PATCH] Add basic toast --- demo/components_list.py | 13 ++++++++ demo/main.py | 1 + src/npm-fastui-bootstrap/src/index.tsx | 3 ++ src/npm-fastui-bootstrap/src/toast.tsx | 25 ++++++++++++++ src/npm-fastui/src/components/index.tsx | 5 +++ src/npm-fastui/src/components/toast.tsx | 33 +++++++++++++++++++ .../fastui/components/__init__.py | 10 ++++++ .../tests/react-fastui-json-schema.json | 31 +++++++++++++++++ 8 files changed, 121 insertions(+) create mode 100644 src/npm-fastui-bootstrap/src/toast.tsx create mode 100644 src/npm-fastui/src/components/toast.tsx diff --git a/demo/components_list.py b/demo/components_list.py index 6e3622c1..06987b18 100644 --- a/demo/components_list.py +++ b/demo/components_list.py @@ -213,6 +213,19 @@ 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 some static content that was set when the modal was defined.')], + open_trigger=PageEvent(name='show-toast'), + ), + ], + class_name='border-top mt-3 pt-1', + ), title='Components', ) diff --git a/demo/main.py b/demo/main.py index b2fa16fe..c9d46234 100644 --- a/demo/main.py +++ b/demo/main.py @@ -36,6 +36,7 @@ def api_index() -> list[AnyComponent]: * `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) +* `Toast` - example [here](/components#toast) """ return demo_page(c.Markdown(text=markdown)) diff --git a/src/npm-fastui-bootstrap/src/index.tsx b/src/npm-fastui-bootstrap/src/index.tsx index 8eb7c20a..d4c4864a 100644 --- a/src/npm-fastui-bootstrap/src/index.tsx +++ b/src/npm-fastui-bootstrap/src/index.tsx @@ -5,6 +5,7 @@ import type { ClassNameGenerator, CustomRender, ClassName } from 'fastui' import { Modal } from './modal' import { Navbar } from './navbar' import { Pagination } from './pagination' +import { Toast } from './toast' export const customRender: CustomRender = (props) => { const { type } = props @@ -15,6 +16,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..5a11867f --- /dev/null +++ b/src/npm-fastui-bootstrap/src/toast.tsx @@ -0,0 +1,25 @@ +import { FC } from 'react' +import { components, events, renderClassName, EventContextProvider } from 'fastui' +import BootstrapToast from 'react-bootstrap/Toast' +import BootstrapToastContainer from 'react-bootstrap/ToastContainer' + +export const Toast: FC = (props) => { + const { className, title, body, 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 ad6d5f4b..40c3ae18 100644 --- a/src/npm-fastui/src/components/index.tsx +++ b/src/npm-fastui/src/components/index.tsx @@ -43,6 +43,7 @@ import { IframeComp, IframeProps } from './Iframe' import { VideoComp, VideoProps } from './video' import { FireEventComp, FireEventProps } from './FireEvent' import { CustomComp, CustomProps } from './Custom' +import { ToastComp, ToastProps } from './toast' export type { TextProps, @@ -73,6 +74,7 @@ export type { VideoProps, FireEventProps, CustomProps, + ToastProps, } // TODO some better way to export components @@ -106,6 +108,7 @@ export type FastProps = | VideoProps | FireEventProps | CustomProps + | ToastProps export type FastClassNameProps = Exclude @@ -194,6 +197,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..9ce5c4a3 --- /dev/null +++ b/src/npm-fastui/src/components/toast.tsx @@ -0,0 +1,33 @@ +import { FC, useEffect } from 'react' + +import type { FastProps } from './index' +import type { ContextType } from '../hooks/eventContext' + +import { ClassName } from '../hooks/className' +import { PageEvent, usePageEventListen } from '../events' + +export interface ToastProps { + type: 'Toast' + title: string + body: FastProps[] + openTrigger?: PageEvent + openContext?: ContextType + className?: ClassName +} + +export const ToastComp: FC = (props) => { + const { title, openTrigger, openContext } = props + + const { fireId, clear } = usePageEventListen(openTrigger, openContext) + + useEffect(() => { + if (fireId) { + setTimeout(() => { + alert(`${title}\n\nNote: toasts are not implemented by pure FastUI, implement a component for 'ToastProps'.`) + clear() + }) + } + }, [fireId, title, clear]) + + return <> +} diff --git a/src/python-fastui/fastui/components/__init__.py b/src/python-fastui/fastui/components/__init__.py index 9466f47b..d2e0ab22 100644 --- a/src/python-fastui/fastui/components/__init__.py +++ b/src/python-fastui/fastui/components/__init__.py @@ -251,6 +251,15 @@ class Custom(_p.BaseModel, extra='forbid'): type: _t.Literal['Custom'] = 'Custom' +class Toast(_p.BaseModel, extra='forbid'): + title: str + body: '_t.List[AnyComponent]' + 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' + + AnyComponent = _te.Annotated[ _t.Union[ Text, @@ -280,6 +289,7 @@ class Custom(_p.BaseModel, extra='forbid'): Form, FormField, ModelForm, + Toast, ], _p.Field(discriminator='type'), ] diff --git a/src/python-fastui/tests/react-fastui-json-schema.json b/src/python-fastui/tests/react-fastui-json-schema.json index 6990db17..a76621b5 100644 --- a/src/python-fastui/tests/react-fastui-json-schema.json +++ b/src/python-fastui/tests/react-fastui-json-schema.json @@ -426,6 +426,9 @@ }, { "$ref": "#/definitions/CustomProps" + }, + { + "$ref": "#/definitions/ToastProps" } ] }, @@ -1385,6 +1388,34 @@ "required": ["text", "type"], "type": "object" }, + "ToastProps": { + "properties": { + "body": { + "items": { + "$ref": "#/definitions/FastProps" + }, + "type": "array" + }, + "className": { + "$ref": "#/definitions/ClassName" + }, + "openContext": { + "$ref": "#/definitions/ContextType" + }, + "openTrigger": { + "$ref": "#/definitions/PageEvent" + }, + "title": { + "type": "string" + }, + "type": { + "const": "Toast", + "type": "string" + } + }, + "required": ["body", "title", "type"], + "type": "object" + }, "VideoProps": { "properties": { "autoplay": {