Skip to content

Commit b1d022b

Browse files
authored
Merge pull request #1 from Rei-Sogawa/mvp
composition-api を用いて todo-app を作ってみる
2 parents 807b043 + cd30489 commit b1d022b

18 files changed

+757
-396
lines changed

package-lock.json

+257-215
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

+6
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,12 @@
88
"lint": "vue-cli-service lint"
99
},
1010
"dependencies": {
11+
"@fortawesome/fontawesome-svg-core": "^1.2.32",
12+
"@fortawesome/free-brands-svg-icons": "^5.15.1",
13+
"@fortawesome/free-solid-svg-icons": "^5.15.1",
14+
"@fortawesome/vue-fontawesome": "^3.0.0-3",
15+
"bootstrap": "^4.5.3",
16+
"nanoid": "^3.1.20",
1117
"vue": "^3.0.0",
1218
"vue-router": "^4.0.0-0"
1319
},

src/App.vue

+3-22
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,11 @@
11
<template>
2-
<div id="nav">
3-
<router-link to="/">Home</router-link> |
4-
<router-link to="/about">About</router-link>
5-
</div>
62
<router-view />
73
</template>
84

95
<style>
6+
html,
7+
body,
108
#app {
11-
font-family: Avenir, Helvetica, Arial, sans-serif;
12-
-webkit-font-smoothing: antialiased;
13-
-moz-osx-font-smoothing: grayscale;
14-
text-align: center;
15-
color: #2c3e50;
16-
}
17-
18-
#nav {
19-
padding: 30px;
20-
}
21-
22-
#nav a {
23-
font-weight: bold;
24-
color: #2c3e50;
25-
}
26-
27-
#nav a.router-link-exact-active {
28-
color: #42b983;
9+
height: 100%;
2910
}
3011
</style>

src/basics/FontAwesomeIcon.vue

+48
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
<template>
2+
<svg
3+
xmlns="http://www.w3.org/2000/svg"
4+
:class="className"
5+
:viewBox="`0 0 ${width} ${height}`"
6+
>
7+
<path fill="currentColor" :d="svgPath" />
8+
</svg>
9+
</template>
10+
11+
<script lang="ts">
12+
import { defineComponent, computed, PropType } from "vue";
13+
import {
14+
findIconDefinition,
15+
IconName
16+
} from "@fortawesome/fontawesome-svg-core";
17+
18+
export default defineComponent({
19+
name: "FontAwesomeIcon",
20+
21+
props: {
22+
icon: {
23+
type: String as PropType<IconName>,
24+
required: true
25+
},
26+
type: {
27+
type: String as PropType<"fas" | "fal" | "far">,
28+
default: "fas"
29+
},
30+
className: String
31+
},
32+
33+
setup(props) {
34+
const definition = computed(() =>
35+
findIconDefinition({
36+
prefix: props.type,
37+
iconName: props.icon
38+
})
39+
);
40+
41+
const width = computed(() => definition.value.icon[0]);
42+
const height = computed(() => definition.value.icon[1]);
43+
const svgPath = computed(() => definition.value.icon[4]);
44+
45+
return { width, height, svgPath };
46+
}
47+
});
48+
</script>

src/components/CreateTodoForm.vue

+48
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
<template>
2+
<form @submit.prevent="handleSubmitNewTodo">
3+
<input
4+
type="text"
5+
placeholder="Add new todo"
6+
required
7+
class="form-control px-5 py-4"
8+
v-model.trim="state.title"
9+
@change="$emit('update:newTodoTitle', $event.target.value)"
10+
/>
11+
</form>
12+
</template>
13+
14+
<script lang="ts">
15+
import { defineComponent, PropType, reactive, watch } from "vue";
16+
17+
export type HandleSubmitNewTodo = () => void;
18+
19+
type State = {
20+
title: string;
21+
};
22+
23+
export default defineComponent({
24+
props: {
25+
newTodoTitle: {
26+
type: String as PropType<string>,
27+
required: true
28+
},
29+
handleSubmitNewTodo: {
30+
type: Function as PropType<HandleSubmitNewTodo>,
31+
required: true
32+
}
33+
},
34+
35+
emits: ["update:newTodoTitle"],
36+
37+
setup(props) {
38+
const state = reactive<State>({ title: props.newTodoTitle });
39+
watch(
40+
() => props.newTodoTitle,
41+
newValue => {
42+
state.title = newValue;
43+
}
44+
);
45+
return { state };
46+
}
47+
});
48+
</script>

