Skip to content

Commit

Permalink
[#646] Select 컴포넌트 1차 개발
Browse files Browse the repository at this point in the history
################
- 드랍다운 박스 좌표 계산 로직 수정
- 컴포넌트 내 clickoutside 로직 수정
- readonly props, change event 추가
- 불명확한 이벤트명 수정 및 불필요한 이벤트, watch 로직 삭제
- 컴포넌트 설명 markdown 내용 작성
  • Loading branch information
kimdoeun committed Sep 24, 2020
1 parent 4adc282 commit 6da1c2d
Show file tree
Hide file tree
Showing 5 changed files with 68 additions and 66 deletions.
65 changes: 30 additions & 35 deletions docs/views/select/api/select.md
Original file line number Diff line number Diff line change
@@ -1,58 +1,53 @@

>### Desc
- 태그는 &lt;EvCheckbox&gt;(이하 <체크박스>), &lt;EvCheckboxGroup&gt;(이하 <체크박스그룹>)으로 정의
- 태그는 &lt;EvSelect&gt;(이하 <셀렉트>)로 정의
```
<EvCheckboxGroup>
<EvCheckbox>텍스트</EvCheckbox>
<EvCheckbox>텍스트</EvCheckbox>
<EvCheckbox>텍스트</EvCheckbox>
</EvCheckboxGroup>
<EvSelect
v-model="초기값(value)"
:items="[{...}, {...}, {...}]"
/>
```

- <체크박스그룹>과 <체크박스> 태그는 위와 같이 상위에 그룹, 하위에 체크박스로 사용
(provide, injected를 사용하여 EVUI2.0 체크박스로직의 부모-자식 1depth 관계인 한계점을 개선하여, <체크박스그룹>과 <체크박스> 사이에 DOM
이 위치하더라도 그룹 내 스코프 내 양방향 데이터 바인딩이 가능하도록 함.)
- <체크박스그룹>.vue파일에서 <슬롯>을 사용하여 <체크박스>태그가 안에 들어가는 로직
- <체크박스><슬롯></체크박스>로 내부에 슬롯값이 들어가며, 내부 슬롯값이 없는 경우 <체크박스>의 label속성 값이 채워진다.
- <셀렉트> 컴포넌트는 `<input type="text" />` 를 래핑하는 구조를 가지고 있다.
- <셀릭트> 컴포넌트를 클릭하였을 때, :items 속성에 1개 이상의 객체를 가진 배열이 존재하는 경우,
하단에 선택할 리스트 데이터를 보여주는 <드랍다운 박스>가 나타난다.
- <드랍다운 박스>가 열린 상태에서 <셀렉트>를 한번더 클릭할 시 <드랍다운 박스>는 사라진다.
- <드랍다운 박스>는 Vue 3의 `<teleport />` 기능을 사용하여 `body` DOM에 렌더링시킨다.
이 기능은 애플리케이션 UI를 Vue 앱 외부의 DOM으로 이동시키지만, 코드를 <셀렉트> 컴포넌트에서
관리하기 위함이다.

>### Props
> 1) 체크박스 그룹 사용 시
>> <체크박스그룹>
> 1) 기본 셀렉트 사용 시
>> <셀렉트>
>
> | 이름 | 디폴트 | 타입 | 설명 | 종류 |
|------------ |-----------|---------|-------------------------|---------------------------------------------------|
| v-model | null | Array | <체크박스그룹>내 선택된 <체크박스> label 값으로, 해당 값은 바인딩되어 동적으로 변함 | |
>> <체크박스>
>
> | 이름 | 디폴트 | 타입 | 설명 | 종류 |
|------------ |-----------|---------|-------------------------|---------------------------------------------------|
| v-model | null | String, Number, Boolean, Symbol, Array | <체크박스그룹>내 선택된 <체크박스> label 값으로, 해당 값은 바인딩되어 동적으로 변함 | |
| label | null | String | HTML element value (required) | |
| disabled | false | Boolean | HTML element disabled attribute | |
| v-model | null | Boolean, String, Number | <셀렉트>에서 선택된 값으로, 해당 값은 바인딩되어 동적으로 변함 | |
| placeholder | '' | String | <셀렉트>의 표기문구 | |
| items | [] | Array | <셀렉트> 선택가능한 리스트 | |
> - <셀렉트> 클릭 시 <드랍다운 박스>가 나타나며, 목록 선택 시 <드랍다운 박스>가 닫혀야한다.
>
>
> 2) 체크박스만 사용 시
>> <체크박스>
> 2) 복수선택 셀렉트 사용 시
>> <셀렉트>
>
> | 이름 | 디폴트 | 타입 | 설명 | 종류 |
> | 이름 | 디폴트 | 타입 | 설명 | 종류 |
|------------ |-----------|---------|-------------------------|---------------------------------------------------|
| v-model | | String or Boolean | <체크박스>의 value | |
| value | | String | HTML element value | |
| size | | String | 체크박스의 크기 | '', 'small' |
| type | | String | 체크박스 박스의 모양 (default : 테두리 원) | '', 'square' |
| after-type | | String | 체크박스 내부 체크모양 (deault : 내부 원) | '', 'minus', 'check' |
| disabled | false | Boolean | HTML element disabled attribute | |
| v-model | null | Boolean, String, Number | <셀렉트>에서 선택된 값으로, 해당 값은 바인딩되어 동적으로 변함 | |
| placeholder | '' | String | <셀렉트>의 표기문구 | |
| items | [] | Array | <셀렉트> 선택가능한 리스트 | |
| multiple | false | Boolean | <셀렉트> 복수 선택 가능여부 | |
> - <셀렉트> 클릭 시 <드랍다운 박스>가 나타나며, 목록 선택 시 <드랍다운 박스>가 닫히지 말아야 한다.
>
>### Event
>> <체크박스그룹>
>> <셀렉트>
| 이름 | 파라미터 | 설명 |
| ---- | ------- | ---- |
| change | event | <체크박스그룹> 내 <체크박스> 변화 이벤트 감지 |
| change | event | <셀렉트> 내 v-model 변화 이벤트 감지 |

