diff --git a/examples/result-summary-component/.gitignore b/examples/result-summary-component/.gitignore new file mode 100644 index 00000000..a547bf36 --- /dev/null +++ b/examples/result-summary-component/.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/result-summary-component/.npmrc b/examples/result-summary-component/.npmrc new file mode 100644 index 00000000..6b5f38e8 --- /dev/null +++ b/examples/result-summary-component/.npmrc @@ -0,0 +1,2 @@ +save-exact = true +package-lock = false diff --git a/examples/result-summary-component/README.md b/examples/result-summary-component/README.md new file mode 100644 index 00000000..a3b1ad73 --- /dev/null +++ b/examples/result-summary-component/README.md @@ -0,0 +1,23 @@ +# Frontend Mentor Result Summary Component + +Here is the implementation in [Bau.js](https://github.com/grucloud/bau) of the [Frontend Mentor Result Summary Component code challenge](https://www.frontendmentor.io/challenges/results-summary-component-CE_K6s0maV) + +## 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/result-summary-component/index.html b/examples/result-summary-component/index.html new file mode 100644 index 00000000..6fbe9613 --- /dev/null +++ b/examples/result-summary-component/index.html @@ -0,0 +1,17 @@ + + + + + + + Result Summary Component | FrontendMentor + + +
+ + + diff --git a/examples/result-summary-component/package.json b/examples/result-summary-component/package.json new file mode 100644 index 00000000..a16914dc --- /dev/null +++ b/examples/result-summary-component/package.json @@ -0,0 +1,20 @@ +{ + "name": "frontendmentor-result-summary-component", + "private": true, + "version": "0.85.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.85.0", + "@grucloud/bau-css": "^0.85.0", + "@grucloud/bau-ui": "^0.85.0" + } +} diff --git a/examples/result-summary-component/public/assets/images/favicon-32x32.png b/examples/result-summary-component/public/assets/images/favicon-32x32.png new file mode 100644 index 00000000..1e2df7f0 Binary files /dev/null and b/examples/result-summary-component/public/assets/images/favicon-32x32.png differ diff --git a/examples/result-summary-component/public/assets/images/icon-memory.svg b/examples/result-summary-component/public/assets/images/icon-memory.svg new file mode 100644 index 00000000..8e110660 --- /dev/null +++ b/examples/result-summary-component/public/assets/images/icon-memory.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/examples/result-summary-component/public/assets/images/icon-reaction.svg b/examples/result-summary-component/public/assets/images/icon-reaction.svg new file mode 100644 index 00000000..4317d212 --- /dev/null +++ b/examples/result-summary-component/public/assets/images/icon-reaction.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/examples/result-summary-component/public/assets/images/icon-verbal.svg b/examples/result-summary-component/public/assets/images/icon-verbal.svg new file mode 100644 index 00000000..c975df5d --- /dev/null +++ b/examples/result-summary-component/public/assets/images/icon-verbal.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/examples/result-summary-component/public/assets/images/icon-visual.svg b/examples/result-summary-component/public/assets/images/icon-visual.svg new file mode 100644 index 00000000..b1d2a564 --- /dev/null +++ b/examples/result-summary-component/public/assets/images/icon-visual.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/examples/result-summary-component/src/data.json b/examples/result-summary-component/src/data.json new file mode 100644 index 00000000..257b468d --- /dev/null +++ b/examples/result-summary-component/src/data.json @@ -0,0 +1,22 @@ +[ + { + "category": "Reaction", + "score": 80, + "icon": "./assets/images/icon-reaction.svg" + }, + { + "category": "Memory", + "score": 92, + "icon": "./assets/images/icon-memory.svg" + }, + { + "category": "Verbal", + "score": 61, + "icon": "./assets/images/icon-verbal.svg" + }, + { + "category": "Visual", + "score": 72, + "icon": "./assets/images/icon-visual.svg" + } +] diff --git a/examples/result-summary-component/src/main.ts b/examples/result-summary-component/src/main.ts new file mode 100644 index 00000000..e621ffb2 --- /dev/null +++ b/examples/result-summary-component/src/main.ts @@ -0,0 +1,20 @@ +import { createContext, type Context } from "@grucloud/bau-ui/context"; +import resultSummary from "./resultSummary"; + +import "./style.css"; + +const context = createContext(); + +const app = (context: Context) => { + const { bau } = context; + const { main } = bau.tags; + + const ResultSummary = resultSummary(context); + + return function () { + return main(ResultSummary()); + }; +}; + +const App = app(context); +document.getElementById("app")?.replaceChildren(App()); diff --git a/examples/result-summary-component/src/resultSummary.ts b/examples/result-summary-component/src/resultSummary.ts new file mode 100644 index 00000000..d3bdb282 --- /dev/null +++ b/examples/result-summary-component/src/resultSummary.ts @@ -0,0 +1,195 @@ +import { type Context } from "@grucloud/bau-ui/context"; +import data from "./data.json"; + +const score = 76; + +export default function (context: Context) { + const { bau, css } = context; + const { h1, h2, div, p, article, section, button, ul, li, span, img } = + bau.tags; + const className = css` + max-width: 500px; + margin: auto; + display: grid; + grid-template-columns: minmax(auto, 350px) minmax(auto, 350px); + grid-template-rows: 1fr; + + @media (max-width: 500px) { + grid-template-columns: 1fr; + + border-radius: 0; + } + border-radius: 0.6rem; + box-shadow: 0 0.5rem 2rem 0 rgba(0, 0, 0, 0.1); + section { + display: flex; + flex-direction: column; + padding: 1rem; + gap: 0.7rem; + &.result { + align-items: center; + border-radius: 0.6rem; + @media (max-width: 600px) { + flex-direction: column; + border-radius: 0 0 0.6rem 0.6rem; + } + background: linear-gradient( + var(--Light-slate-blue), + var(--Light-royal-blue) + ); + + min-width: 250px; + + h1 { + color: var(--color); + } + h2 { + color: var(--white); + } + p { + color: var(--color); + text-align: center; + padding: 0 1rem; + } + .score-container { + color: var(--white); + background: linear-gradient(var(--Violet-blue), var(--Persian-blue)); + display: flex; + flex-direction: column; + align-items: center; + border-radius: 50%; + aspect-ratio: 1; + padding: 1.5rem; + .score { + color: var(--white); + font-size: 3rem; + font-weight: bold; + line-height: 3rem; + } + .score-total { + font-size: 0.7rem; + color: var(--color); + } + } + } + &.summary { + min-width: 250px; + button { + width: 100%; + font-weight: 500; + font-size: 1rem; + padding: 0.6rem 0; + color: white; + border-radius: 1rem; + background-color: var(--btn-bg); + border: none; + outline: none; + cursor: pointer; + &:hover { + background: linear-gradient( + var(--Light-slate-blue), + var(--Light-royal-blue) + ); + } + } + } + ul { + padding: 0; + flex-grow: 1; + li { + list-style: none; + padding: 0.7rem 0.5rem; + margin: 0.5rem 0rem; + border-radius: 0.3rem; + display: flex; + justify-content: space-between; + align-items: center; + + &:nth-child(1) { + color: hsl(0, 100%, 67%); + background: hsla(0, 100%, 67%, 0.05); + } + &:nth-child(2) { + color: hsl(39, 100%, 56%); + background: hsla(39, 100%, 56%, 0.05); + } + &:nth-child(3) { + color: hsl(166, 100%, 37%); + background: hsla(166, 100%, 37%, 0.05); + } + &:nth-child(4) { + color: hsl(234, 85%, 45%); + background: hsla(234, 85%, 45%, 0.05); + } + .category { + display: flex; + align-items: center; + gap: 0.4rem; + } + .category { + font-weight: 700; + } + .score { + color: hsl(224, 30%, 27%); + font-weight: 700; + padding-right: 0.3rem; + } + .score-total { + color: hsla(224, 30%, 27%, 0.5); + } + } + } + } + `; + + const scoreState = bau.state(score); + + var count = 0; + var interval = setInterval(() => { + count++; + scoreState.val = count; + if (count >= score) { + clearInterval(interval); + } + }, 5); + + return function resultSummary() { + return article( + { class: className }, + section( + { class: "result" }, + h1("Result"), + + div( + { class: "score-container" }, + div({ class: "score" }, scoreState), + div({ class: "score-total" }, "of 100") + ), + h2("Great"), + p( + "You scored higher than 65% of the people who have taken these tests." + ) + ), + section( + { class: "summary" }, + h1("Summary"), + ul( + data.map(({ category, score, icon }) => + li( + div( + { class: "category" }, + img({ src: icon }), + span({ class: "category" }, category) + ), + div( + span({ class: "score" }, score), + span({ class: "score-total" }, "/100") + ) + ) + ) + ), + button("Continue") + ) + ); + }; +} diff --git a/examples/result-summary-component/src/style.css b/examples/result-summary-component/src/style.css new file mode 100644 index 00000000..b3241a20 --- /dev/null +++ b/examples/result-summary-component/src/style.css @@ -0,0 +1,28 @@ +@import url("https://fonts.googleapis.com/css2?family=Hanken+Grotesk:wght@500;700;800&display=swap"); + +* { + margin: 0; + box-sizing: border-box; +} + +:root { + --Light-slate-blue: hsl(252, 100%, 67%); + --Light-royal-blue: hsl(241, 81%, 54%); + --Violet-blue: hsla(256, 72%, 46%, 1); + --Persian-blue: hsla(241, 72%, 46%, 0); + --white: hsl(0, 0%, 100%); + --Pale-blue: hsl(221, 100%, 96%); + --color: hsl(241, 100%, 89%); + --btn-bg: hsl(224, 30%, 27%); +} + +body { + font-family: "Hanken Grotesk", sans-serif; + min-height: 100vh; + display: grid; + place-items: center; + + @media (max-width: 600px) { + place-items: flex-start; + } +} diff --git a/examples/result-summary-component/src/vite-env.d.ts b/examples/result-summary-component/src/vite-env.d.ts new file mode 100644 index 00000000..11f02fe2 --- /dev/null +++ b/examples/result-summary-component/src/vite-env.d.ts @@ -0,0 +1 @@ +/// diff --git a/examples/result-summary-component/tsconfig.json b/examples/result-summary-component/tsconfig.json new file mode 100644 index 00000000..75abdef2 --- /dev/null +++ b/examples/result-summary-component/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/result-summary-component/vite.config.js b/examples/result-summary-component/vite.config.js new file mode 100644 index 00000000..41713bec --- /dev/null +++ b/examples/result-summary-component/vite.config.js @@ -0,0 +1,9 @@ +import { defineConfig } from "vite"; + +export default defineConfig(({ command, mode, ssrBuild }) => { + return { + server: { + open: true, + }, + }; +});