Comparing version 0.0.3 to 0.0.4
@@ -156,7 +156,6 @@ # 시작하기 | ||
onMount() { | ||
this.element().addEventListener('click', () => this.onClick()); | ||
return this; | ||
this.element().addEventListener('click', () => this.toggle()); | ||
} | ||
onClick() { | ||
toggle() { | ||
this.data.checked = !this.data.checked; | ||
@@ -174,7 +173,7 @@ this.element().classList.toggle('checked'); | ||
onMount() { | ||
this.addEventListener('click', this.onClick); | ||
return this; | ||
this.addEventListener('click', this.toggle); | ||
// or this.addEventListener('click', 'toggle'); | ||
} | ||
onClick() { | ||
toggle() { | ||
this.data.checked = !this.data.checked; | ||
@@ -186,10 +185,26 @@ this.element().classList.toggle('checked'); | ||
`view.addEventListener()`는 받은 함수를 등록해두었다가 이벤트가 실행되었을 때 `this`에 `view`를 바인딩하여 실행합니다. 위 코드에서 `ColorCheckboxView.prototype.onClick`은 하나의 함수이기 때문에 여러개의 ColorCheckboxView가 만들어지더라도 효율적입니다. | ||
`view.addEventListener()`는 받은 함수를 등록해두었다가 이벤트가 실행되었을 때 `this`에 `view`를 바인딩하여 실행합니다. 위 코드에서 `ColorCheckboxView.prototype.toggle`은 하나의 함수이기 때문에 여러개의 ColorCheckboxView가 만들어지더라도 효율적입니다. 혹은 `'toggle'`과 같이 메서드명을 전달해도 됩니다. | ||
### 커스텀 이벤트 디스패치 | ||
### 이벤트 등록 데코레이터 | ||
`@on` 데코레이터를 사용하면 보다 간결하게 코드를 작성할 수 있습니다. `@on('click')`은 `onMount` 내 작성했던 코드를 대체합니다. | ||
```typescript | ||
export class ColorCheckboxView extends View<Color> { | ||
@on('click') | ||
toggle() { | ||
this.data.checked = !this.data.checked; | ||
this.element().classList.toggle('checked'); | ||
} | ||
} | ||
``` | ||
### 커스텀 이벤트 디스패치와 이벤트 델리게이트 | ||
```typescript | ||
export class ColorCheckboxView extends View<Color> { | ||
... | ||
onClick() { | ||
@on('click') | ||
toggle() { | ||
this.data.checked = !this.data.checked; | ||
@@ -221,6 +236,28 @@ this.element().classList.toggle('checked'); | ||
}); | ||
return this; | ||
} | ||
} | ||
``` | ||
`@on` 데코레이터에 인자를 하나만 전달하면 `addEventListener`를 사용하고, `@on`에 두 번째 인자로 CSS 셀렉터를 함께 전달하면 `delegate`를 사용합니다. `delegate`도 데코레이터로 아래처럼 간결하게 작성할 수 있습니다. | ||
```typescript | ||
class MyView extends View<number> { | ||
onMount() { | ||
this.delegate('click', '.target', () => this.remove()); | ||
} | ||
remove() { | ||
this.element().remove(); | ||
} | ||
} | ||
class MyView extends View<number> { | ||
@on('click', '.target') | ||
remove() { | ||
this.element().remove(); | ||
} | ||
} | ||
``` | ||
### ColorCheckBoxListView 마무리 | ||
@@ -239,3 +276,3 @@ | ||
onMount() { | ||
return this.delegate('checkbox:change', '.ColorCheckboxView', this.onChange); | ||
this.delegate('checkbox:change', '.ColorCheckboxView', this.onChange); | ||
} | ||
@@ -273,5 +310,5 @@ | ||
위의 `ColorCheckboxListView`와 `ColorCheckboxView`는 체크할 수 있다는 속성을 가지고 있습니다. 체크 기능을 추상화한 View를 준비하면 체크 기능이 적용된 더 많은 View를 보다 쉽게 만들 수 있습니다. | ||
위의 `ColorCheckboxListView`와 `ColorCheckboxView`는 체크할 수 있다는 속성을 가지고 있습니다. 체크 기능을 추상화한 `View`를 준비하면 체크 기능이 적용된 더 많은 `View`를 보다 쉽게 만들 수 있습니다. | ||
먼저 `ColorView`, `ColorCheckboxListView`, `ColorCheckboxView`가 구현된 코드를 다시보면 아래와 같습니다. | ||
먼저 `ColorView`, `ColorCheckboxListView`, `ColorCheckboxView`의 코드를 다시보면 아래와 같습니다. | ||
@@ -301,8 +338,4 @@ ```typescript | ||
onMount() { | ||
this.element().addEventListener('click', () => this.onClick()); | ||
return this; | ||
} | ||
onClick() { | ||
@on('click') | ||
toggle() { | ||
this.data.checked = !this.data.checked; | ||
@@ -326,3 +359,3 @@ this.element().classList.toggle('checked'); | ||
onMount() { | ||
return this.delegate('checkbox:change', '.ColorCheckboxView', this.onChange); | ||
this.delegate('checkbox:change', '.ColorCheckboxView', this.onChange); | ||
} | ||
@@ -350,3 +383,2 @@ | ||
checked?: boolean; | ||
value?: string; | ||
}; | ||
@@ -367,11 +399,7 @@ | ||
createSubView(): View<T> | string { | ||
return this.SubView ? new this.SubView(this.data) : this.data.value || ''; | ||
return this.SubView ? new this.SubView(this.data) : ''; | ||
} | ||
onMount() { | ||
this.element().addEventListener('click', () => this.onClick()); | ||
return this; | ||
} | ||
onClick() { | ||
@on('click') | ||
toggle() { | ||
this.data.checked = !this.data.checked; | ||
@@ -403,6 +431,3 @@ this.element().classList.toggle('checked'); | ||
onMount() { | ||
return this.delegate('checkbox:change', `> .${this.CheckboxView}`, this.onChange); | ||
} | ||
@on('checkbox:change', '> *') | ||
onChange() { | ||
@@ -420,4 +445,4 @@ this.element().dispatchEvent( | ||
제네릭을 활용하여 `ColorCheckboxListView`, `ColorCheckboxView`를 확장한 `View`의 코드들에서 `data`의 | ||
타입을 추론할 수 있도록 하였습니다. `CheckboxView<T extends CheckboxData>`는 `CheckboxView`을 사용하거나 확장할 새로운 `View`의 `data`의 타입을 제약합니다. `tagName`, `CheckboxView` Constructor 등을 확장할 수 있도록 프로퍼티를 추가했습니다. | ||
제네릭을 활용하여 `CheckboxListView`, `CheckboxView`를 확장할 코드들에서 `data`의 | ||
타입을 추론할 수 있도록 하였습니다. `CheckboxView<T extends CheckboxData>`는 `CheckboxView`을 확장할 새로운 `View`의 `data`의 타입을 제약합니다. 또한 `tagName`, `SubView`, `CheckboxView` 등을 확장할 수 있도록 프로퍼티를 추가했습니다. | ||
@@ -518,2 +543,30 @@ | ||
### html을 활용하여 좀 더 쉽게 추상화하기 | ||
위에서 `CheckboxView`를 구현한 방식은 타입 정의를 잘해주어야하기 때문에 추상화가 약간 어렵습니다. rune의 템플릿 함수를 활용하면 아래처럼 좀 더 쉽게 추상화할 수 있습니다. | ||
```typescript | ||
export class CheckboxView<T extends CheckboxData> extends View<T> { | ||
tagName: string = 'li'; | ||
template({ checked }: T) { | ||
return html` | ||
<${this.tagName} class="${checked ? 'checked' : ''}"> | ||
${this.createSubView()} | ||
</${this.tagName}> | ||
`; | ||
} | ||
createSubView() { | ||
return html``; | ||
} | ||
} | ||
export class ColorCheckboxView extends CheckboxView<Color> { | ||
createSubView() { | ||
return html`${new ColorView(this.data)}`; | ||
} | ||
} | ||
``` | ||
## Enable | ||
@@ -535,7 +588,4 @@ | ||
class Checkable<T extends CheckableData> extends Enable<T> { | ||
onMount() { | ||
this.addEventListener('click', this.onClick); | ||
} | ||
onClick() { | ||
@on('click') | ||
toggle() { | ||
this.view.data.checked = !this.view.data.checked; | ||
@@ -557,3 +607,3 @@ this.view.element().classList.toggle('checked'); | ||
override template(color: Color) { | ||
template(color: Color) { | ||
return html` | ||
@@ -575,3 +625,3 @@ <div class="${color.checked ? 'checked' : ''}" style="background-color: ${color.code}"> | ||
```typescript | ||
onClick() { | ||
toggle() { | ||
this.data.checked = !this.data.checked; | ||
@@ -585,4 +635,21 @@ this.element().classList.toggle('checked'); | ||
`Enable`에서 `this.view.data === this.data` 이고 `this.view.element() === this.element()` 이기 때문에 onClick 영역을 위 코드처럼 변경할 수 있습니다. 이는 `View`를 작성할 때 만들었던 코드를 `Enable`로 옮겨 재사용가능한 코드로 만들고자 할 때 용이하게 합니다. | ||
`Enable`에서 `this.view.data === this.data` 이고 `this.view.element() === this.element()` 이기 때문에 toggle 영역을 위 코드처럼 변경할 수 있습니다. 이는 `View`를 작성할 때 만들었던 코드를 `Enable`로 옮겨 재사용가능한 코드로 만들고자 할 때 용이하게 합니다. | ||
### @enable 데코레이터 | ||
@enable 데코레이터를 활용하면 `CheckableColorView`를 간결하게 꾸며줄 수 있습니다. | ||
```typescript | ||
@enable(Checkable) | ||
class CheckableColorView extends View<Color> { | ||
template(color: Color) { | ||
return html` | ||
<div class="${color.checked ? 'checked' : ''}" style="background-color: ${color.code}"> | ||
</div> | ||
`; | ||
} | ||
} | ||
``` | ||
### 데이터 공유가 없는 View 확장 | ||
@@ -610,3 +677,3 @@ | ||
override template() { | ||
template() { | ||
return html` | ||
@@ -636,3 +703,3 @@ <div style=" | ||
### ExtraViewInterface | ||
### ViewExtraInterface | ||
@@ -642,8 +709,8 @@ 위 코드에서는 `Deletable`의 삭제를 트리거하는 엘리먼트의 클래스명을 `remove-target`라는 문자열로 약속을 했습니다. `interface`를 활용하면 객체간 통신의 규약을 더 확장성이 있으면서도 안전하게 추상화할 수 있습니다. | ||
```typescript | ||
interface DeletableExtraViewInterface { | ||
interface DeletableViewExtraInterface { | ||
readonly targetClassName: string; | ||
} | ||
export class Deletable extends Enable<unknown, DeletableExtraViewInterface> { | ||
override onMount() { | ||
export class Deletable extends Enable<unknown, DeletableViewExtraInterface> { | ||
onMount() { | ||
this.delegate('mousedown', `.${this.view.targetClassName}`, this.remove); | ||
@@ -659,5 +726,6 @@ } | ||
deletable = new Deletable(this).init(); | ||
readonly targetClassName = 'target'; | ||
override template() { | ||
template() { | ||
return html` | ||
@@ -681,3 +749,3 @@ <div style=" | ||
이제 `BallView`에서 `targetClassName`를 구현하지 않는다면 `Argument of type this is not assignable to parameter of type View<unknown> & DeletableExtraViewInterface`와 같은 에러메시지가 출력되어 개발자가 반드시 구현하도록 가이드를 줄 수 있습니다. | ||
이제 `BallView`에서 `targetClassName`를 구현하지 않는다면 `Argument of type this is not assignable to parameter of type View<unknown> & DeletableViewExtraInterface`와 같은 에러메시지가 출력되어 개발자가 반드시 구현하도록 가이드를 줄 수 있습니다. | ||
@@ -687,3 +755,3 @@ 다음은 객체간 통신의 예시로 `Deletable`이 `View`에게 `canRemove()`를 물어보고 삭제하도록 인터페이스와 구현을 추가했습니다. | ||
```typescript | ||
interface DeletableExtraViewInterface { | ||
interface DeletableViewExtraInterface { | ||
readonly targetClassName: string; | ||
@@ -693,4 +761,4 @@ canRemove(): boolean; | ||
export class Deletable extends Enable<unknown, DeletableExtraViewInterface> { | ||
override onMount() { | ||
export class Deletable extends Enable<unknown, DeletableViewExtraInterface> { | ||
onMount() { | ||
this.delegate('mousedown', `.${this.view.targetClassName}`, this.remove); | ||
@@ -713,3 +781,5 @@ } | ||
deletable = new Deletable(this).init(); | ||
readonly targetClassName = 'target'; | ||
canRemove() { | ||
@@ -719,3 +789,3 @@ return confirm('삭제하시겠습니까?'); | ||
override template() { | ||
template() { | ||
return html` | ||
@@ -756,5 +826,6 @@ ... | ||
movable = new Movable(this).init(); | ||
deletable = new Deletable(this).init(); | ||
readonly targetClassName = 'target'; | ||
canRemove() { | ||
@@ -764,3 +835,3 @@ return confirm('삭제하시겠습니까?'); | ||
override template() { | ||
template() { | ||
return html` | ||
@@ -783,5 +854,6 @@ ... | ||
movable = new Movable(this).init(); | ||
deletable = new Deletable(this).init(); | ||
readonly targetClassName = 'target'; | ||
canRemove() { | ||
@@ -791,3 +863,3 @@ return --this.data.count === 0; | ||
override template() { | ||
template() { | ||
return html` | ||
@@ -794,0 +866,0 @@ ... |
{ | ||
"name": "rune-ts", | ||
"version": "0.0.3", | ||
"version": "0.0.4", | ||
"description": "Rune Core Library", | ||
@@ -5,0 +5,0 @@ "engines": { |
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
191194
0