diff --git a/demo/components_list.py b/demo/components_list.py
index 25c9ecf9..a7ab8951 100644
--- a/demo/components_list.py
+++ b/demo/components_list.py
@@ -192,6 +192,19 @@ class Delivery(BaseModel):
],
class_name='border-top mt-3 pt-1',
),
+ c.Div(
+ components=[
+ c.Heading(text='Spinner', level=2),
+ c.Paragraph(
+ text=(
+ 'A component displayed while waiting for content to load, '
+ 'this is also used automatically while loading server content.'
+ )
+ ),
+ c.Spinner(text='Content incoming...'),
+ ],
+ class_name='border-top mt-3 pt-1',
+ ),
c.Div(
components=[
c.Heading(text='Video', level=2),
diff --git a/src/npm-fastui-bootstrap/src/index.tsx b/src/npm-fastui-bootstrap/src/index.tsx
index b394a3c5..05567799 100644
--- a/src/npm-fastui-bootstrap/src/index.tsx
+++ b/src/npm-fastui-bootstrap/src/index.tsx
@@ -150,5 +150,13 @@ export const classNameGenerator: ClassNameGenerator = ({
} else {
return 'error-alert alert alert-danger m-3'
}
+ case 'Spinner':
+ if (subElement === 'text') {
+ return 'd-flex justify-content-center mb-2'
+ } else if (subElement === 'animation') {
+ return 'd-flex justify-content-center'
+ } else {
+ return 'my-4'
+ }
}
}
diff --git a/src/npm-fastui-prebuilt/src/App.tsx b/src/npm-fastui-prebuilt/src/App.tsx
index 7fb00135..c43cfdd4 100644
--- a/src/npm-fastui-prebuilt/src/App.tsx
+++ b/src/npm-fastui-prebuilt/src/App.tsx
@@ -11,7 +11,6 @@ export default function App() {
classNameGenerator={bootstrap.classNameGenerator}
customRender={customRender}
NotFound={NotFound}
- Spinner={Spinner}
Transition={Transition}
/>
)
@@ -30,12 +29,6 @@ const NotFound = ({ url }: { url: string }) => (
)
-const Spinner = () => (
-
-)
-
const Transition: FC<{ children: ReactNode; transitioning: boolean }> = ({ children, transitioning }) => (
<>
diff --git a/src/npm-fastui-prebuilt/src/main.scss b/src/npm-fastui-prebuilt/src/main.scss
index b099f9c8..a4d5c8cf 100644
--- a/src/npm-fastui-prebuilt/src/main.scss
+++ b/src/npm-fastui-prebuilt/src/main.scss
@@ -74,17 +74,17 @@ h6 {
}
// custom spinner from https://cssloaders.github.io/
-
-.spinner,
-.spinner:before,
-.spinner:after {
+.fastui-spinner-animation,
+.fastui-spinner-animation:before,
+.fastui-spinner-animation:after {
border-radius: 50%;
width: 2.5em;
height: 2.5em;
animation-fill-mode: both;
- animation: dots 1.8s infinite ease-in-out;
+ animation: spinner-dots 1.8s infinite ease-in-out;
}
-.spinner {
+.fastui-spinner-animation {
+ top: -2.5em;
color: var(--bs-dark);
font-size: 7px;
position: relative;
@@ -105,8 +105,7 @@ h6 {
left: 3.5em;
}
}
-
-@keyframes dots {
+@keyframes spinner-dots {
0%,
80%,
100% {
diff --git a/src/npm-fastui/src/Defaults.tsx b/src/npm-fastui/src/Defaults.tsx
index 3b636da5..a8469a61 100644
--- a/src/npm-fastui/src/Defaults.tsx
+++ b/src/npm-fastui/src/Defaults.tsx
@@ -1,7 +1,5 @@
import { FC, ReactNode } from 'react'
-export const DefaultSpinner: FC = () => loading...
-
export const DefaultNotFound: FC<{ url: string }> = ({ url }) => Page not found: {url}
// default here does nothing
diff --git a/src/npm-fastui/src/components/ServerLoad.tsx b/src/npm-fastui/src/components/ServerLoad.tsx
index defda042..0199cb3d 100644
--- a/src/npm-fastui/src/components/ServerLoad.tsx
+++ b/src/npm-fastui/src/components/ServerLoad.tsx
@@ -4,7 +4,7 @@ import type { ServerLoad, PageEvent, FastProps } from '../models'
import { ErrorContext } from '../hooks/error'
import { useRequest, useSSE } from '../tools'
-import { DefaultSpinner, DefaultNotFound, DefaultTransition } from '../Defaults'
+import { DefaultNotFound, DefaultTransition } from '../Defaults'
import { ConfigContext } from '../hooks/config'
import { usePageEventListen } from '../events'
import { EventContextProvider, useEventContext } from '../hooks/eventContext'
@@ -12,6 +12,8 @@ import { LocationContext } from '../hooks/locationContext'
import { AnyCompList } from './index'
+import { SpinnerComp } from './spinner'
+
export const ServerLoadComp: FC = ({ path, components, loadTrigger, sse }) => {
if (components) {
return
@@ -103,8 +105,7 @@ const Render: FC<{ propsList: FastProps[] | null; notFoundUrl?: string; transiti
transitioning,
}) => {
const { error } = useContext(ErrorContext)
- const { Spinner, NotFound, Transition } = useContext(ConfigContext)
- const SpinnerComp = Spinner ?? DefaultSpinner
+ const { NotFound, Transition } = useContext(ConfigContext)
const NotFoundComp = NotFound ?? DefaultNotFound
const TransitionComp = Transition ?? DefaultTransition
@@ -114,7 +115,7 @@ const Render: FC<{ propsList: FastProps[] | null; notFoundUrl?: string; transiti
if (error) {
return <>>
} else {
- return
+ return
}
} else {
return (
diff --git a/src/npm-fastui/src/components/index.tsx b/src/npm-fastui/src/components/index.tsx
index 1cd84748..d0398a75 100644
--- a/src/npm-fastui/src/components/index.tsx
+++ b/src/npm-fastui/src/components/index.tsx
@@ -39,6 +39,7 @@ import { IframeComp } from './Iframe'
import { VideoComp } from './video'
import { FireEventComp } from './FireEvent'
import { ErrorComp } from './error'
+import { SpinnerComp } from './spinner'
import { CustomComp } from './Custom'
// TODO some better way to export components
@@ -73,6 +74,7 @@ export {
VideoComp,
FireEventComp,
ErrorComp,
+ SpinnerComp,
CustomComp,
LinkRender,
}
@@ -160,6 +162,8 @@ export const AnyComp: FC = (props) => {
return
case 'Error':
return
+ case 'Spinner':
+ return
case 'Custom':
return
default:
diff --git a/src/npm-fastui/src/components/spinner.tsx b/src/npm-fastui/src/components/spinner.tsx
new file mode 100644
index 00000000..687a6b71
--- /dev/null
+++ b/src/npm-fastui/src/components/spinner.tsx
@@ -0,0 +1,18 @@
+import { FC } from 'react'
+
+import type { Spinner } from '../models'
+
+import { useClassName } from '../hooks/className'
+
+export const SpinnerComp: FC = (props) => {
+ const { text } = props
+
+ return (
+
+ )
+}
diff --git a/src/npm-fastui/src/index.tsx b/src/npm-fastui/src/index.tsx
index 6bb8db40..9eed89b7 100644
--- a/src/npm-fastui/src/index.tsx
+++ b/src/npm-fastui/src/index.tsx
@@ -24,7 +24,6 @@ export interface FastUIProps {
APIPathMode?: 'append' | 'query'
// start of the path to remove from the URL before making a request to the API
APIPathStrip?: string
- Spinner?: FC
NotFound?: FC<{ url: string }>
Transition?: FC<{ children: ReactNode; transitioning: boolean }>
classNameGenerator?: ClassNameGenerator
diff --git a/src/npm-fastui/src/models.d.ts b/src/npm-fastui/src/models.d.ts
index f7eb10bd..4cac44fb 100644
--- a/src/npm-fastui/src/models.d.ts
+++ b/src/npm-fastui/src/models.d.ts
@@ -26,6 +26,7 @@ export type FastProps =
| Video
| FireEvent
| Error
+ | Spinner
| Custom
| Table
| Pagination
@@ -256,6 +257,11 @@ export interface Error {
type: 'Error'
children?: ReactNode
}
+export interface Spinner {
+ text?: string
+ className?: ClassName
+ type: 'Spinner'
+}
export interface Custom {
data: JsonData
subType: string
diff --git a/src/python-fastui/fastui/components/__init__.py b/src/python-fastui/fastui/components/__init__.py
index b43128d6..fb728e6f 100644
--- a/src/python-fastui/fastui/components/__init__.py
+++ b/src/python-fastui/fastui/components/__init__.py
@@ -28,7 +28,7 @@
from .tables import Pagination, Table
__all__ = (
- # first we include everything from `AnyComponent`
+ # first we include all components from this file
'Text',
'Paragraph',
'PageTitle',
@@ -48,7 +48,9 @@
'Iframe',
'FireEvent',
'Error',
+ 'Spinner',
'Custom',
+ # then we include components from other files
'Table',
'Pagination',
'Display',
@@ -291,6 +293,12 @@ def __get_pydantic_json_schema__(
return json_schema
+class Spinner(_p.BaseModel, extra='forbid'):
+ text: _t.Union[str, None] = None
+ class_name: _class_name.ClassNameField = None
+ type: _t.Literal['Spinner'] = 'Spinner'
+
+
class Custom(_p.BaseModel, extra='forbid'):
data: _types.JsonData
sub_type: str = _p.Field(serialization_alias='subType')
@@ -322,6 +330,7 @@ class Custom(_p.BaseModel, extra='forbid'):
Video,
FireEvent,
Error,
+ Spinner,
Custom,
Table,
Pagination,