JSFiddle - React, Tailwind, and code Playground

by uiwwnw

SCSS

.ux-dialog {
  z-index: 1000;
  position: fixed;
  top: 0;
  left: 0;
  overflow: hidden;
  width: 100%;
  height: 100%;
  font-size: 0;
  text-align: center;

  &.close {
    pointer-events: none;
  }

  &:before {
    display: inline-block;
    height: 100%;
    vertical-align: middle;
    content: '';
  }

  &--close {
    position: absolute;
    top: 0;
    right: 0;
    width: 20px;
    height: 20px;
    margin: -10px;
    padding: 0;
    border: 0;
    border-radius: 50%;
    font-size: 0;
    background: #fff;

    &:before {
      font-size: 20px;
      content: '\d7';
    }
  }

  &--content {
    position: relative;
    max-width: 80%;
    display: inline-block;
    vertical-align: middle;
    padding: 10px;
    font-size: 12px;
    border-radius: 3px;
    box-shadow: 0 0 5px rgba(0, 0, 0, 0.25);
    background: #fff;
    animation: open-content .6s;

    .close > & {
      animation: close-content .6s;
    }
  }

  &--header {
    white-space: nowrap;
    text-align: left;
    font-size: 12px;
    border-bottom: 2px solid #333;
  }

  &--container {
    overflow: auto;
    max-height: 80vh;
    padding: 10px 0;
  }

  &--footer {
    font-size: 0;
    button {
      display: inline-block;
      font-size: 12px;
      border: 0;
      background: 0;
    }
  }

  &--dim {
    z-index: -1;
    top: 0;
    left: 0;
    position: absolute;
    width: 100%;
    height: 100%;
    opacity: .3;
    background: #000;
    animation: open-dim .4s;

    .close > & {
      animation: close-dim .6s;
    }
  }
}

@keyframes open-dim {
  0% {
    opacity: 0;
  }
  100% {
    opacity: .3;
  }
}


@keyframes close-dim {
  0% {
    opacity: .3;
  }
  100% {
    opacity: 0;
  }
}

@keyframes open-content {
  0% {
    transform: scale3d(.9, .9, .9);
  }
  50% {
    transform: scale3d(1.05, 1.05, 1.05);
  }
  100% {
    transform: scale3d(1, 1, 1);
  }
}

@keyframes close-content {
  0% {
    transform: scale3d(1, 1, 1);
  }
  50% {
    transform: scale3d(1.05, 1.05, 1.05);
...

TypeScript

interface Button {
    callback?: Function;
    text?: string;
}

interface Contents {
    title?: string;
    content?: string | Node;
    confirm?: Button | null;
    cancel?: Button | null;
}

const defaultText = {
    confirm: '확인',
    cancel: '취소'
};

const contentsType = {
    confirm: 'boolean',
    cancel: 'boolean',
    title: 'string',
    content: 'string',
    callback: 'function',
    text: 'string',
};

class UxDialog {
    private element: any;
    private contents: Contents;

    constructor(contents: Contents) {
        this.getDefaultContents(contents);
    }

    private checkType(object: object): void {
        Object.entries(object).map((val) => {
            const key = val[0];
            const value = val[1];
            if (typeof value === 'object') {
                this.checkType(value);
            } else {
                const type = contentsType[key];
                if (typeof value !== type) {
                    throw `${value}는 ${type}이 아닙니다.`
                }
            }
        });
    }

    private makeNode(contents: Contents): any {
        let confirm = '';
        let cancel = '';
        if (contents.confirm) {
            confirm = `<button name="confirm" type="button">${contents.confirm.text || defaultText.confirm}</button>`
        }
        if (contents.cancel) {
            cancel = `<button name="cancel" type="button">${contents.cancel.text || defaultText.cancel}</button>`
        }
        const dialog = `
          <div class="ux-dialog">
            <div class="ux-dialog--content">
              <button type="button" class="ux-dialog--close">this dialog close</button>
              <div class="ux-dialog--header">${contents.title}</div>
              <div class="ux-dialog--container">${contents.content}</div>
              <div class="ux-dialog--footer">${confirm}${cancel}</div>
            </div>
            <i class="ux-dialog--dim"></i>
          </div>
        `;
        return...