接下來就來把Task元件組裝成TaskList吧
可以把TaskList規劃成四種狀態
接下來就可以來建立 TaskList.vue 及 TaskList.stories.vue
<!--src/components/TaskList.vue-->
<template>
<div>
<div class="list-items" v-if="loading">loading</div>
<div class="list-items" v-if="noTasks && !this.loading">empty</div>
<div class="list-items" v-if="showTasks">
<task
v-for="(task, index) in tasks"
:key="index"
:task="task"
@archiveTask="$emit('archive-task', $event)"
@pinTask="$emit('pin-task', $event)"
/>
</div>
</div>
</template>
<script>
import Task from "./Task";
export default {
name: "task-list",
props: {
loading: {
type: Boolean,
default: false
},
tasks: {
type: Array,
default: () => []
}
},
components: {
Task
},
computed: {
noTasks() {
return this.tasks.length === 0;
},
showTasks() {
return !this.loading && !this.noTasks;
}
}
};
</script>
//src/components/TaskList.stories.js
import TaskList from "./TaskList";
import { taskData, actionsData } from "./Task.stories";
const paddedList = () => {
return {
template: '<div style="padding: 3rem;"><story/></div>'
};
};
export default {
title: "TaskList",
excludeStories: /.*Data$/,
decorators: [paddedList]
};
export const defaultTasksData = [
{ ...taskData, id: "1", title: "Task 1" },
{ ...taskData, id: "2", title: "Task 2" },
{ ...taskData, id: "3", title: "Task 3" },
{ ...taskData, id: "4", title: "Task 4" },
{ ...taskData, id: "5", title: "Task 5" },
{ ...taskData, id: "6", title: "Task 6" }
];
export const withPinnedTasksData = [
...defaultTasksData.slice(0, 5),
{ id: "6", title: "Task 6 (pinned)", state: "TASK_PINNED" }
];
// default TaskList state
export const Default = () => ({
components: { TaskList },
template: `<task-list :tasks="tasks" @archiveTask="onArchiveTask" @pinTask="onPinTask"/>`,
props: {
tasks: {
default: () => defaultTasksData
}
},
methods: actionsData
});
// tasklist with pinned tasks
export const WithPinnedTasks = () => ({
components: { TaskList },
template: `<task-list :tasks="tasks" @archiveTask="onArchiveTask" @pinTask="onPinTask"/>`,
props: {
tasks: {
default: () => withPinnedTasksData
}
},
methods: actionsData
});
// tasklist in loading state
export const Loading = () => ({
components: { TaskList },
template: `<task-list loading @archiveTask="onArchiveTask" @pinTask="onPinTask"/>`,
methods: actionsData
});
// tasklist no tasks
export const Empty = () => ({
components: { TaskList },
template: `<task-list @archiveTask="onArchiveTask" @pinTask="onPinTask"/>`,
methods: actionsData
});
在以上範例有一個decorators的設定
style="padding: 3rem;"
,在Decorators的單元有介紹到,可以使用Decorators為要描述的Stories製作元件以外的版型設定。
程式碼設置完成後,目前運行的畫面如下圖
Default Task 的部分是完整呈現了
但 WithPinnedTasks 的 Pinned Task 沒有出現在最上面
而 Loading 及 Empty 則還需要加上樣式調整
<template>
<div>
<div v-if="loading">
<div class="loading-item" v-for="(n, index) in 5" :key="index">
<span class="glow-checkbox" />
<span class="glow-text">
<span>Loading</span> <span>cool</span> <span>state</span>
</span>
</div>
</div>
<div class="list-items" v-if="noTasks && !this.loading">
<div class="wrapper-message">
<span class="icon-check" />
<div class="title-message">You have no tasks</div>
<div class="subtitle-message">Sit back and relax</div>
</div>
</div>
<div class="list-items" v-if="showTasks">
<task
v-for="(task, index) in tasksInOrder"
:key="index"
:task="task"
@archiveTask="$emit('archive-task', $event)"
@pinTask="$emit('pin-task', $event)"
/>
</div>
</div>
</template>
<script>
import Task from "./Task";
export default {
name: "task-list",
props: {
loading: {
type: Boolean,
default: false
},
tasks: {
type: Array,
default: () => []
}
},
components: {
Task
},
computed: {
noTasks() {
return this.tasks.length === 0;
},
showTasks() {
return !this.loading && !this.noTasks;
},
tasksInOrder() {
return [
...this.tasks.filter(t => t.state === "TASK_PINNED"),
...this.tasks.filter(t => t.state !== "TASK_PINNED")
];
}
}
};
</script>
從差異修改的地方可以看到 Loading 及 Empty 已加上樣式調整
而 tasks 改用 tasksInorder 計算屬性
用來整理資料,把 Pinned Task 放在資料的最前頭
目前運行畫面:
整個元件建置的過程,也沒有啟動專案本身的運行環境,僅僅只有啟動Storybook!
tag: composite-component https://git.io/JURQp
Composite Component:Vue