Skip to content

Commit

Permalink
custom spinner and 404 page
Browse files Browse the repository at this point in the history
  • Loading branch information
samuelcolvin committed Nov 28, 2023
1 parent adc2d51 commit 3cddb00
Show file tree
Hide file tree
Showing 8 changed files with 95 additions and 23 deletions.
1 change: 0 additions & 1 deletion packages/fastui/src/DefaultLoading.tsx

This file was deleted.

3 changes: 3 additions & 0 deletions packages/fastui/src/Defaults.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export const DefaultSpinner = () => <div>loading...</div>

export const DefaultNotFound = ({ url }: { url: string }) => <div>Page not found: {url}</div>
30 changes: 20 additions & 10 deletions packages/fastui/src/components/ServerLoad.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { FC, useCallback, useContext, useEffect, useState } from 'react'

import { ErrorContext } from '../hooks/error'
import { useRequest, useSSE } from '../tools'
import { DefaultLoading } from '../DefaultLoading'
import { DefaultSpinner, DefaultNotFound } from '../Defaults'
import { ConfigContext } from '../hooks/config'
import { PageEvent, usePageEventListen } from '../events'
import { EventContextProvider, useEventContext } from '../hooks/eventContext'
Expand All @@ -16,6 +16,7 @@ export interface ServerLoadProps {
loadTrigger?: PageEvent
sse?: boolean
}

export const ServerLoadComp: FC<ServerLoadProps> = ({ path, components, loadTrigger, sse }) => {
if (components) {
return <ServerLoadDefer path={path} components={components} loadTrigger={loadTrigger} sse={sse} />
Expand Down Expand Up @@ -47,20 +48,27 @@ const ServerLoadDefer: FC<{ path: string; components: FastProps[]; loadTrigger?:

export const ServerLoadFetch: FC<{ path: string; devReload?: number }> = ({ path, devReload }) => {
const [componentProps, setComponentProps] = useState<FastProps[] | null>(null)
const [notFoundUrl, setNotFoundUrl] = useState<string | undefined>(undefined)

const url = useServerUrl(path)
const request = useRequest()

useEffect(() => {
const promise = request({ url })
promise.then(([, data]) => setComponentProps(data as FastProps[]))
const promise = request({ url, expectedStatus: [200, 404] })
promise.then(([status, data]) => {
if (status === 200) {
setComponentProps(data as FastProps[])
} else {
setNotFoundUrl(url)
}
})

return () => {
promise.then(() => null)
}
}, [url, request, devReload])

return <Render propsList={componentProps} />
return <Render propsList={componentProps} notFoundUrl={notFoundUrl} />
}

export const ServerLoadSSE: FC<{ path: string }> = ({ path }) => {
Expand All @@ -73,17 +81,19 @@ export const ServerLoadSSE: FC<{ path: string }> = ({ path }) => {
return <Render propsList={componentProps} />
}

const Render: FC<{ propsList: FastProps[] | null }> = ({ propsList }) => {
const Render: FC<{ propsList: FastProps[] | null; notFoundUrl?: string }> = ({ propsList, notFoundUrl }) => {
const { error } = useContext(ErrorContext)
const { Loading } = useContext(ConfigContext)
const { Spinner, NotFound } = useContext(ConfigContext)
const SpinnerComp = Spinner ?? DefaultSpinner
const NotFoundComp = NotFound ?? DefaultNotFound

if (propsList === null) {
if (notFoundUrl) {
return <NotFoundComp url={notFoundUrl} />
} else if (propsList === null) {
if (error) {
return <></>
} else if (Loading) {
return <Loading />
} else {
return <DefaultLoading />
return <SpinnerComp />
}
} else {
return <AnyCompList propsList={propsList} />
Expand Down
10 changes: 2 additions & 8 deletions packages/fastui/src/hooks/config.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,10 @@
import { createContext, FC, useContext } from 'react'

import type { CustomRender } from '../index'
import type { FastUIProps } from '../index'

import { FastProps } from '../components'

interface Config {
rootUrl: string
// defaults to 'append'
pathSendMode?: 'append' | 'query'
customRender?: CustomRender
Loading?: FC
}
type Config = Omit<FastUIProps, 'DisplayError' | 'classNameGenerator' | 'devMode'>

export const ConfigContext = createContext<Config>({ rootUrl: '' })

Expand Down
3 changes: 2 additions & 1 deletion packages/fastui/src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ export interface FastUIProps {
rootUrl: string
// defaults to 'append'
pathSendMode?: 'append' | 'query'
Loading?: FC
Spinner?: FC
NotFound?: FC<{ url: string }>
DisplayError?: ErrorDisplayType
classNameGenerator?: ClassNameGenerator
customRender?: CustomRender
Expand Down
23 changes: 22 additions & 1 deletion packages/vanilla/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,28 @@ import * as bootstrap from 'fastui-bootstrap'
export default function App() {
return (
<div className="app">
<FastUI rootUrl="/api" classNameGenerator={bootstrap.classNameGenerator} customRender={bootstrap.customRender} />
<FastUI
rootUrl="/api"
classNameGenerator={bootstrap.classNameGenerator}
customRender={bootstrap.customRender}
NotFound={NotFound}
Spinner={Spinner}
/>
</div>
)
}

const NotFound = ({ url }: { url: string }) => (
<div className="container mt-5 text-center">
<h1>Page not found</h1>
<p>
No page found at <code>{url}</code>.
</p>
</div>
)

const Spinner = () => (
<div className="container d-flex justify-content-center my-3" role="status">
<div className="spinner" />
</div>
)
44 changes: 44 additions & 0 deletions packages/vanilla/src/main.scss
Original file line number Diff line number Diff line change
@@ -1 +1,45 @@
@import 'bootstrap/scss/bootstrap';

// custom spinner from https://cssloaders.github.io/

.spinner,
.spinner:before,
.spinner:after {
border-radius: 50%;
width: 2.5em;
height: 2.5em;
animation-fill-mode: both;
animation: dots 1.8s infinite ease-in-out;
}
.spinner {
color: var(--bs-dark);
font-size: 7px;
position: relative;
text-indent: -9999em;
transform: translateZ(0);
animation-delay: -0.16s;
&:before,
&:after {
content: '';
position: absolute;
top: 0;
}
&:before {
left: -3.5em;
animation-delay: -0.32s;
}
&:after {
left: 3.5em;
}
}

@keyframes dots {
0%,
80%,
100% {
box-shadow: 0 2.5em 0 -1.3em;
}
40% {
box-shadow: 0 2.5em 0 0;
}
}
4 changes: 2 additions & 2 deletions python/demo/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ def panel(*components: AnyComponent) -> AnyComponent:


@app.get('/api/', response_model=FastUI, response_model_exclude_none=True)
def read_root() -> list[AnyComponent]:
def api_index() -> list[AnyComponent]:
return [
c.PageTitle(text='FastUI Demo'),
navbar(),
Expand Down Expand Up @@ -245,7 +245,7 @@ async def sse_generator():
d = datetime.now()
m = FastUI(root=[c.Div(components=[c.Text(text=f'Time {d:%H:%M:%S.%f}'[:-4])], class_name='font-monospace')])
yield dict(data=m.model_dump_json(by_alias=True))
await asyncio.sleep(0.15)
await asyncio.sleep(0.09)


@app.get('/api/sse', response_class=EventSourceResponse)
Expand Down

0 comments on commit 3cddb00

Please sign in to comment.