import { Subject, Observable } from 'rxjs';
import {
BucketEventType,
BucketDragStart,
BucketEvent,
BucketDragEnd,
IBucketContainerRef,
IBucketRef,
BucketDragChoose,
BucketRangeSelectionEvent
} from './types';
const MouseupEventType = 'mouseup';
const KeydownEventType = 'keydown';
const KeyCodes = {
ESCAPE: 27
};
/**
* 버킷 최상위 관리
* @export
* @class JdBucketRef
* @implements {IBucketRef}
*/
export class JdBucketRef implements IBucketRef {
constructor() {}
protected containerRefMap: Map<string, IBucketContainerRef> = new Map();
protected subjectDragger: Subject<BucketEvent> = new Subject();
protected subjectRangeSelection: Subject<BucketRangeSelectionEvent> = new Subject();
protected draggingContainerRef: IBucketContainerRef | null = null;
protected fallbackIndicate: boolean = false;
/**
* dnd 폴백을 사용하는지 여부
* @readonly
* @type {boolean}
*/
get isFallbackIndicate(): boolean {
return this.fallbackIndicate;
}
/**
* dnd 폴백을 사용하는지 여부 지정.
* 폴백을 사용하지 않는 다면 브라우저에서 기본으로 지원하는 드래그(중인 이미지) UI 를 사용하고,
* 폴백을 사용한다면 DOM 을 이용하는데, draggable 에서 사용하는 폴백 DOM 을 invisible 처리하고,
* draggable 대신 드래그 중인 UI 를 표시한다.
* @param {boolean} is
*/
setFallbackIndicate(is: boolean): void {
this.fallbackIndicate = !!is;
}
/**
*
* 등록된 버킷 컨테이너 맵.
* @returns {Map<string, IBucketContainerRef>}
*/
getContainerRefs(): Map<string, IBucketContainerRef> {
return this.containerRefMap;
}
/**
* 옵저버: 드래그 상태
* @returns {Observable<BucketEvent>}
*/
observeDragger(): Observable<BucketEvent> {
return this.subjectDragger.asObservable();
}
/**
* 옵저버: 영역 선택기 상태
* @returns {Observable<BucketRangeSelectionEvent>}
*/
observeRangeSelection(): Observable<BucketRangeSelectionEvent> {
return this.subjectRangeSelection.asObservable();
}
/**
* 알림: 드래그 아이템 선택
* @param {BucketDragChoose} params
*/
dispatchDragChoose(params: BucketDragChoose): void {
const { fromContainer, sortableEvent } = params;
this.subjectDragger.next({
type: BucketEventType.DRAG_CHOOSE,
fromContainer,
sortableEvent
});
}
/**
* 알림: 드래그 아이템 선택 해제
* @param {BucketDragChoose} params
*/
dispatchDragUnchoose(params: BucketDragChoose): void {
const { sortableEvent } = params;
this.subjectDragger.next({
type: BucketEventType.DRAG_UNCHOOSE,
sortableEvent
});
}
/**
* 알림: 드래그 시작
* @param {BucketDragStart} params
*/
dispatchDragStart(params: BucketDragStart): void {
const { fromContainer, sortableEvent } = params;
this.draggingContainerRef = fromContainer;
this.subjectDragger.next({
type: BucketEventType.DRAG_START,
sortableEvent,
fromContainer
});
this.removeKeyboardListener();
this.addKeyboardListener();
}
/**
* 알림: 드래그 종료
* @param {BucketDragEnd} params
*/
dispatchDragEnd(params: BucketDragEnd): void {
this.removeKeyboardListener();
const { toContainer, sortableEvent } = params;
const fromContainer = this.draggingContainerRef;
this.subjectDragger.next({
type: BucketEventType.DRAG_END,
sortableEvent,
fromContainer,
toContainer
});
toContainer.dispatchDropped({
sortableEvent,
fromContainer: this.draggingContainerRef,
toContainer: toContainer
});
this.draggingContainerRef = null;
}
/**
* 알림: 영역 선택기 시작
* @param {BucketRangeSelectionEvent} params
*/
dispatchRangeSelectionStart(params: BucketRangeSelectionEvent): void {
this.subjectRangeSelection.next(params);
}
/**
* 버킷 컨테이너 등록
* @param {IBucketContainerRef} containerRef
*/
joinContainerRef(containerRef: IBucketContainerRef): void {
this.containerRefMap.set(containerRef.getUid(), containerRef);
}
/**
* 버킷 컨테이너 등록 해제
* @param {IBucketContainerRef} containerRef
*/
unjoinContainerRef(containerRef: IBucketContainerRef): void {
this.containerRefMap.delete(containerRef.getUid());
}
/**
* 해당 element(DOM) 가 속한 버킷 컨테이너 찾기
* @param {Element} element
* @returns {IBucketContainerRef}
*/
findContainerRefByElement(element: Element): IBucketContainerRef {
const finded = Array.from(this.containerRefMap.values()).find((ref: IBucketContainerRef) => {
return ref.elContainer === element;
}) as IBucketContainerRef;
return finded;
}
/**
* document
* @returns {Document}
*/
protected getDocument(): Document {
return window.document;
}
/**
* 키보드 이벤트 핸들러
* @protected
*/
protected handleKeyboard = (evt: KeyboardEvent) => {
if (evt.keyCode === KeyCodes.ESCAPE) {
this.cancel();
}
};
/**
* 키보드 이벤트 핸들러 등록
* @protected
*/
protected addKeyboardListener() {
const doc = this.getDocument();
if (doc) {
doc.addEventListener(KeydownEventType, this.handleKeyboard);
}
}
/**
* 키보드 이벤트 핸들러 제거
* @protected
*/
protected removeKeyboardListener() {
const doc = this.getDocument();
if (doc) {
doc.removeEventListener(KeydownEventType, this.handleKeyboard);
}
}
/**
* 드래그 중 취소
*/
cancel() {
this.removeKeyboardListener();
if (this.draggingContainerRef) {
this.draggingContainerRef.flushDragItem();
document.dispatchEvent(new MouseEvent(MouseupEventType));
}
}
}