src/components/EditTodoForm.vue

+46
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
<template>
2+
<form @submit.prevent="handleSubmitEditedTodoAndReset(todoBeingEdited)">
3+
<input
4+
ref="input"
5+
type="text"
6+
class="form-control px-5 py-4"
7+
v-model.trim="todoBeingEdited.title"
8+
@blur="handleCancelEdit"
9+
required
10+
/>
11+
</form>
12+
</template>
13+
14+
<script lang="ts">
15+
import { defineComponent, PropType, ref, onMounted } from "vue";
16+
import type Todo from "@/models/todo";
17+
18+
export type HandleCancelEdit = () => void;
19+
export type HandleSubmitEditedTodoAndReset = (editedTodo: Todo) => void;
20+
21+
export default defineComponent({
22+
props: {
23+
todo: {
24+
type: Object as PropType<Todo>,
25+
required: true,
26+
},
27+
handleSubmitEditedTodoAndReset: {
28+
type: Function as PropType<HandleSubmitEditedTodoAndReset>,
29+
required: true,
30+
},
31+
handleCancelEdit: {
32+
type: Function as PropType<HandleCancelEdit>,
33+
required: true,
34+
},
35+
},
36+
37+
setup(props) {
38+
const todoBeingEdited: Todo = { ...props.todo };
39+
const input = ref<HTMLInputElement | null>(null);
40+
onMounted(() => {
41+
input.value?.focus();
42+
});
43+
return { todoBeingEdited, input };
44+
},
45+
});
46+
</script>

src/components/HelloWorld.vue

-124
This file was deleted.

src/components/TodoItem.vue

+67
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
<template>
2+
<li class="list-group-item d-flex justify-content-between">
3+
<div>
4+
<input
5+
type="checkbox"
6+
class="mr-3"
7+
required
8+
:checked="todo.completed"
9+
@change="handleToggleCompleted(todo)"
10+
/>
11+
<span>{{ todo.title }}</span>
12+
</div>
13+
<div class="my-n2">
14+
<button class="btn px-1 mr-1" @click="handleClickEdit(todo.id)">
15+
<FontAwesomeIcon icon="edit" className="edit-icon" />
16+
</button>
17+
<button class="btn px-1" @click="handleClickRemove(todo.id)">
18+
<FontAwesomeIcon icon="trash" className="trash-icon" />
19+
</button>
20+
</div>
21+
</li>
22+
</template>
23+
24+
<script lang="ts">
25+
import { defineComponent, PropType } from "vue";
26+
import { FontAwesomeIcon } from "@/plugins/font-awesome";
27+
import Todo from "@/models/todo";
28+
29+
export type HandleToggleCompleted = (todo: Todo) => void;
30+
export type HandleClickEdit = (todoId: string) => void;
31+
export type HandleClickRemove = (todoId: string) => void;
32+
33+
export default defineComponent({
34+
components: { FontAwesomeIcon },
35+
36+
props: {
37+
todo: {
38+
type: Object as PropType<Todo>,
39+
required: true
40+
},
41+
handleToggleCompleted: {
42+
type: Function as PropType<HandleToggleCompleted>,
43+
required: true
44+
},
45+
handleClickEdit: {
46+
type: Function as PropType<HandleClickEdit>,
47+
required: true
48+
},
49+
handleClickRemove: {
50+
type: Function as PropType<HandleClickRemove>,
51+
required: true
52+
}
53+
}
54+
});
55+
</script>
56+
57+
<style scoped>
58+
.edit-icon {
59+
width: 15px;
60+
height: 15px;
61+
}
62+
63+
.trash-icon {
64+
width: 15px;
65+
height: 15px;
66+
}
67+
</style>

0 commit comments

Comments
 (0)