Skip to content

Commit dc860f7

Browse files
authored
Modal prompt (#183)
1 parent f26041e commit dc860f7

File tree

12 files changed

+104
-25
lines changed

12 files changed

+104
-25
lines changed

demo/components_list.py

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,56 @@ class Delivery(BaseModel):
126126
],
127127
class_name='border-top mt-3 pt-1',
128128
),
129+
c.Div(
130+
components=[
131+
c.Heading(text='Modal Form / Confirm prompt', level=2),
132+
c.Markdown(text='The button below will open a modal with a form.'),
133+
c.Button(text='Show Modal Form', on_click=PageEvent(name='modal-form')),
134+
c.Modal(
135+
title='Modal Form',
136+
body=[
137+
c.Paragraph(text='Form inside a modal!'),
138+
c.Form(
139+
form_fields=[
140+
c.FormFieldInput(name='foobar', title='Foobar', required=True),
141+
],
142+
submit_url='/api/components/modal-form',
143+
footer=[],
144+
submit_trigger=PageEvent(name='modal-form-submit'),
145+
),
146+
],
147+
footer=[
148+
c.Button(
149+
text='Cancel', named_style='secondary', on_click=PageEvent(name='modal-form', clear=True)
150+
),
151+
c.Button(text='Submit', on_click=PageEvent(name='modal-form-submit')),
152+
],
153+
open_trigger=PageEvent(name='modal-form'),
154+
),
155+
c.Button(text='Show Modal Prompt', on_click=PageEvent(name='modal-prompt'), class_name='+ ms-2'),
156+
c.Modal(
157+
title='Form Prompt',
158+
body=[
159+
c.Paragraph(text='Are you sure you want to do whatever?'),
160+
c.Form(
161+
form_fields=[],
162+
submit_url='/api/components/modal-prompt',
163+
loading=[c.Spinner(text='Okay, good luck...')],
164+
footer=[],
165+
submit_trigger=PageEvent(name='modal-form-submit'),
166+
),
167+
],
168+
footer=[
169+
c.Button(
170+
text='Cancel', named_style='secondary', on_click=PageEvent(name='modal-prompt', clear=True)
171+
),
172+
c.Button(text='Submit', on_click=PageEvent(name='modal-form-submit')),
173+
],
174+
open_trigger=PageEvent(name='modal-prompt'),
175+
),
176+
],
177+
class_name='border-top mt-3 pt-1',
178+
),
129179
c.Div(
130180
components=[
131181
c.Heading(text='Server Load', level=2),
@@ -240,3 +290,15 @@ class Delivery(BaseModel):
240290
async def modal_view() -> list[AnyComponent]:
241291
await asyncio.sleep(0.5)
242292
return [c.Paragraph(text='This is some dynamic content. Open devtools to see me being fetched from the server.')]
293+
294+
295+
@router.post('/modal-form', response_model=FastUI, response_model_exclude_none=True)
296+
async def modal_form_submit() -> list[AnyComponent]:
297+
await asyncio.sleep(0.5)
298+
return [c.FireEvent(event=PageEvent(name='modal-form', clear=True))]
299+
300+
301+
@router.post('/modal-prompt', response_model=FastUI, response_model_exclude_none=True)
302+
async def modal_prompt_submit() -> list[AnyComponent]:
303+
await asyncio.sleep(0.5)
304+
return [c.FireEvent(event=PageEvent(name='modal-prompt', clear=True))]

demo/forms.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -85,19 +85,19 @@ def form_content(kind: FormKind):
8585
return [
8686
c.Heading(text='Login Form', level=2),
8787
c.Paragraph(text='Simple login form with email and password.'),
88-
c.ModelForm(model=LoginForm, submit_url='/api/forms/login'),
88+
c.ModelForm(model=LoginForm, display_mode='page', submit_url='/api/forms/login'),
8989
]
9090
case 'select':
9191
return [
9292
c.Heading(text='Select Form', level=2),
9393
c.Paragraph(text='Form showing different ways of doing select.'),
94-
c.ModelForm(model=SelectForm, submit_url='/api/forms/select'),
94+
c.ModelForm(model=SelectForm, display_mode='page', submit_url='/api/forms/select'),
9595
]
9696
case 'big':
9797
return [
9898
c.Heading(text='Large Form', level=2),
9999
c.Paragraph(text='Form with a lot of fields.'),
100-
c.ModelForm(model=BigModel, submit_url='/api/forms/big'),
100+
c.ModelForm(model=BigModel, display_mode='page', submit_url='/api/forms/big'),
101101
]
102102
case _:
103103
raise ValueError(f'Invalid kind {kind!r}')

src/npm-fastui-bootstrap/package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@pydantic/fastui-bootstrap",
3-
"version": "0.0.20",
3+
"version": "0.0.21",
44
"description": "Boostrap renderer for FastUI",
55
"main": "dist/index.js",
66
"types": "dist/index.d.ts",
@@ -29,6 +29,6 @@
2929
"sass": "^1.69.5"
3030
},
3131
"peerDependencies": {
32-
"@pydantic/fastui": "0.0.20"
32+
"@pydantic/fastui": "0.0.21"
3333
}
3434
}

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,13 +62,15 @@ export const classNameGenerator: ClassNameGenerator = ({
6262
default:
6363
return 'row row-cols-lg-4 align-items-center justify-content-end'
6464
}
65-
} else {
65+
} else if (props.displayMode === 'page') {
6666
switch (subElement) {
6767
case 'form-container':
6868
return 'row justify-content-center'
6969
default:
7070
return 'col-md-4'
7171
}
72+
} else {
73+
break
7274
}
7375
case 'FormFieldInput':
7476
case 'FormFieldTextarea':

