(demo) use modal to show create form

Now that we have a modal component, let's use it to display our creation
 form.
This commit is contained in:
jbpenrath
2024-02-15 00:57:41 +01:00
committed by Jean-Baptiste PENRATH
parent 7913075faf
commit 86138df8fe
4 changed files with 114 additions and 106 deletions

View File

@@ -1,4 +1,9 @@
import { Button, CunninghamProvider, Select } from "@openfun/cunningham-react"; import {
Button,
CunninghamProvider,
Select,
useModal,
} from "@openfun/cunningham-react";
import React, { useEffect, useMemo, useState } from "react"; import React, { useEffect, useMemo, useState } from "react";
import { Create } from "./Create"; import { Create } from "./Create";
import { Home } from "./Home"; import { Home } from "./Home";
@@ -29,14 +34,7 @@ const THEMES: Record<Theme, Record<Variant, string | undefined>> = {
}, },
}; };
export enum Page { export type PageProps = ReturnType<typeof useModal>;
HOME,
CREATE,
}
export interface PageProps {
changePage: (page: Page) => void;
}
const preferredScheme = window.matchMedia("(prefers-color-scheme: dark)") const preferredScheme = window.matchMedia("(prefers-color-scheme: dark)")
.matches .matches
@@ -54,7 +52,7 @@ export const App = () => {
preferredScheme.variant, preferredScheme.variant,
); );
const activeTheme = useMemo(() => THEMES[theme][variant], [theme, variant]); const activeTheme = useMemo(() => THEMES[theme][variant], [theme, variant]);
const [page, setPage] = useState<Page>(Page.HOME); const modal = useModal();
const handleThemeChange = (event: MediaQueryListEvent) => { const handleThemeChange = (event: MediaQueryListEvent) => {
const nextVariant = event.matches ? Variant.DARK : Variant.LIGHT; const nextVariant = event.matches ? Variant.DARK : Variant.LIGHT;
if (THEMES[theme][nextVariant] !== undefined) { if (THEMES[theme][nextVariant] !== undefined) {
@@ -92,8 +90,8 @@ export const App = () => {
alt="Background pattern" alt="Background pattern"
/> />
)} )}
{page === Page.HOME && <Home changePage={(p) => setPage(p)} />} <Home modal={modal} />
{page === Page.CREATE && <Create changePage={(p) => setPage(p)} />} <Create {...modal} />
</div> </div>
<div className="theme-switch"> <div className="theme-switch">

View File

@@ -7,6 +7,8 @@ import {
DateRangePicker, DateRangePicker,
FileUploader, FileUploader,
Input, Input,
Modal,
ModalSize,
Radio, Radio,
RadioGroup, RadioGroup,
Select, Select,
@@ -16,10 +18,10 @@ import {
VariantType, VariantType,
} from "@openfun/cunningham-react"; } from "@openfun/cunningham-react";
import { faker } from "@faker-js/faker"; import { faker } from "@faker-js/faker";
import { PageProps } from "./App";
import { Character, database, randomDates } from "./Character"; import { Character, database, randomDates } from "./Character";
import { Page, PageProps } from "./App";
export const Create = ({ changePage }: PageProps) => { export const Create = (props: PageProps) => {
const { toast } = useToastProvider(); const { toast } = useToastProvider();
const submit: React.FormEventHandler<HTMLFormElement> = (e) => { const submit: React.FormEventHandler<HTMLFormElement> = (e) => {
@@ -40,102 +42,104 @@ export const Create = ({ changePage }: PageProps) => {
}; };
database.unshift(character); database.unshift(character);
changePage(Page.HOME); props.close();
toast("Character created successfully", VariantType.SUCCESS); toast("Character created successfully", VariantType.SUCCESS);
}; };
return ( return (
<form className="page__create clr-greyscale-900" onSubmit={submit}> <Modal {...props} size={ModalSize.LARGE} closeOnClickOutside>
<h1>Add a character</h1> <form className="page__create clr-greyscale-900" onSubmit={submit}>
<div className="card"> <h1>Add a character</h1>
<h3 className="fw-bold fs-h3">General Information</h3> <div className="card">
<Alert type={VariantType.INFO}> <h3 className="fw-bold fs-h3">General Information</h3>
You are about to add a new character to the collection <Alert type={VariantType.INFO}>
</Alert> You are about to add a new character to the collection
<Input </Alert>
name="name" <Input
label="Name" name="name"
text="Enter first and last name" label="Name"
fullWidth={true} text="Enter first and last name"
required fullWidth={true}
/> required
<Select
name="sex"
label="Sex"
fullWidth={true}
options={[
{
label: "Female",
value: "female",
},
{
label: "Male",
value: "male",
},
]}
/>
<DatePicker name="birthdate" label="Birth Date" fullWidth={true} />
<DateRangePicker
name="appearance_dates"
startLabel="First appearance"
endLabel="Last appearance"
fullWidth={true}
/>
<Checkbox name="is_guest" label="This character is a guest" />
</div>
<div className="card mt-l">
<h3 className="fw-bold fs-h3">Bio</h3>
<Alert type={VariantType.WARNING}>
Please be exhaustive, every detail counts!
</Alert>
<TextArea
name="bio"
label="Biography"
text="Enter a detailed biography"
fullWidth={true}
rows={8}
/>
<RadioGroup>
<Radio
label="Appeared between 0 and 5 times"
name="occurences"
value="A"
/> />
<Radio <Select
label="Appeared between 6 and 10 times" name="sex"
name="occurences" label="Sex"
value="B" fullWidth={true}
options={[
{
label: "Female",
value: "female",
},
{
label: "Male",
value: "male",
},
]}
/> />
<Radio <DatePicker name="birthdate" label="Birth Date" fullWidth={true} />
label="Appeared between 10 and 20 times" <DateRangePicker
name="occurences" name="appearance_dates"
value="C" startLabel="First appearance"
endLabel="Last appearance"
fullWidth={true}
/> />
<Radio <Checkbox name="is_guest" label="This character is a guest" />
label="Appeared between 20+ times" </div>
name="occurences" <div className="card mt-l">
value="D" <h3 className="fw-bold fs-h3">Bio</h3>
<Alert type={VariantType.WARNING}>
Please be exhaustive, every detail counts!
</Alert>
<TextArea
name="bio"
label="Biography"
text="Enter a detailed biography"
fullWidth={true}
rows={8}
/> />
</RadioGroup> <RadioGroup>
<Switch name="is_public" label="Make this character public" /> <Radio
label="Appeared between 0 and 5 times"
<div> name="occurences"
<h4>Add pictures</h4> value="A"
<div className="mt-s">
<FileUploader
fullWidth={true}
text="JPG, PNG or GIF - Max file size 2MB"
multiple={true}
/> />
<Radio
label="Appeared between 6 and 10 times"
name="occurences"
value="B"
/>
<Radio
label="Appeared between 10 and 20 times"
name="occurences"
value="C"
/>
<Radio
label="Appeared between 20+ times"
name="occurences"
value="D"
/>
</RadioGroup>
<Switch name="is_public" label="Make this character public" />
<div>
<h4>Add pictures</h4>
<div className="mt-s">
<FileUploader
fullWidth={true}
text="JPG, PNG or GIF - Max file size 2MB"
multiple={true}
/>
</div>
</div> </div>
</div> </div>
</div> <div className="mt-l">
<div className="mt-l"> <Button fullWidth={true} type="submit">
<Button fullWidth={true} type="submit"> Add character
Add character </Button>
</Button> </div>
</div> </form>
</form> </Modal>
); );
}; };

