From abc4c7054f0becb1db2583a2f2d195a41aaa5f46 Mon Sep 17 00:00:00 2001 From: Frederic Heem Date: Sun, 17 Nov 2024 23:12:41 -0300 Subject: [PATCH] multi step form wip --- .../e-commerce-product-page/src/style.css | 4 - examples/multi-step-form/.gitignore | 24 +++ examples/multi-step-form/.npmrc | 2 + examples/multi-step-form/README.md | 23 +++ examples/multi-step-form/index.html | 17 +++ examples/multi-step-form/package.json | 20 +++ .../assets/images/bg-sidebar-desktop.svg | 1 + .../assets/images/bg-sidebar-mobile.svg | 1 + .../public/assets/images/favicon-32x32.png | Bin 0 -> 1063 bytes .../public/assets/images/icon-advanced.svg | 1 + .../public/assets/images/icon-arcade.svg | 1 + .../public/assets/images/icon-checkmark.svg | 1 + .../public/assets/images/icon-pro.svg | 1 + .../public/assets/images/icon-thank-you.svg | 1 + examples/multi-step-form/src/main.ts | 20 +++ examples/multi-step-form/src/multiStepForm.ts | 140 ++++++++++++++++++ examples/multi-step-form/src/stepAddOns.ts | 18 +++ .../multi-step-form/src/stepSelectPlan.ts | 60 ++++++++ examples/multi-step-form/src/stepSummary.ts | 14 ++ examples/multi-step-form/src/stepYourInfo.ts | 32 ++++ examples/multi-step-form/src/style.css | 57 +++++++ examples/multi-step-form/src/vite-env.d.ts | 1 + examples/multi-step-form/tsconfig.json | 23 +++ examples/multi-step-form/vite.config.js | 9 ++ 24 files changed, 467 insertions(+), 4 deletions(-) create mode 100644 examples/multi-step-form/.gitignore create mode 100644 examples/multi-step-form/.npmrc create mode 100644 examples/multi-step-form/README.md create mode 100644 examples/multi-step-form/index.html create mode 100644 examples/multi-step-form/package.json create mode 100644 examples/multi-step-form/public/assets/images/bg-sidebar-desktop.svg create mode 100644 examples/multi-step-form/public/assets/images/bg-sidebar-mobile.svg create mode 100644 examples/multi-step-form/public/assets/images/favicon-32x32.png create mode 100644 examples/multi-step-form/public/assets/images/icon-advanced.svg create mode 100644 examples/multi-step-form/public/assets/images/icon-arcade.svg create mode 100644 examples/multi-step-form/public/assets/images/icon-checkmark.svg create mode 100644 examples/multi-step-form/public/assets/images/icon-pro.svg create mode 100644 examples/multi-step-form/public/assets/images/icon-thank-you.svg create mode 100644 examples/multi-step-form/src/main.ts create mode 100644 examples/multi-step-form/src/multiStepForm.ts create mode 100644 examples/multi-step-form/src/stepAddOns.ts create mode 100644 examples/multi-step-form/src/stepSelectPlan.ts create mode 100644 examples/multi-step-form/src/stepSummary.ts create mode 100644 examples/multi-step-form/src/stepYourInfo.ts create mode 100644 examples/multi-step-form/src/style.css create mode 100644 examples/multi-step-form/src/vite-env.d.ts create mode 100644 examples/multi-step-form/tsconfig.json create mode 100644 examples/multi-step-form/vite.config.js diff --git a/examples/e-commerce-product-page/src/style.css b/examples/e-commerce-product-page/src/style.css index 2ca615e4..99573ed4 100644 --- a/examples/e-commerce-product-page/src/style.css +++ b/examples/e-commerce-product-page/src/style.css @@ -20,10 +20,6 @@ --color-gray-50: hsl(0, 0%, 95%); } -html[data-theme="dark"] { - --background-color: #202c37; -} - body { font-family: "Kumbh Sans", sans-serif; max-width: 100vw; diff --git a/examples/multi-step-form/.gitignore b/examples/multi-step-form/.gitignore new file mode 100644 index 00000000..a547bf36 --- /dev/null +++ b/examples/multi-step-form/.gitignore @@ -0,0 +1,24 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +dist-ssr +*.local + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? diff --git a/examples/multi-step-form/.npmrc b/examples/multi-step-form/.npmrc new file mode 100644 index 00000000..6b5f38e8 --- /dev/null +++ b/examples/multi-step-form/.npmrc @@ -0,0 +1,2 @@ +save-exact = true +package-lock = false diff --git a/examples/multi-step-form/README.md b/examples/multi-step-form/README.md new file mode 100644 index 00000000..9b0d24ae --- /dev/null +++ b/examples/multi-step-form/README.md @@ -0,0 +1,23 @@ +# Frontend Mentor Multi Step Form + +Here is the implementation in [Bau.js](https://github.com/grucloud/bau) of the [Frontend Mentor Multi Step Form code challenge](https://www.frontendmentor.io/challenges/multistep-form-YVAnSdqQBJ) + +## Workflow + +Install the dependencies: + +```sh +npm install +``` + +Start a development server: + +```sh +npm run dev +``` + +Build a production version: + +```sh +npm run build +``` diff --git a/examples/multi-step-form/index.html b/examples/multi-step-form/index.html new file mode 100644 index 00000000..6d4b66e3 --- /dev/null +++ b/examples/multi-step-form/index.html @@ -0,0 +1,17 @@ + + + + + + + Multi Step Form | FrontendMentor + + +
+ + + diff --git a/examples/multi-step-form/package.json b/examples/multi-step-form/package.json new file mode 100644 index 00000000..7f684b90 --- /dev/null +++ b/examples/multi-step-form/package.json @@ -0,0 +1,20 @@ +{ + "name": "frontendmentor-multi-step-form", + "private": true, + "version": "0.90.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "tsc && vite build", + "preview": "vite preview" + }, + "devDependencies": { + "typescript": "^5.0.2", + "vite": "^5.2.11" + }, + "dependencies": { + "@grucloud/bau": "^0.90.0", + "@grucloud/bau-css": "^0.90.0", + "@grucloud/bau-ui": "^0.90.0" + } +} diff --git a/examples/multi-step-form/public/assets/images/bg-sidebar-desktop.svg b/examples/multi-step-form/public/assets/images/bg-sidebar-desktop.svg new file mode 100644 index 00000000..d9153d9f --- /dev/null +++ b/examples/multi-step-form/public/assets/images/bg-sidebar-desktop.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/examples/multi-step-form/public/assets/images/bg-sidebar-mobile.svg b/examples/multi-step-form/public/assets/images/bg-sidebar-mobile.svg new file mode 100644 index 00000000..0097d56a --- /dev/null +++ b/examples/multi-step-form/public/assets/images/bg-sidebar-mobile.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/examples/multi-step-form/public/assets/images/favicon-32x32.png b/examples/multi-step-form/public/assets/images/favicon-32x32.png new file mode 100644 index 0000000000000000000000000000000000000000..1e2df7f089f46dd930239e418bf13e8e4c1cca0f GIT binary patch literal 1063 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE3?yBabR7dyEa{HEjtmSN`?>!lvVtU&J%W50 z7^>757#dm_7=8hT8eT9klo~KFyh>nTu$sZZAYL$MSD+081EY0-Plzi}!G9Wnr(eF` z|M+F+!xzhMK3;zF(cgdn_B?v|`rD7+fB)V2@MY=EN9|{BEWPpQ{f}RtfBl|!^+Ea3 z%d2iZe)i@2x9`6{eg66V&!4ULpSPU4KJ)Uu^RM22`~BzO$wz&&PyPA(@7<4I`yRjQ zJAZr1#XB#){D-o5+r^Vjdc3)fsquh@R{^yA;Z|8C!Zw`1zDvzMNK{QUFr z^G~yvU&yK5nOnE3q5ts2d8Z#d`S9=G|MwriU%2{m%ii0owp@Sy>dXGuA$NemCsPvS z7YvLEt(>(tKmBDkpML+hROc(RL;oBPGFiO5x8~1ErS?rgb&N^g?k~Ih+L^ zk;M!Q+`=Ht$S`Y;1W=H@#M9T6{SlWiKeL>x)y)*39#2mf#}JFtt&`J}n+ycniko-@ z3;26vOWyCzeS7I$?C$UX{_kIKV}WAF+24(KRQ;Wbm=(hXgpO=b(eBb|byZ~P5M3dt zyDB-nDx`Zw9LoWN-GrY`kHxRfy}Q3KfBxRdclJCv_%N_mL-x0j+x*I+s%JIY&vgg3Y zTes?8j%DMbh5PocynFBNZJq}$w`Q+tKfe6=^y}tkkv_Zb+uQBmSHB|O-+X(*4%OrO zQF%8%OSj*z`t$7X`oHyz%ReTa40^8r7Z}c}C9V-ADTyViR>?)FK#IZ0z|cb1&_LJF zGQ`l-%D~vl*g)IB$jZQAv*1E06b-rgDVb@NxHTN|kz5AU01WSllAy$Lg@U5|w9K4T zg_6pGRE5lfl4J&kiaC!z@o*G|X=t4CKYhmYX%GXmGPhnbx3IFX_hb=fVFi~4lfx;@ u%9}$JPT#n4;>ejJGDp}?H+U@Y(qnifE?Dx($#g2v3I \ No newline at end of file diff --git a/examples/multi-step-form/public/assets/images/icon-arcade.svg b/examples/multi-step-form/public/assets/images/icon-arcade.svg new file mode 100644 index 00000000..5e062fe0 --- /dev/null +++ b/examples/multi-step-form/public/assets/images/icon-arcade.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/examples/multi-step-form/public/assets/images/icon-checkmark.svg b/examples/multi-step-form/public/assets/images/icon-checkmark.svg new file mode 100644 index 00000000..f6017b21 --- /dev/null +++ b/examples/multi-step-form/public/assets/images/icon-checkmark.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/examples/multi-step-form/public/assets/images/icon-pro.svg b/examples/multi-step-form/public/assets/images/icon-pro.svg new file mode 100644 index 00000000..57e438ac --- /dev/null +++ b/examples/multi-step-form/public/assets/images/icon-pro.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/examples/multi-step-form/public/assets/images/icon-thank-you.svg b/examples/multi-step-form/public/assets/images/icon-thank-you.svg new file mode 100644 index 00000000..5aea55de --- /dev/null +++ b/examples/multi-step-form/public/assets/images/icon-thank-you.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/examples/multi-step-form/src/main.ts b/examples/multi-step-form/src/main.ts new file mode 100644 index 00000000..a7a5a755 --- /dev/null +++ b/examples/multi-step-form/src/main.ts @@ -0,0 +1,20 @@ +import { createContext, type Context } from "@grucloud/bau-ui/context"; +import multiStepForm from "./multiStepForm"; + +import "./style.css"; + +const context = createContext(); + +const app = (context: Context) => { + const { bau } = context; + const { main } = bau.tags; + + const MultiStepForm = multiStepForm(context); + + return function () { + return main(MultiStepForm()); + }; +}; + +const App = app(context); +document.getElementById("app")?.replaceChildren(App()); diff --git a/examples/multi-step-form/src/multiStepForm.ts b/examples/multi-step-form/src/multiStepForm.ts new file mode 100644 index 00000000..41944135 --- /dev/null +++ b/examples/multi-step-form/src/multiStepForm.ts @@ -0,0 +1,140 @@ +import { type Context } from "@grucloud/bau-ui/context"; +import stepYourInfo from "./stepYourInfo"; +import stepSelectPlan from "./stepSelectPlan"; + +export default function (context: Context) { + const { bau, css } = context; + const { article, div, header, ul, li, span } = bau.tags; + const StepYourInfo = stepYourInfo(context); + const StepSelectPlan = stepSelectPlan(context); + + const currentStepState = bau.state(1); + + const className = css` + border: 1px solid red; + max-width: 1000px; + margin: auto; + display: flex; + justify-content: center; + align-items: stretch; + gap: 0.6rem; + border-radius: 0.6rem; + padding: 1rem; + + & header { + border: 1px solid blue; + background-image: url("./assets/images/bg-sidebar-desktop.svg"); + background-size: cover; + > ul { + display: flex; + flex-direction: column; + gap: 1rem; + padding: 1rem; + > li { + &.active { + color: red; + } + display: flex; + align-items: center; + gap: 1rem; + color: var(--font-color-inverse); + + .step-number { + font-weight: bold; + padding: 1rem; + border: 1px solid white; + border-radius: 100%; + width: 2rem; + height: 2rem; + display: grid; + place-content: center; + } + .step-label { + text-transform: uppercase; + font-size: smaller; + color: var(--font-color-inverse-secondary); + } + .label { + text-transform: uppercase; + font-weight: bold; + } + } + } + } + .content { + border: 1px solid blue; + min-width: 375px; + min-height: 400px; + > ul { + > li { + display: none; + &.active { + display: block; + } + form { + display: flex; + flex-direction: column; + gap: 1rem; + } + } + } + } + `; + const isCurrentIndex = (index: number) => currentStepState.val == index; + + const Header = ({ index, label }: any) => + li( + { class: () => isCurrentIndex(index) && "active" }, + div({ class: "step-number" }, index), + div( + div({ class: "step-label" }, "Step", index), + div({ class: "label" }, label) + ) + ); + + const next = () => { + currentStepState.val++; + }; + const previous = () => { + currentStepState.val--; + }; + + const onsubmitYourInfo = (event: HTMLFormElement) => { + event.preventDefault(); + const payload = Object.fromEntries(new FormData(event.currentTarget)); + next(); + }; + + const onsubmitAddon = (event) => { + event.preventDefault(); + const payload = Object.fromEntries(new FormData(event.currentTarget)); + alert(JSON.stringify(payload)); + // next(); + }; + + const steps = [ + { + label: "Your Info", + Content: () => StepYourInfo({ onsubmit: onsubmitYourInfo }), + }, + { + label: "Select Plan", + Content: ({}) => StepSelectPlan({ onsubmit: onsubmitAddon }), + }, + ]; + + return () => { + return article( + { class: className }, + header(ul(steps.map(({ label }, index) => Header({ index, label })))), + div( + { class: "content" }, + ul( + steps.map(({ Content, label }, index) => + li({ class: () => isCurrentIndex(index) && "active" }, Content({})) + ) + ) + ) + ); + }; +} diff --git a/examples/multi-step-form/src/stepAddOns.ts b/examples/multi-step-form/src/stepAddOns.ts new file mode 100644 index 00000000..681e37c5 --- /dev/null +++ b/examples/multi-step-form/src/stepAddOns.ts @@ -0,0 +1,18 @@ +import { type Context } from "@grucloud/bau-ui/context"; + +export default function (context: Context) { + const { bau, css } = context; + const { form, h1, button } = bau.tags; + + const className = css` + border: 1px solid red; + `; + + return ({ onsubmit }) => { + return form( + { class: className, onsubmit }, + h1("Add-Ons"), + button({ type: "submit" }, "Next") + ); + }; +} diff --git a/examples/multi-step-form/src/stepSelectPlan.ts b/examples/multi-step-form/src/stepSelectPlan.ts new file mode 100644 index 00000000..c6e6730d --- /dev/null +++ b/examples/multi-step-form/src/stepSelectPlan.ts @@ -0,0 +1,60 @@ +import { type Context } from "@grucloud/bau-ui/context"; + +export default function (context: Context) { + const { bau, css } = context; + const { + form, + button, + h1, + p, + input, + footer, + div, + label, + span, + strong, + small, + } = bau.tags; + + const className = css` + border: 1px solid red; + .radio-group { + display: flex; + justify-content: space-around; + > label { + border: 1px solid red; + padding: 1rem; + } + } + `; + + return ({ onsubmit }: any) => { + return form( + { class: className, onsubmit }, + h1("Select your plan"), + p("You have the option of monthly or yearly billing."), + div( + { class: "radio-group" }, + label( + input({ type: "radio", name: "plan", id: "arcade" }), + p(strong("Arcade")), + p(small("$9/month")) + ), + label( + input({ type: "radio", name: "plan", id: "advanced" }), + p(strong("Advanced")), + p(small("$12/month")) + ), + label( + input({ type: "radio", name: "plan", id: "pro" }), + p(strong("Pro")), + p(small("$15/month")) + ) + ), + footer( + button({ type: "button" }, "Go back"), + button({ type: "submit" }, "Next Step") + ) + ); + }; +} diff --git a/examples/multi-step-form/src/stepSummary.ts b/examples/multi-step-form/src/stepSummary.ts new file mode 100644 index 00000000..f4ed2d2c --- /dev/null +++ b/examples/multi-step-form/src/stepSummary.ts @@ -0,0 +1,14 @@ +import { type Context } from "@grucloud/bau-ui/context"; + +export default function (context: Context) { + const { bau, css } = context; + const { section, h1 } = bau.tags; + + const className = css` + border: 1px solid red; + `; + + return () => { + return section({ class: className }, h1("SSummary")); + }; +} diff --git a/examples/multi-step-form/src/stepYourInfo.ts b/examples/multi-step-form/src/stepYourInfo.ts new file mode 100644 index 00000000..0633e888 --- /dev/null +++ b/examples/multi-step-form/src/stepYourInfo.ts @@ -0,0 +1,32 @@ +import { type Context } from "@grucloud/bau-ui/context"; + +export default function (context: Context) { + const { bau, css } = context; + const { form, h1, input, label, button } = bau.tags; + + const className = css` + border: 1px solid red; + display: flex; + flex-direction: column; + `; + + return ({ onsubmit }) => { + return form( + { class: className, onsubmit }, + h1("Your Info"), + label( + "Name", + input({ name: "name", placeholder: "e.g Stephen King", required: true }) + ), + label( + "Email Address", + input({ type: "email", placeholder: "e.g stephenking@lorem.com" }) + ), + label( + "Phone Number", + input({ name: "phone", placeholder: "e.g. 1234567890" }) + ), + button({ type: "submit" }, "Next") + ); + }; +} diff --git a/examples/multi-step-form/src/style.css b/examples/multi-step-form/src/style.css new file mode 100644 index 00000000..5bdddd1a --- /dev/null +++ b/examples/multi-step-form/src/style.css @@ -0,0 +1,57 @@ +@import url("https://fonts.googleapis.com/css2?family=Ubuntu:wght@400;500;700&display=swap"); + +* { + margin: 0; + box-sizing: border-box; +} +/** +- Marine blue: hsl(213, 96%, 18%) +- Purplish blue: hsl(243, 100%, 62%) +- Pastel blue: hsl(228, 100%, 84%) +- Light blue: hsl(206, 94%, 87%) +- Strawberry red: hsl(354, 84%, 57%) + +### Neutral + +- Cool gray: hsl(231, 11%, 63%) +- Light gray: hsl(229, 24%, 87%) +- Magnolia: hsl(217, 100%, 97%) +- Alabaster: hsl(231, 100%, 99%) +- White: hsl(0, 0%, 100%) + +*/ +:root { + --color-primary-h: 26; + --color-primary-base-s: 100%; + --color-primary-l: 55%; + + --color-neutral-h: 219; + --color-neutral-base-s: 9%; + --color-neutral-l: 45%; + + --background-color: #fafafa; + --font-size: 1rem; + --font-color-secondary: #68707d; +} + +body { + font-family: "Ubuntu", sans-serif; + max-width: 100vw; + background-color: var(--background-color); + font-size: var(--font-size); +} +main { + display: grid; + align-items: center; + min-height: 100vh; +} + +ul { + list-style: none; +} +button { + cursor: pointer; + /* background: none; + border: none; + border-radius: 1rem; */ +} diff --git a/examples/multi-step-form/src/vite-env.d.ts b/examples/multi-step-form/src/vite-env.d.ts new file mode 100644 index 00000000..11f02fe2 --- /dev/null +++ b/examples/multi-step-form/src/vite-env.d.ts @@ -0,0 +1 @@ +/// diff --git a/examples/multi-step-form/tsconfig.json b/examples/multi-step-form/tsconfig.json new file mode 100644 index 00000000..75abdef2 --- /dev/null +++ b/examples/multi-step-form/tsconfig.json @@ -0,0 +1,23 @@ +{ + "compilerOptions": { + "target": "ES2020", + "useDefineForClassFields": true, + "module": "ESNext", + "lib": ["ES2020", "DOM", "DOM.Iterable"], + "skipLibCheck": true, + + /* Bundler mode */ + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "resolveJsonModule": true, + "isolatedModules": true, + "noEmit": true, + + /* Linting */ + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true + }, + "include": ["src"] +} diff --git a/examples/multi-step-form/vite.config.js b/examples/multi-step-form/vite.config.js new file mode 100644 index 00000000..41713bec --- /dev/null +++ b/examples/multi-step-form/vite.config.js @@ -0,0 +1,9 @@ +import { defineConfig } from "vite"; + +export default defineConfig(({ command, mode, ssrBuild }) => { + return { + server: { + open: true, + }, + }; +});