javascript

Создание модального компонента с помощью Vue.js

  • суббота, 17 февраля 2018 г. в 03:13:45
https://habrahabr.ru/post/349306/
  • JavaScript


В этой статье вы узнаете, как создать многоразовый модальный компонент с использованием переходов и слотов.

Определение структуры шаблона


Начнем с определения нашего шаблона. Нам понадобится div для тени заднего плана, div, чтобы действовать как модальный блок и некоторые элементы, чтобы определить его структуру:

<template>
  <div class="modal-backdrop">
    <div class="modal">
      <slot name="header">
      </slot>

      <slot name="body">
      </slot>

      <slot name="footer">
      </slot>
    </div>
  </div>
</template>

Обратите внимание на использование слотов? Мы могли бы использовать реквизиты для создания заголовка, тела и нижнего колонтитула, но использование слотов позволит обеспечить большую гибкость.

Использование слотов позволяет нам легко использовать один и тот же модальный вид с различными типами содержимого тела. Мы можем использовать модальный, чтобы показать простой текст, но мы можем захотеть повторно использовать тот же модальный форма, чтобы отправить запрос. Хотя реквизита обычно достаточно для создания компонента, предоставление HTML через опору потребует от нас использовать его v-htmlдля рендеринга — что может привести к атакам XSS.

Здесь мы используем именованные слоты, чтобы мы могли использовать более одного слота в одном компоненте.

Когда мы определяем именованный слот, все, что мы идентифицируем с этим именем, будет отображаться вместо исходного слота — давайте подумаем об этом как заполнителе.

Подобно заполнителю, слот может также иметь контент по умолчанию, который будет отображаться в случае, если мы его не предоставим.

Поскольку предоставленный контент заменяет тег, чтобы гарантировать, что наши разделы имеют требуемые классы, нам нужно обернуть каждый слот.

Давайте установим некоторые значения по умолчанию для слотов, элементов оболочки и начального CSS, чтобы сделать его похожим на базовый модальный.

<script>
  export default {
    name: 'modal',

    methods: {
      close() {
        this.$emit('close');
      },
    },
  };
</script>

<template>
  <div class="modal-backdrop">
    <div class="modal">
      <header class="modal-header">
        <slot name="header">
          This is the default tile!

          <button
            type="button"
            class="btn-close"
            @click="close"
          >
            x
          </button>
        </slot>
      </header>
      <section class="modal-body">
        <slot name="body">
          I'm the default body!
        </slot>
       </section>
       <footer class="modal-footer">
          <slot name="footer">
            I'm the default footer!

            <button
              type="button"
              class="btn-green"
              @click="close"
            >
              Close me!
          </button>
        </slot>
      </footer>
    </div>
  </div>
</template>

<style>
  .modal-backdrop {
    position: fixed;
    top: 0;
    bottom: 0;
    left: 0;
    right: 0;
    background-color: rgba(0, 0, 0, 0.3);
    display: flex;
    justify-content: center;
    align-items: center;
  }

  .modal {
    background: #FFFFFF;
    box-shadow: 2px 2px 20px 1px;
    overflow-x: auto;
    display: flex;
    flex-direction: column;
  }

  .modal-header,
  .modal-footer {
    padding: 15px;
    display: flex;
  }

  .modal-header {
    border-bottom: 1px solid #eeeeee;
    color: #4AAE9B;
    justify-content: space-between;
  }

  .modal-footer {
    border-top: 1px solid #eeeeee;
    justify-content: flex-end;
  }

  .modal-body {
    position: relative;
    padding: 20px 10px;
  }

  .btn-close {
    border: none;
    font-size: 20px;
    padding: 20px;
    cursor: pointer;
    font-weight: bold;
    color: #4AAE9B;
    background: transparent;
  }

  .btn-green {
    color: white;
    background: #4AAE9B;
    border: 1px solid #4AAE9B;
    border-radius: 2px;
  }
</style>

И мы сделали очень простую версию модальной коробки!

Добавление переходов


Обратите внимание, как модально открывается внезапно? Мы можем заставить его задушить, используя переход.

Vue предоставляет компонент-оболочку, transitionкоторый позволяет нам добавлять переходы для входа и выхода. Этот компонент оболочки может использоваться для любого элемента или компонента, и они позволяют использовать как CSS, так и JavaScript.

