From 68999dc5c3048ed9eea9fa8ebd42268831e04932 Mon Sep 17 00:00:00 2001 From: Marlon Baeten Date: Fri, 24 May 2024 21:45:10 +0200 Subject: [PATCH] Split components --- src/App.tsx | 72 +++++-------------------------------------- src/Item.tsx | 51 ++++++++++++++++++++++++++++++ src/Table.tsx | 28 +++++++++++++++++ src/Theme.tsx | 50 ++++++++++++++++++++++++++++++ src/index.css | 24 ++++++++++++++- src/usePersistence.ts | 44 ++++++++++++++++++++++++++ 6 files changed, 204 insertions(+), 65 deletions(-) create mode 100644 src/Item.tsx create mode 100644 src/Table.tsx create mode 100644 src/Theme.tsx create mode 100644 src/usePersistence.ts diff --git a/src/App.tsx b/src/App.tsx index e5fa979..4538fbb 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,5 +1,6 @@ -import { Fragment } from 'react/jsx-runtime'; import rawData from './data.json'; +import Table from './Table'; +import usePersistence, { PersistenceContext } from './usePersistence'; const data = rawData as unknown as { [theme: string]: { @@ -16,9 +17,11 @@ const levels = [ 'meermaals zelfstandig uitgevoerd', ]; -function App() { +export default function App(): JSX.Element { + const persistence = usePersistence(); + return ( -
+

Ervaringskompas

Welke ervaring heb je opgedaan met deze beroepsactiviteit in de praktijk @@ -32,66 +35,7 @@ function App() { ))}

- - - - - {levels.map((level, levelIndex) => ( - - ))} - - - - {Object.entries(data).map(([theme, { color, items }], themeIndex) => ( - - - - - - {items.map((item, index) => ( - - - - {levels - .map((level, levelIndex) => ({ - level, - key: `option-${index}-${levelIndex}`, - })) - .map(({ level, key }, levelIndex) => ( - - ))} - - ))} - - ))} - -
{levelIndex + 1}
-

- {themeIndex + 1} - {theme} -

-
- - - {item} - - -
- + + ); } - -export default App; diff --git a/src/Item.tsx b/src/Item.tsx new file mode 100644 index 0000000..4c20c86 --- /dev/null +++ b/src/Item.tsx @@ -0,0 +1,51 @@ +interface ItemProps { + name: string; + index: number; + color: string; + levels: string[]; + selected: number; + onSelect: (level: number) => void; +} + +export default function Item({ name, index, color, levels, selected, onSelect }: ItemProps): JSX.Element { + return ( + + + + {levels + .map((level, levelIndex) => ({ + level, + key: `option-${index}-${levelIndex}`, + })) + .map(({ level, key }, levelIndex) => ( + + ))} + + ); +} diff --git a/src/Table.tsx b/src/Table.tsx new file mode 100644 index 0000000..088c738 --- /dev/null +++ b/src/Table.tsx @@ -0,0 +1,28 @@ +import Theme from './Theme'; + +interface TableProps { + themes: [string, { color: string; items: string[] }][]; + levels: string[]; +} + +export default function Table({ levels, themes }: TableProps): JSX.Element { + return ( +
+ + + {name} + + +
+ + + + {levels.map((level, levelIndex) => ( + + ))} + + + + {themes + .map(([name, { color, items }], index) => ({ name, color, items, index })) + .map((theme) => ( + + ))} + +
{levelIndex + 1}
+ ); +} diff --git a/src/Theme.tsx b/src/Theme.tsx new file mode 100644 index 0000000..f05e714 --- /dev/null +++ b/src/Theme.tsx @@ -0,0 +1,50 @@ +import { Fragment, useContext } from 'react'; +import Item from './Item'; +import { PersistenceContext } from './usePersistence'; + +interface ThemeProps { + theme: { + name: string; + color: string; + items: string[]; + index: number; + }; + levels: string[]; +} + +export default function Theme({ + theme: { name, color, items, index: themeIndex }, + levels, +}: ThemeProps): JSX.Element { + const { getLevel, select } = useContext(PersistenceContext); + + return ( + + + +

+ {themeIndex + 1} + {name} +

+ + + {items.map((item, index) => ( + select(themeIndex, index, level)} + /> + ))} +
+ ); +} diff --git a/src/index.css b/src/index.css index 994364c..b5b01a1 100644 --- a/src/index.css +++ b/src/index.css @@ -3,6 +3,7 @@ html { box-sizing: border-box; font-size: 16px; + min-width: 470px; } *, *:before, *:after { @@ -33,6 +34,12 @@ body { padding: 2rem; } +@media screen and (max-width: 768px) { + body { + padding: 1rem; + } +} + h1 { font-size: 2rem; margin-bottom: 1rem; @@ -57,6 +64,10 @@ ol { background-color: #fafafa; } +.legenda h2 { + margin-bottom: 0.5rem; +} + /* table styling */ table { @@ -71,13 +82,19 @@ tbody th { padding: 0.5rem; } +th.theme { + border: none; + border-bottom: 1px solid #000; +} + tbody td { width: 50px; } td, th { - border: 1px solid #eaeaea; + border: 1px solid #f0f0f0; vertical-align: middle; + min-width: 2rem; } tbody td label { @@ -88,6 +105,10 @@ tbody td label { cursor: pointer; } +table h3 { + margin-top: 1rem; +} + table h3 span { display: inline-block; width: 2rem; @@ -96,6 +117,7 @@ table h3 span { text-align: center; border-radius: 1rem; margin-right: 1rem; + color: #fff; } tbody input { diff --git a/src/usePersistence.ts b/src/usePersistence.ts new file mode 100644 index 0000000..d66144c --- /dev/null +++ b/src/usePersistence.ts @@ -0,0 +1,44 @@ +import { createContext, useEffect, useReducer } from 'react'; + +type Action = { type: 'select'; theme: number; item: number; level: number }; + +type State = { [key: string]: number }; + +function reducer(state: State, action: Action) { + switch (action.type) { + case 'select': + return { ...state, [`${action.theme}-${action.item}`]: action.level }; + default: + throw new Error(); + } +} + +interface PersistenceState { + getLevel: (theme: number, item: number) => number; + select: (theme: number, item: number, level: number) => void; +} + +function initialState(): State { + const localState = window.localStorage.getItem('persistence'); + + return localState ? JSON.parse(localState) : {}; +} + +export const PersistenceContext = createContext({ + getLevel: () => 0, + select: () => {}, +}); + +export default function usePersistence(): PersistenceState { + const [state, dispatch] = useReducer(reducer, [], initialState); + + useEffect(() => { + window.localStorage.setItem('persistence', JSON.stringify(state)); + }, [state]); + + return { + getLevel: (theme: number, item: number) => state[`${theme}-${item}`], + select: (theme: number, item: number, level: number) => + dispatch({ type: 'select', theme, item, level }), + }; +}