>### 참고
- :id는 내부적으로 가지고 있으며, <체크박스>태그 내 <label for=":id">와 연동하기 위함.
- 현재는 ${&#95;uid}&#95;${value}로 되어있으나, 추후 바꿀 예정
- <체크박스>체크박스텍스트값</체크박스>로 태그 내부에 텍스트는 &lt;slot/&gt;을 사용함.
- 현재 개발 진행 중
6 changes: 3 additions & 3 deletions src/components/checkbox/Checkbox.vue
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
type="checkbox"
:disabled="disabled"
:value="label"
@change="onChange"
@change="changeMv"
/>
<span
v-if="$slots.default"
Expand Down Expand Up @@ -69,15 +69,15 @@ export default {
return mv.value;
});
const onChange = async (e) => {
const changeMv = async (e) => {
await nextTick();
emit('change', mv.value, e);
};
return {
mv,
isChecked,
onChange,
changeMv,
};
},
};
Expand Down
21 changes: 12 additions & 9 deletions src/components/select/Select.vue
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,16 @@
:class="{
'is-selected': isDropbox,
}"
@click.stop.prevent="clickDropbox"
>
<input
v-model="mvName"
v-clickoutside="clickOutsideDropbox"
type="text"
class="ev-input"
:placeholder="placeholder"
readonly
:readonly="readonly"
@click.stop.prevent="clickSelectInput"
@change="changeMv"
/>
<i
class="ev-input-suffix ev-icon-s-arrow-down"
Expand Down Expand Up @@ -49,7 +49,7 @@

<script>
import { selectClickoutside as clickoutside } from '@/directives/clickoutside';
import { useDropdown, useModel } from './uses';
import { useModel, useDropdown } from './uses';
export default {
name: 'EvSelect',
Expand All @@ -69,12 +69,13 @@ export default {
type: Array,
default: () => [],
},
inputSize: {
type: Object,
default: () => ({
width: 200,
height: 35,
}),
multiple: {
type: Boolean,
default: false,
},
readonly: {
type: Boolean,
default: true,
},
},
emits: {
Expand All @@ -85,6 +86,7 @@ export default {
mv,
mvName,
clickItem,
changeMv,
} = useModel();
const {
Expand All @@ -100,6 +102,7 @@ export default {
mv,
mvName,
clickItem,
changeMv,
select,
isDropbox,
dropdownStyle,
Expand Down
24 changes: 14 additions & 10 deletions src/components/select/uses.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { ref, reactive, computed, watch, getCurrentInstance } from 'vue';
import { ref, reactive, computed, getCurrentInstance, nextTick } from 'vue';

export const useModel = () => {
const { props, emit } = getCurrentInstance();
Expand All @@ -9,12 +9,18 @@ export const useModel = () => {
set: value => emit('update:modelValue', value),
});
const mvName = computed(() => props.items.find(v => v.value === mv.value)?.name);

const clickItem = (val) => { mv.value = val; };
const changeMv = async (e) => {
await nextTick();
emit('change', mv.value, e);
};

return {
mv,
mvName,
clickItem,
changeMv,
};
};

Expand All @@ -26,14 +32,9 @@ export const useDropdown = () => {
top: 0,
left: 0,
width: 0,
height: 0,
});
const isDropbox = ref(false);
watch(
() => select.value?.getBoundingClientRect().width,
(width) => {
dropboxPosition.width = width;
},
);

const dropdownStyle = computed(() => ({
top: `${dropboxPosition.top}px`,
Expand All @@ -45,10 +46,13 @@ export const useDropdown = () => {
if (!props.items.length) {
return;
}
isDropbox.value = true;
isDropbox.value = !isDropbox.value;
const borderWidth = (select.value.offsetWidth - select.value.clientWidth) / 2;
dropboxPosition.left = e.clientX - e.offsetX - borderWidth;
dropboxPosition.top = e.clientY - e.offsetY + props.inputSize.height - borderWidth;
const selectRect = select.value?.getBoundingClientRect();

dropboxPosition.left = e.pageX - e.offsetX - borderWidth;
dropboxPosition.top = e.pageY - e.offsetY + selectRect?.height - borderWidth;
dropboxPosition.width = selectRect?.width;
};
const clickDropbox = () => { isDropbox.value = true; };
const clickOutsideDropbox = () => { isDropbox.value = false; };
Expand Down
18 changes: 9 additions & 9 deletions src/directives/clickoutside.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,19 +22,19 @@ const clickoutside = {

const selectClickoutside = {
mounted(el, binding) {
let componentEl = el;
const componentEl = el;
let dropLi = null;
const bubble = binding.modifiers.bubble;
const selectDropbox = document.body.getElementsByClassName('ev-select-dropdown');
const handler = (e) => {
if (!selectDropbox || !selectDropbox.length) {
if (selectDropbox && selectDropbox.length) {
if (bubble || (componentEl !== e.target && !componentEl.contains(e.target))) {
binding.value(e);
}
} else {
for (let i = 0; i < selectDropbox.length; i++) {
componentEl = selectDropbox[i];
if (componentEl !== e.target && !componentEl.contains(e.target)) {
binding.value(e);
for (let i = 0; i < selectDropbox.length; i++) {
dropLi = selectDropbox[i];
if (dropLi !== e.target && !dropLi.contains(e.target)) {
binding.value(e);
break;
}
}
}
}
Expand Down

0 comments on commit 6da1c2d

Please sign in to comment.