Каждый раз, когда компонент или элемент, завернутый переходом, вставлен или удален, Vue проверяет, имеет ли данный элемент CSS-переходы и будет добавлять или удалять их в нужное время. То же самое верно и для JavaScript-перехватчиков, но для этого случая мы будем использовать только CSS.

Когда элемент добавляется или удаляется, для перехода ввода / вывода применяются шесть классов. Каждое из них будет иметь префикс имени перехода. В этом руководстве вы найдете подробное объяснение того, как работают переходы.

Сначала давайте начнем с добавления компонента оболочки перехода к нашему модальному:

<template>
  <transition name="modal-fade">
    <div class="modal-backdrop">
      <div class="modal">
        ...
      </div>
    </div>
  </transition>
</template>

Теперь наш модально открывается и закрывается гладко!

Единственное, чего не хватает, — превратить этот модальный в доступный.

Мы можем достичь этого, используя атрибуты арии.

Добавление role=«dialog»поможет вспомогательному программному обеспечению идентифицировать наш компонент как диалоговое окно приложения, которое отделено от остальной части пользовательского интерфейса. Хотя добавление роли диалога полезно, недостаточно, чтобы сделать его доступным, мы должны соответствующим образом наклеить его. Мы можем достичь этого через aria-labelledbyи aria-describedbyатрибуты. Мы не можем забыть также наметить наши кнопки закрытия!

Окончательная версия нашего модального компонента теперь должна выглядеть так:

<script>
  export default {
    name: 'modal',
    methods: {
      close() {
        this.$emit('close');
      },
    },
  };
</script>
<template>
  <transition name="modal-fade">
    <div class="modal-backdrop">
      <div class="modal"
        role="dialog"
        aria-labelledby="modalTitle"
        aria-describedby="modalDescription"
      >
        <header
          class="modal-header"
          id="modalTitle"
        >
          <slot name="header">
            This is the default tile!

            <button
              type="button"
              class="btn-close"
              @click="close"
              aria-label="Close modal"
            >
              x
            </button>
          </slot>
        </header>
        <section
          class="modal-body"
          id="modalDescription"
        >
          <slot name="body">
            I'm the default body!
          </slot>
        </section>
        <footer class="modal-footer">
          <slot name="footer">
            I'm the default footer!

            <button
              type="button"
              class="btn-green"
              @click="close"
              aria-label="Close modal"
            >
              Close me!
            </button>
          </slot>
        </footer>
      </div>
    </div>
  </transition>
</template>
<style>
  .modal-backdrop {
    position: fixed;
    top: 0;
    bottom: 0;
    left: 0;
    right: 0;
    background-color: rgba(0, 0, 0, 0.3);
    display: flex;
    justify-content: center;
    align-items: center;
  }

  .modal {
    background: #FFFFFF;
    box-shadow: 2px 2px 20px 1px;
    overflow-x: auto;
    display: flex;
    flex-direction: column;
  }

  .modal-header,
  .modal-footer {
    padding: 15px;
    display: flex;
  }

  .modal-header {
    border-bottom: 1px solid #eeeeee;
    color: #4AAE9B;
    justify-content: space-between;
  }

  .modal-footer {
    border-top: 1px solid #eeeeee;
    justify-content: flex-end;
  }

  .modal-body {
    position: relative;
    padding: 20px 10px;
  }

  .btn-close {
    border: none;
    font-size: 20px;
    padding: 20px;
    cursor: pointer;
    font-weight: bold;
    color: #4AAE9B;
    background: transparent;
  }

  .btn-green {
    color: white;
    background: #4AAE9B;
    border: 1px solid #4AAE9B;
    border-radius: 2px;
  }
</style>


Использование модального компонента в нашем приложении


Теперь мы можем использовать наш модальный компонент, включив его в наше приложение.

<script>
  import modal from './components/modal.vue';

  export default {
    name: 'app',
    components: {
      modal,
    },
    data () {
      return {
        isModalVisible: false,
      };
    },
    methods: {
      showModal() {
        this.isModalVisible = true;
      },
      closeModal() {
        this.isModalVisible = false;
      }
    },
  };
</script>

<template>
  <div id="app">
    <button
      type="button"
      class="btn"
      @click="showModal"
    >
      Open Modal!
    </button>

    <modal
      v-show="isModalVisible"
      @close="closeModal"
    />
  </div>
</template>


P.S. Эта статья — перевод этой забугорной. В комментарии ниже я объяснил, как так получилось.