Skip to content

Commit

Permalink
[#701] Scheduler 컴포넌트 개발
Browse files Browse the repository at this point in the history
################
- 스케쥴러 컴포넌트 개발
- 리펙토링 및 테스트
- DOCS > 스케쥴러 예제 작성
- DOCS > 컴포넌트 마크다운 설명 추가
  • Loading branch information
kimdoeun committed Nov 3, 2020
1 parent 9ef4c9b commit 80c0554
Show file tree
Hide file tree
Showing 8 changed files with 494 additions and 0 deletions.
7 changes: 7 additions & 0 deletions docs/router/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import datePickerProps from 'docs/views/datePicker/props';
import calendarProps from 'docs/views/calendar/props';
import messageProps from 'docs/views/message/props';
import notificationProps from 'docs/views/notification/props';
import schedulerProps from 'docs/views/scheduler/props';

const routes = [
{
Expand Down Expand Up @@ -154,6 +155,12 @@ const routes = [
component: PageView,
props: notificationProps,
},
{
path: '/scheduler',
name: 'Scheduler',
component: PageView,
props: schedulerProps,
},
{
path: '/:catchAll(.*)',
name: 'PageNotFound',
Expand Down
31 changes: 31 additions & 0 deletions docs/views/scheduler/api/scheduler.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@

>### Desc
- 태그는 &lt;ev-scheduler&gt;(이하 <스케쥴러>)로 정의
```
<ev-scheduler
v-model=[]
...
/>
```

- <스케쥴러> 컴포넌트는 2차원 요소 내부에서 범위를 지정할 때 사용한다.

>### Props
| 이름 | 타입 | 디폴트 | 설명 | 종류 |
|------|--------|------|------|------|
| v-model | Array | [] | <스케쥴러>에서 선택된 값 | |
| widthOptions | Object | { count: 7, labels: \['<span style="color: #FF0000">SUN</span>', 'MON', 'TUE', 'WED', 'THU', 'FRI', '<span style="color: #0006F9">SAT</span>'\] } | 캘린더 Width(Column)의 옵션 | |
| | | count | 가로 칸 개수 | default: 7 (Number) |
| | | labels | 가로 칸 HTML 텍스트 | default: \['<span style="color: #FF0000">SUN</span>', 'MON', 'TUE', 'WED', 'THU', 'FRI', '<span style="color: #0006F9">SAT</span>'\] |
| heightOptions | Object | { count: 24, labels: \['00:00', '01:00', '02:00', '03:00', '04:00', '05:00', ... , '23:00''\] } | 캘린더 Height(Row)의 옵션 | |
| | | count | 세로 칸 개수 | default: 24 (Number) |
| | | labels | 가로 칸 HTML 텍스트 | default: \['00:00', '01:00', '02:00', '03:00', '04:00', '05:00', ... , '23:00''\] |

>### Event
| 이름 | 파라미터 | 설명 |
| ---- | ------- | ---- |
| change | newValue | <스케쥴러> 내 v-model 변화 이벤트 감지 |

>### 참고
-
93 changes: 93 additions & 0 deletions docs/views/scheduler/example/Default.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
<template>
<div class="case">
<p class="case-title">Common</p>
<ev-scheduler
v-model="checkVal1"
/>
<div class="description">
<span
class="badge"
@click="clearVal1"
>
Clear
</span>
{{ checkVal1 }}
</div>
</div>
<div class="case">
<p class="case-title">Custom</p>
<ev-scheduler
v-model="checkVal2"
:width-options="widthOptions"
:height-options="heightOptions"
/>
<div class="description">
{{ checkVal2 }}
</div>
</div>
</template>

<script>
import { ref } from 'vue';
export default {
setup() {
const checkVal1 = ref([]);
const checkVal2 = ref([]);
const widthLabels = (cnt) => {
const result = [];
for (let i = 0; i < cnt; i++) {
result.push(`+${i + 1}`);
}
return result;
};
const widthOptions = ref({
count: 15,
labels: widthLabels(15),
});
/**
* 월, 일을 두자리 숫자로 보정
* @param num
* @returns {string|*}
*/
const lpadToTwoDigits = (num) => {
if (num === null) {
return '00';
} else if (+num < 10) {
return `0${num}`;
}
return num;
};
const heightLabels = (cnt) => {
const result = [];
for (let i = 0; i < cnt; i++) {
if (i % 2) {
result.push(`${lpadToTwoDigits(Math.floor(i / 2))}:30`);
} else {
result.push(`${lpadToTwoDigits(i / 2)}:00`);
}
}
return result;
};
const heightOptions = ref({
count: 48,
labels: heightLabels(48),
});
const clearVal1 = () => { checkVal1.value = []; };
return {
checkVal1,
checkVal2,
widthOptions,
heightOptions,
clearVal1,
};
},
};
</script>

<style lang="scss">
</style>
15 changes: 15 additions & 0 deletions docs/views/scheduler/props.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { parseComponent } from 'vue-template-compiler';
import mdText from 'raw-loader!./api/scheduler.md';
import Default from './example/Default';
import DefaultRaw from '!!raw-loader!./example/Default';

export default {
mdText,
components: {
Default: {
description: '스케쥴피커는 일주일 내 시간 스케쥴을 선택하는 컴포넌트입니다.',
component: Default,
parsedData: parseComponent(DefaultRaw),
},
},
};
155 changes: 155 additions & 0 deletions src/components/scheduler/Scheduler.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
<template>
<table
class="ev-scheduler"
:class="{ draggable: !!dragEventName }"
>
<thead>
<tr class="ev-scheduler-header">
<td />
<td
v-for="(wItem, wIdx) in widthOptions.count"
:key="`${wItem}_${wIdx}_header`"
class="ev-scheduler-header-label"
@click="selectColumn(wIdx)"
v-html="widthOptions.labels[wIdx]"
/>
</tr>
</thead>
<tbody @mouseleave="mouseleaveBoxArea">
<tr
v-for="(hItem, hIdx) in heightOptions.count"
:key="`${hItem}_${hIdx}_body`"
class="ev-scheduler-body"
>
<td
:key="`${hItem}_${hIdx}_${heightOptions.labels[hIdx]}`"
class="ev-scheduler-body-label"
@click="selectRow(hIdx)"
v-html="heightOptions.labels[hIdx]"
/>
<td
v-for="(wItem, wIdx) in widthOptions.count"
:key="`${wItem}_${hIdx}_${wIdx}_body`"
class="ev-scheduler-body-box"
:class="{ selected: mv[hIdx][wIdx] }"
:style="selectionStyle(hIdx, wIdx)"
@mousedown.stop.prevent="mousedownBox(hIdx, wIdx, $event)"
@mouseup.stop.prevent="mouseupBox(hIdx, wIdx)"
@[`${dragEventName}`]="mousemoveBox(hIdx, wIdx)"
/>
</tr>
</tbody>
</table>
</template>

<script>
import { toRefs } from 'vue';
import { useModel, useEvent } from './uses';
export default {
name: 'EvScheduler',
props: {
modelValue: {
type: Array,
default: () => [],
},
widthOptions: {
type: Object,
default: () => ({
count: 7,
labels: ['<span style="color: #FF0000">SUN</span>',
'MON', 'TUE', 'WED', 'THU', 'FRI',
'<span style="color: #0006F9">SAT</span>',
],
}),
validator: ({ count, labels }) =>
(count ? typeof count === 'number' && count > 0 : true)
&& (labels ? Array.isArray(labels) && labels.length === count : true),
},
heightOptions: {
type: Object,
default: () => ({
count: 24,
labels: ['00:00', '01:00', '02:00', '03:00', '04:00', '05:00',
'06:00', '07:00', '08:00', '09:00', '10:00', '11:00',
'12:00', '13:00', '14:00', '15:00', '16:00', '17:00',
'18:00', '19:00', '20:00', '21:00', '22:00', '23:00'],
}),
validator: ({ count, labels }) =>
(count ? typeof count === 'number' && count > 0 : true)
&& (labels ? Array.isArray(labels) && labels.length === count : true),
},
},
emits: {
'update:modelValue': Array,
change: null,
},
setup() {
const {
mv,
validateValue,
} = useModel();
validateValue();
const {
mousePos,
selectionStyle,
mousedownBox,
mouseupBox,
mousemoveBox,
mouseleaveBoxArea,
selectColumn,
selectRow,
} = useEvent({ mv });
return {
mv,
selectionStyle,
mousedownBox,
mouseupBox,
mousemoveBox,
mouseleaveBoxArea,
selectColumn,
selectRow,
...toRefs(mousePos),
};
},
};
</script>

<style lang="scss">
@import '../../style/index.scss';
.ev-scheduler {
width: 100%;
border-collapse: collapse;
border-spacing: 0;
table-layout: fixed;
text-align: center;
font-size: $font-size-small;
user-select: none;
cursor: pointer;
&.draggable {
cursor: crosshair !important;
}
td {
height: 18px;
}
}
.ev-scheduler-body-box {
border: 1px solid #CCCCCC;
&.selected {
background-color: #CCEECC;
}
}
.ev-scheduler-selection {
position: absolute;
top: 0;
left: 0;
}
</style>
7 changes: 7 additions & 0 deletions src/components/scheduler/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import EvScheduler from './Scheduler';

EvScheduler.install = (app) => {
app.component(EvScheduler.name, EvScheduler);
};

export default EvScheduler;
Loading

0 comments on commit 80c0554

Please sign in to comment.