View File

@@ -3,14 +3,15 @@ import {
Button, Button,
DataGrid, DataGrid,
SortModel, SortModel,
VariantType,
usePagination, usePagination,
useToastProvider, useToastProvider,
VariantType,
} from "@openfun/cunningham-react"; } from "@openfun/cunningham-react";
import { Page, PageProps } from "./App";
import { PageProps } from "./App";
import { database } from "./Character"; import { database } from "./Character";
export const Home = ({ changePage }: PageProps) => { export const Home = ({ modal }: { modal: PageProps }) => {
const { toast } = useToastProvider(); const { toast } = useToastProvider();
const [rowSelection, setRowSelection] = useState({}); const [rowSelection, setRowSelection] = useState({});
const [isLoading, setIsLoading] = useState(true); const [isLoading, setIsLoading] = useState(true);
@@ -21,6 +22,7 @@ export const Home = ({ changePage }: PageProps) => {
sort: "desc", sort: "desc",
}, },
]); ]);
const [rows, setRows] = useState<typeof database>([]); const [rows, setRows] = useState<typeof database>([]);
const [refresh, setRefresh] = useState(1); const [refresh, setRefresh] = useState(1);
@@ -49,7 +51,7 @@ export const Home = ({ changePage }: PageProps) => {
), ),
); );
setIsLoading(false); setIsLoading(false);
}, [pagination.page, sortModel, refresh]); }, [pagination.page, sortModel, modal.isOpen, refresh]);
return ( return (
<div className="page__home"> <div className="page__home">
@@ -70,7 +72,7 @@ export const Home = ({ changePage }: PageProps) => {
<Button <Button
color="primary" color="primary"
icon={<span className="material-icons">movie</span>} icon={<span className="material-icons">movie</span>}
onClick={() => changePage(Page.CREATE)} onClick={modal.open}
> >
Add character Add character
</Button> </Button>

View File

@@ -19,7 +19,6 @@ p {
// App // App
html { html {
min-height: 100vh; min-height: 100vh;
padding-bottom: 5rem;
background: linear-gradient(153deg, var(--c--theme--colors--greyscale-100) 0%, var(--c--theme--colors--greyscale-000) 100%); background: linear-gradient(153deg, var(--c--theme--colors--greyscale-100) 0%, var(--c--theme--colors--greyscale-000) 100%);
} }
@@ -95,6 +94,10 @@ html[data-theme="blueney"] {
// Create // Create
.page__create { .page__create {
max-width: 960px;
margin: 0 auto;
padding-bottom: 5rem;
h1 { h1 {
text-align: center; text-align: center;
margin: 4rem 0; margin: 4rem 0;
@@ -104,6 +107,7 @@ html[data-theme="blueney"] {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: 1.5rem; gap: 1.5rem;
text-align: left;
h3, h4 { h3, h4 {
margin: 0; margin: 0;