Added basic application design with material design
This commit is contained in:
parent
60724dfa60
commit
a9ecf32a56
36
src/App.tsx
36
src/App.tsx
@ -1,3 +1,35 @@
|
|||||||
import React from "react";
|
import React, { useMemo } from "react";
|
||||||
|
import CssBaseline from "@mui/material/CssBaseline";
|
||||||
|
import useMediaQuery from "@mui/material/useMediaQuery";
|
||||||
|
import { createTheme, ThemeProvider } from "@mui/material/styles";
|
||||||
|
|
||||||
export const App = () => <div>Hello, world</div>;
|
import { Layout } from "./Layout";
|
||||||
|
import { TodoList } from "./TodoList";
|
||||||
|
import { AppBar } from "./AppBar";
|
||||||
|
import { TaskItemT } from "./types";
|
||||||
|
|
||||||
|
const tasks: TaskItemT[] = [
|
||||||
|
{ text: "test", done: false, id: 0 },
|
||||||
|
{ text: "Long, a bit tooooooo0000000000oooooo long test", done: true, id: 1 },
|
||||||
|
];
|
||||||
|
|
||||||
|
export const App = () => {
|
||||||
|
const prefersDarkMode = useMediaQuery("(prefers-color-scheme: dark)");
|
||||||
|
|
||||||
|
const theme = useMemo(
|
||||||
|
() =>
|
||||||
|
createTheme({ palette: { mode: prefersDarkMode ? "dark" : "light" } }),
|
||||||
|
[prefersDarkMode]
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ThemeProvider theme={theme}>
|
||||||
|
<CssBaseline />
|
||||||
|
<Layout
|
||||||
|
appBar={<AppBar />}
|
||||||
|
title="My tasks"
|
||||||
|
content={<TodoList tasks={tasks} />}
|
||||||
|
/>
|
||||||
|
</ThemeProvider>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
23
src/AppBar.tsx
Normal file
23
src/AppBar.tsx
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
import { Fab, Box } from "@mui/material";
|
||||||
|
import Add from "@mui/icons-material/Add";
|
||||||
|
import React from "react";
|
||||||
|
|
||||||
|
/* TODO: create component for task adding */
|
||||||
|
|
||||||
|
export const AppBar: React.FC = () => (
|
||||||
|
<Box sx={{ width: "100%", justifyContent: "center", display: "flex" }}>
|
||||||
|
<Fab
|
||||||
|
variant="extended"
|
||||||
|
color="primary"
|
||||||
|
aria-label="add"
|
||||||
|
sx={{
|
||||||
|
position: "relative",
|
||||||
|
zIndex: 1,
|
||||||
|
top: -30,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Add sx={{ marginRight: 1 }} />
|
||||||
|
Add a new task
|
||||||
|
</Fab>
|
||||||
|
</Box>
|
||||||
|
);
|
27
src/Layout.tsx
Normal file
27
src/Layout.tsx
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
import { AppBar, Box, Toolbar, Typography, Checkbox } from "@mui/material";
|
||||||
|
import React, { ReactNode } from "react";
|
||||||
|
|
||||||
|
export type LayoutProps = {
|
||||||
|
appBar: ReactNode;
|
||||||
|
content: ReactNode;
|
||||||
|
title: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const Layout: React.FC<LayoutProps> = ({ appBar, content, title }) => (
|
||||||
|
<>
|
||||||
|
<Box sx={{ padding: (theme) => theme.spacing(2, 2, 10, 2) }}>
|
||||||
|
<Typography variant="h4" sx={{ paddingLeft: 2, marginBottom: 2 }}>
|
||||||
|
<Checkbox sx={{ visibility: "hidden" }} />
|
||||||
|
{title}
|
||||||
|
</Typography>
|
||||||
|
{content}
|
||||||
|
</Box>
|
||||||
|
<AppBar
|
||||||
|
position="fixed"
|
||||||
|
color="transparent"
|
||||||
|
sx={{ top: "auto", bottom: 0 }}
|
||||||
|
>
|
||||||
|
<Toolbar>{appBar}</Toolbar>
|
||||||
|
</AppBar>
|
||||||
|
</>
|
||||||
|
);
|
41
src/TodoItem.tsx
Normal file
41
src/TodoItem.tsx
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
import {
|
||||||
|
Checkbox,
|
||||||
|
IconButton,
|
||||||
|
ListItem,
|
||||||
|
ListItemSecondaryAction,
|
||||||
|
ListItemText,
|
||||||
|
TextField,
|
||||||
|
} from "@mui/material";
|
||||||
|
import DeleteOutlined from "@mui/icons-material/DeleteOutlined";
|
||||||
|
import React, { useState } from "react";
|
||||||
|
import { TaskItemT } from "./types";
|
||||||
|
|
||||||
|
export type TodoItemProps = { task: TaskItemT };
|
||||||
|
|
||||||
|
export const TodoItem: React.FC<TodoItemProps> = ({ task }) => {
|
||||||
|
const { done, text } = task;
|
||||||
|
|
||||||
|
const [editing, setEditing] = useState(false);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ListItem>
|
||||||
|
<Checkbox checked={done} />
|
||||||
|
{editing ? (
|
||||||
|
<TextField
|
||||||
|
fullWidth
|
||||||
|
variant="standard"
|
||||||
|
value={text}
|
||||||
|
autoFocus={true}
|
||||||
|
onBlur={() => setEditing(false)}
|
||||||
|
></TextField>
|
||||||
|
) : (
|
||||||
|
<ListItemText onClick={() => setEditing(true)} primary={text} />
|
||||||
|
)}
|
||||||
|
<ListItemSecondaryAction>
|
||||||
|
<IconButton aria-label="Delete Todo">
|
||||||
|
<DeleteOutlined />
|
||||||
|
</IconButton>
|
||||||
|
</ListItemSecondaryAction>
|
||||||
|
</ListItem>
|
||||||
|
);
|
||||||
|
};
|
20
src/TodoList.tsx
Normal file
20
src/TodoList.tsx
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
import { List, Paper } from "@mui/material";
|
||||||
|
import React from "react";
|
||||||
|
import { TodoItem } from "./TodoItem";
|
||||||
|
import { TaskItemT } from "./types";
|
||||||
|
|
||||||
|
export type TodoListProps = {
|
||||||
|
tasks: TaskItemT[];
|
||||||
|
};
|
||||||
|
|
||||||
|
export const TodoList: React.FC<TodoListProps> = ({ tasks }) => {
|
||||||
|
return (
|
||||||
|
<Paper variant="outlined">
|
||||||
|
<List>
|
||||||
|
{tasks.map((task) => (
|
||||||
|
<TodoItem key={task.id} task={task} />
|
||||||
|
))}
|
||||||
|
</List>
|
||||||
|
</Paper>
|
||||||
|
);
|
||||||
|
};
|
20
src/hooks.ts
Normal file
20
src/hooks.ts
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
import { ChangeEventHandler, useState } from "react";
|
||||||
|
|
||||||
|
export type UseInputValueReturnT = {
|
||||||
|
onChange: ChangeEventHandler<HTMLInputElement>;
|
||||||
|
submit: () => void;
|
||||||
|
value: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useInputValue = (
|
||||||
|
initialValue: string = "",
|
||||||
|
onSubmit: (value: string) => void
|
||||||
|
): UseInputValueReturnT => {
|
||||||
|
const [value, setValue] = useState(initialValue);
|
||||||
|
|
||||||
|
return {
|
||||||
|
onChange: (e) => setValue(e.currentTarget.value),
|
||||||
|
submit: () => onSubmit(value),
|
||||||
|
value,
|
||||||
|
};
|
||||||
|
};
|
@ -1,25 +0,0 @@
|
|||||||
* {
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
-ms-overflow-style: none;
|
|
||||||
scrollbar-width: none;
|
|
||||||
box-sizing: border-box;
|
|
||||||
outline: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
*::-webkit-scrollbar {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
body {
|
|
||||||
font-family: Inter, system-ui, sans-serif;
|
|
||||||
-webkit-font-smoothing: antialiased;
|
|
||||||
-moz-osx-font-smoothing: grayscale;
|
|
||||||
background: rgba(54, 54, 69, 0.05) none repeat scroll 0% 0%;
|
|
||||||
color: rgb(54, 54, 69);
|
|
||||||
}
|
|
||||||
|
|
||||||
code {
|
|
||||||
font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New",
|
|
||||||
monospace;
|
|
||||||
}
|
|
@ -1,8 +1,6 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import ReactDOM from "react-dom";
|
import ReactDOM from "react-dom";
|
||||||
|
|
||||||
import './index.css'
|
|
||||||
|
|
||||||
import { App } from "./App";
|
import { App } from "./App";
|
||||||
|
|
||||||
ReactDOM.render(
|
ReactDOM.render(
|
||||||
@ -11,7 +9,3 @@ ReactDOM.render(
|
|||||||
</React.StrictMode>,
|
</React.StrictMode>,
|
||||||
document.getElementById("root")
|
document.getElementById("root")
|
||||||
);
|
);
|
||||||
|
|
||||||
if (import.meta.hot) {
|
|
||||||
import.meta.hot.accept();
|
|
||||||
}
|
|
||||||
|
5
src/types.ts
Normal file
5
src/types.ts
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
export type TaskItemT = {
|
||||||
|
id: number;
|
||||||
|
text: string;
|
||||||
|
done: boolean;
|
||||||
|
};
|
Loading…
x
Reference in New Issue
Block a user