src/npm-fastui-prebuilt/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@pydantic/fastui-prebuilt",
3-
"version": "0.0.20",
3+
"version": "0.0.21",
44
"description": "Pre-built files for FastUI",
55
"main": "dist/index.html",
66
"type": "module",

src/npm-fastui-prebuilt/src/main.scss

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
$primary: black;
2-
$secondary: #666;
2+
$secondary: white;
33
$link-color: #0d6efd; // bootstrap primary
44

55
@import 'bootstrap/scss/bootstrap';
@@ -121,3 +121,7 @@ h6 {
121121
position: relative;
122122
top: 60px;
123123
}
124+
125+
.btn-secondary {
126+
--bs-btn-border-color: #dee2e6;
127+
}

src/npm-fastui/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@pydantic/fastui",
3-
"version": "0.0.20",
3+
"version": "0.0.21",
44
"description": "Build better UIs faster.",
55
"main": "dist/index.js",
66
"types": "dist/index.d.ts",

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

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,17 @@ import { FormFieldProps } from './FormField'
1414

1515
export const FormComp: FC<Form | ModelForm> = (props) => {
1616
const formRef = useRef<HTMLFormElement>(null)
17-
const { formFields, initial, submitUrl, method, footer, displayMode, submitOnChange, submitTrigger } = props
17+
const {
18+
formFields,
19+
initial,
20+
submitUrl,
21+
method,
22+
footer,
23+
displayMode,
24+
submitOnChange,
25+
submitTrigger,
26+
loading: loadingComponents,
27+
} = props
1828

1929
// mostly equivalent to `<input disabled`
2030
const [locked, setLocked] = useState(false)
@@ -84,20 +94,14 @@ export const FormComp: FC<Form | ModelForm> = (props) => {
8494
}
8595

8696
const onChange = useCallback(() => {
87-
if (submitOnChange && formRef.current) {
88-
const formData = new FormData(formRef.current)
89-
submit(formData)
90-
}
91-
}, [submitOnChange, submit])
97+
submitOnChange && formRef.current && formRef.current.requestSubmit()
98+
}, [submitOnChange])
9299

93100
const { fireId } = usePageEventListen(submitTrigger)
94101

95102
useEffect(() => {
96-
if (fireId && formRef.current) {
97-
const formData = new FormData(formRef.current)
98-
submit(formData)
99-
}
100-
}, [fireId, submit])
103+
fireId && formRef.current && formRef.current.requestSubmit()
104+
}, [fireId])
101105

102106
const fieldProps: FormFieldProps[] = formFields.map((formField) => {
103107
const f = {
@@ -127,6 +131,7 @@ export const FormComp: FC<Form | ModelForm> = (props) => {
127131
return (
128132
<div className={containerClassName}>
129133
<form ref={formRef} className={formClassName} onSubmit={onSubmit}>
134+
{locked && loadingComponents && <AnyCompList propsList={loadingComponents} />}
130135
<AnyCompList propsList={fieldProps} />
131136
{error ? <div>Error: {error}</div> : null}
132137
<Footer footer={footer} />

src/npm-fastui/src/events.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,9 @@ export function usePageEventListen(event?: PageEvent, initialContext: ContextTyp
116116
return {
117117
eventContext,
118118
fireId,
119-
clear: useCallback(() => setEventContext(null), []),
119+
clear: useCallback(() => {
120+
setEventContext(null)
121+
setFireId(null)
122+
}, []),
120123
}
121124
}

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

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -319,9 +319,10 @@ export interface Form {
319319
[k: string]: JsonData
320320
}
321321
method?: 'POST' | 'GOTO' | 'GET'
322-
displayMode?: 'default' | 'inline'
322+
displayMode?: 'default' | 'page' | 'inline'
323323
submitOnChange?: boolean
324324
submitTrigger?: PageEvent
325+
loading?: FastProps[]
325326
footer?: FastProps[]
326327
className?: ClassName
327328
formFields: (
@@ -436,9 +437,10 @@ export interface ModelForm {
436437
[k: string]: JsonData
437438
}
438439
method?: 'POST' | 'GOTO' | 'GET'
439-
displayMode?: 'default' | 'inline'
440+
displayMode?: 'default' | 'page' | 'inline'
440441
submitOnChange?: boolean
441442
submitTrigger?: PageEvent
443+
loading?: FastProps[]
442444
footer?: FastProps[]
443445
className?: ClassName
444446
type: 'ModelForm'

0 commit comments

Comments
 (0)