# Breakpoints

Переменные-брекпоинты лучше называть более интуитивно-понятно.

~/src/stylus/utils/_variables.styl

// Breakpoints
//////////////////////////////////////////////////////

$breakpoints = {
  tablet: 768px,
  desktop: 1025px,
}
$breakpoints["mobile--max"] = $breakpoints.tablet - 1
$breakpoints["tablet--max"] = $breakpoints.desktop - 1

WARNING

Основные точки перехода: в стилевой базе препроцессора в px и в константах скриптов библиотеки в Number - должны соответствовать друг-другу.

@/src/utils/сonstants.js

// Design constants
//////////////////////////////////////////////////////

export const DESIGN = {
  BREAKPOINTS: {
    tablet: 768,
    desktop: 1025,
  },
};

В препроцессоре - мощнейшее, очень удобное средство - построенные на брекпоинтах примеси принимающие контент:

// Media
//////////////////////////////////////////////////////

$no-gadgets()
  @media only screen and (max-width $breakpoints.desktop--max)
    {block}

$desktop()
  @media only screen and (min-width $breakpoints.desktop)
    {block}

$gadgets()
  @media only screen and (max-width $breakpoints.tablet--max)
    {block}

$tablet()
  @media only screen and (min-width $breakpoints.tablet) and (max-width $breakpoints.tablet--max)
    {block}

$not-mobile()
  @media only screen and (min-width $breakpoints.tablet)
    {block}

$mobile()
  @media only screen and (max-width $breakpoints.mobile--max)
    {block}

Использование в любом блоке стилей SFC:

.selector
  +$desktop()
    // styles only for desktops

  +$tablet()
    // styles only for tablet

  +$mobile()
    // styles only for mobile

TIP

В строгой традиции запрещается использование любых глобальных классов со стилями, за исключением анимаций для Vue и вынужденных кастомизаций действительно необходимых сторонних модулей где «классический ад с !important»))). Мы стараемся минимизировать количество зависимостей и «точечно» закрываем самые «дорогие», неподъемные по ресурсам проблемные места.

Точки перехода скриптов обрабатываются специальным модулем-помощником для экрана через matchMedia:

@/src/utils/screen-helper.js

// Viewport utils module
//////////////////////////////////////////////////////

import { DESIGN } from './constants.js';

const ScreenHelper = (() => {
  const TABLET = DESIGN.BREAKPOINTS.tablet;
  const DESKTOP = DESIGN.BREAKPOINTS.desktop;

  const isMobile = () => {
    return window.matchMedia(`(max-width: ${TABLET - 1}px)`).matches;
  };

  const isTablet = () => {
    return window.matchMedia(
      `(min-width: ${TABLET}px) and (max-width: ${DESKTOP - 1}px)`,
    ).matches;
  };

  const isDesktop = () => {
    return window.matchMedia(`(min-width: ${DESKTOP}px)`).matches;
  };

  const getOrientation = () => {
    if (window.matchMedia('(orientation: portrait)').matches) {
      return 'portrait';
    }
    return 'landscape';
  };

  const getPixelRatio = () => {
    // eslint-disable-next-line prettier/prettier
    return (
      window.devicePixelRatio ||
      window.screen.deviceXDPI / window.screen.logicalXDPI
    );
  };

  return {
    isMobile,
    isTablet,
    isDesktop,
    getOrientation,
    getPixelRatio,
  };
})();

export default ScreenHelper;

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

@/src/mixins/resize.js

import ScreenHelper from '../utils/screen-helper.js';

export default {
  data() {
    return {
      isMobile: null,
      isTablet: null,
      isDesktop: null,
    };
  },

  mounted() {
    window.addEventListener('resize', this.onWindowResizeCommon, false);
    this.onWindowResizeCommon();
  },

  beforeDestroy() {
    window.removeEventListener('resize', this.onWindowResizeCommon, false);
  },

  methods: {
    onWindowResizeCommon() {
      // console.log('onWindowResizeCommon!!!!');

      this.isMobile = ScreenHelper.isMobile();
      this.isTablet = ScreenHelper.isTablet();
      this.isDesktop = ScreenHelper.isDesktop();
    },
  },
};

На любых компонентах или видах в библиотеке:

<template>
  <Component v-show="isDesktop" />
  
  <div v-if="isDesktop" />
</template>

<script>
import resize from '../../../src/mixins/resize.js';

export default {
  name: 'ComponentName',

  mixins: [resize],
};
</script>

В проектах:

<script>
import resize from 'ui-library-starter/src/mixins/resize.js';

export default {
  name: 'Test',

  mixins: [resize],
};
</script>

Test component:

@/src/components/Tests/TestBreakpoints/TestBreakpoints.vue

Responsive Stylus mixins test:
The block is visible only on desktops
The block is visible only on tables
The block is visible only on mobiles

Resize javasript mixin test:
<template>
  <div>
    <div>Responsive Stylus mixins test:</div>
    <div class="visible--desktop">The block is visible only on desktops</div>
    <div class="visible--tablet">The block is visible only on tables</div>
    <div class="visible--mobile">The block is visible only on mobiles</div>
    <br />
    <div>Resize javasript mixin test:</div>
    <div v-if="isDesktop">The block is present in the DOM only on desktops</div>
    <div v-if="isTablet">The block is present in the DOM only on tablets</div>
    <div v-if="isMobile">The block is present in the DOM only on mobiles</div>
  </div>
</template>

<script>
import resize from '../../../../src/mixins/resize.js';

export default {
  name: 'TestBreakpoints',

  mixins: [resize],
};
</script>

<style lang="stylus" scoped>
@import "~/src/stylus/_stylebase.styl";

.visible
  &--desktop,
  &--tablet,
  &--mobile
    display none

  &--desktop
    +$desktop()
      display block

  &--tablet
    +$tablet()
      display block

  &--mobile
    +$mobile()
      display block
</style>