Update index.vue

Update index.vue

Added documentation for job picks, item reporting, and batching.

<template>

ePickList

ePickList (EPL) is a Linnworks application that generates an electronic pick list for your orders. EPL has been designed to improve your warehouse picking in two key areas:

Paper Waste: without having to print paper pick lists, you stand to save time & money by using an electronic alternative.

Human Error: by implementing restrictions, such as forced barcode scanning and user permissions, you can cut down on human error, like when warehouse staff accidentally pick the wrong item for an order.

Using EPL, you will be able to create interactive pick lists, that are simple and easy for warehouse staff to use. Once you have picked all the items you need to, ePickList can flag those picked orders, so that you can identify and manage them after a pick.

Bulk Order Pick

EPL is best used when picking for multiple orders all at the same time. When EPL generates a bulk pick list, it groups all items together, regardless of order relation, so your warehouse staff are making the most of their allotted time.

To switch to bulk picking mode, tap the menu toggle at the top right of the screen, and select Pick by View.

While orders are being picked, EPL will assign the Pick List Printed flag to the order in Linnworks.net. This can be useful, as configuring a Linnworks.net view to only show orders that have not had their pick list printed, means that you can hide all orders that are currently being picked from all future lists. This is great when you have more than one picker using the app at the same time.

Aborting the pick will remove the Pick List printed flag from the order, and successfully picking enough items to fulfil an order will trigger any pick actions that you have configured to run against that order.

Once you have picked enough items to fulfil at least one order, you have the option to end the pick list early. For orders that have not been picked, this is the same as aborting the pick, however orders that have been picked will still retain their pick action.

Single Order Pick

To pick for a single order at a time, use the menu toggle located at the top right of the screen to select ‘pick by order’ mode.

Simply scan or type a Linnworks order ID into the search field, and EPL will return a full list of order items, ready for picking.

While an order is being picked, EPL will assign the Pick List Printed flag to the order in Linnworks.net.

Aborting the pick will remove the Pick List printed flag from the order, and finishing a pick will trigger any pick actions that you have configured.

Job Pick

If you are using the Linnworks Order Flow functionality, you can use ePickList to generate a pick list for all orders in a specified workflow job.

To do this, first make sure you are in 'Job pick' mode, by selecting it from the top right menu.

Once you have configured your pick settings, you can search for a Job Name using the search bar located at the bottom of the screen.

The job Name can either be manually typed, or scanned from a barcode. Once ePickList has found the correct job, the pick list will be loaded, and you can begin picking

NOTE: This functionality is not available to all Linnworks users. For more information about the Order Workflows feature, please contact Linnworks Support

Pick Source

Single Order Pick

This setting allows you to select which Linnworks.net warehouse location you want to pick from. You can only pick orders that exist in the select location

Bulk Order Pick

When generating a pick list for multiple orders in bulk, EPL makes use of you existing Linnworks.net open order views.

Because Linnworks.net open order views are user specific, first select a user. EPL will now only be looking at this user’s views.

Once you have selected a view, EPL will only allow you to pick orders from that view. It may be worth setting up a ready to pick view in Linnworks, so that you know you are only selecting orders that are actually ready to be picked.

Alternatively, you can use the EPL Recommended view. This isn’t a view that exists in your Linnworks account, but is a view we have created to allow you to get started quickly, without having to manage your Linnworks.net views. This view has predefined filters. To be included in this view, the order must

Be Paid

Not be locked or parked

Have enough of its items in stock to be fulfilled

Not already have its pick list printed flag set

Pick Options

In Pick Options, you can change how EPL behaves while you work on a pick

Composite Mode lets you choose between showing composite children or parents

Pick Sort allows you to customise how the order’s items are arranged on screen

Auto Focus to Input Box means that after each pick, focus is always brought back to the search field. This option is perfect for paired barcode scanners

Move Picked Items to Bottom will instruct EPL to move an item row all the way to the bottom of the pick list once it’s been fully picked.

Pick Requirements

The Pick Requirements setting lets you choose if you want to force the picker to scan the correct item SKU or Barcode before an item can be marked as picked.

Pick Actions

Under Pick Actions, you can decide what happens to the order once it’s picked. You can choose to assign the order to a folder, assign a tag to the order, or both! You can even decide whether or not you want EPL to trigger an invoice print once the order has been fully picked.

Additional

The Additional settings allows you to decide if you want images & stock levels visible in your pick. If you have a slow internet connection, it may be worth disabling these.

Pick Amount

When working on a pick list, tap the menu icon at the top right of the screen to change your pick amount.

One: a tap or scan will add a single item to the amount required for that pick

Many: a tap or scan will open a keyboard, allowing the user to select how many is being picked

All: a tap or scan will mark the entire item row as picked, picking all of that item in one go

Item View

When picking, you can tap on the SKU of an item to see more information. On this screen you can view

An image of the item

The item's SKU, title, barcode, and bin/rack code

How many need to be picked, and how many you have already picked

A list of order IDs that require this item

Batch information

You can also tap the flag icon at the top right of this screen to report the item

Reporting an Item

When picking, you can tap on the SKU of an item to see more information. On this screen, tapping the flag icon will let you report this item.

When reporting an item, tap the 'reason' section to choose why you are reporting this item. There are two reason you may want to report an item:

Cannot find product: For when you can't find the item in the bin/rack location specified

Not enough to fulfil: For when you can find the item, but there isn't enough in the bin/rack location specified to fully pick the item

Reporting an item will leave a note on all the orders that could not be fulfilled, and flag these items in the ePickList Dashboard

Batched Items

If you are using the Stock Item Batching functionality in Linnworks, Linnworks will automatically assign your order items to the appropriate batches when ePickList generates a new pick.

Once an order item has been assigned to a batch, you will see the batch number highlighted in green, located underneath the amount needed to be picked.

If you decide that you cannot pick from the batches that Linnworks has assigned to these order items, you can change the batch assignment from the Report Item screen

On this screen you will be able to see all avaialable batches for this item. Tapping one will allow you to re-arrange the batch allocation

EPL Dashboard

In Linnworks, you can see an overview of all your EPL picks from the EPL Dashboard.

To view the dashboard in Linnworks.net, click the Apps button in the Linnworks.net left hand menu, and select ePickList. On this screen you can see every pick you have generated using ePicklist

The search bar allows you to find a specific pick by entering a pick reference. You can use the toggles to the right to view Active, Complete or Aborted picks

Clicking on a pick will allow you to see a list of orders and items included in that pick

The dashboard contains live information. Every action taken by a picker in EPL is reported instantly to this screen

</template>

<template>

ePickList

ePickList (ePL) is a Linnworks application that generates an electronic pick list for your orders. ePL has been designed to improve your warehouse picking in two key areas:

Paper Waste: without having to print paper pick lists, you stand to save time & money by using an electronic alternative.

Human Error: by implementing restrictions, such as forced barcode scanning and user permissions, you can cut down on human error, like when warehouse staff accidentally pick the wrong item for an order.

Using ePL, you will be able to create interactive pick lists, that are simple and easy for warehouse staff to use. Once you have picked all the items you need to, ePickList can flag those picked orders, so that you can identify and manage them after a pick.

Single Order Pick

ePL has two distinct modes for order picking. To pick a single order at a time, use the menu toggle located at the top right of the screen to select ‘pick by order’ mode.

Simply scan or type a Linnworks order ID into the search field, and ePL will return a full list of order items, ready for picking.

While an order is being picked, ePL will assign the Pick List Printed flag to the order in Linnworks.net.

Aborting the pick will remove the Pick List printed flag from the order, and finishing a pick will trigger any pick actions that you have configured.

Bulk Order Pick

ePL is best used when picking for multiple orders all at the same time. When ePL generates a bulk pick list, it groups all items together, regardless of order relation, so your warehouse staff are making the most of their allotted time.

To switch to bulk picking mode, tap the menu toggle at the top right of the screen, and select Pick by View.

While orders are being picked, ePL will assign the Pick List Printed flag to the order in Linnworks.net. This can be useful, as configuring a Linnworks.net view to only show orders that have not had their pick list printed, means that you can hide all orders that are currently being picked from all future lists. This is great when you have more than one picker using the app at the same time.

Aborting the pick will remove the Pick List printed flag from the order, and successfully picking enough items to fulfil an order will trigger any pick actions that you have configured to run against that order.

Once you have picked enough items to fulfil at least one order, you have the option to end the pick list early. For orders that have not been picked, this is the same as aborting the pick, however orders that have been picked will still retain their pick action.

Pick Amount

When working on a pick list, tap the menu icon at the top right of the screen to change your pick amount.

One: a tap or scan will add a single item to the amount required for that pick

Many: a tap or scan will open a keyboard, allowing the user to select how many is being picked

All: a tap or scan will mark the entire item row as picked, picking all of that item in one go

Pick Source

Single Order Pick

This setting allows you to select which Linnworks.net warehouse location you want to pick from. You can only pick orders that exist in the select location

Bulk Order Pick

When generating a pick list for multiple orders in bulk, ePL makes use of you existing Linnworks.net open order views.

Because Linnworks.net open order views are user specific, first select a user. ePL will now only be looking at this user’s views.

Once you have selected a view, ePL will only allow you to pick orders from that view. It may be worth setting up a ready to pick view in Linnworks, so that you know you are only selecting orders that are actually ready to be picked.

Alternatively, you can use the EPL Recommended view. This isn’t a view that exists in your Linnworks account, but is a view we have created to allow you to get started quickly, without having to manage your Linnworks.net views. This view has predefined filters. To be included in this view, the order must

Be Paid

Not be locked or parked

Have enough of its items in stock to be fulfilled

Not already have its pick list printed flag set

Pick Options

In Pick Options, you can change how ePL behaves while you work on a pick

Composite Mode lets you choose between showing composite children or parents

Pick Sort allows you to customise how the order’s items are arranged on screen

Auto Focus to Input Box means that after each pick, focus is always brought back to the search field. This option is perfect for paired barcode scanners

Move Picked Items to Bottom will instruct ePL to move an item row all the way to the bottom of the pick list once it’s been fully picked.

Pick Requirements

The Pick Requirements setting lets you choose if you want to force the picker to scan the correct item SKU or Barcode before an item can be marked as picked.

Pick Actions

Under Pick Actions, you can decide what happens to the order once it’s picked. You can choose to assign the order to a folder, assign a tag to the order, or both! You can even decide whether or not you want ePL to trigger an invoice print once the order has been fully picked.

Additional Settings

The Additional settings allows you to decide if you want images & stock levels visible in your pick. If you have a slow internet connection, it may be worth disabling these.

</template>


Change CreateArticle.vue Change CreateArticle.vue.

<template>
<v-layout row wrap>
    <v-flex xs12>

Create Article

	        <v-alert v-bind:info="!info" v-bind:error="!success" v-bind:value="true" v-if="msg">
			    {{ msg }}
			</v-alert>

		    <v-text-field
			    name="title"
			    label="Title"
			    type="text"
			    v-model="article.title"
			    dark
			    prepend-icon="subject"
			></v-text-field>
			{{ errFor.title.join(",") }}

		    <v-text-field
			    name="link"
			    label="Link"
			    type="text"
			    v-model="article.link"
			    dark
			    prepend-icon="link"
			></v-text-field>
			{{ errFor.link.join(",") }}

		    <v-text-field
			    name="short_description"
			    label="Short Description"
			    type="text"
			    v-model="article.short_description"
			    dark
			    prepend-icon="description"
			></v-text-field>
			{{ errFor.short_description.join(",") }}

		<v-layout row wrap>
	    	<v-flex xs6>
	    	    <v-text-field
	                name="content"
		            label="Content"
	                v-model="article.content"
	                multi-line
	                dark
	                prepend-icon="create"
	            ></v-text-field>
		    </v-flex>
		    <v-flex xs6>

Preview

		    </v-flex>
			{{ errFor.content.join(",") }}
		</v-layout>

		    <v-text-field
			    name="thumbnail"
			    label="Thumbnail"
			    type="text"
			    v-model="article.thumbnail"
			    dark
			    prepend-icon="insert_photo"
			></v-text-field>
			{{ errFor.thumbnail.join(",") }}

		    <v-select
		        v-bind:items="statusList"
			    name="status"
			    label="Status"
			    v-model="article.status"
			    dark
			    prepend-icon="visibility"
			></v-select>
			{{ errFor.status.join(",") }}

            <v-btn
                info
                v-bind:disabled="sending"
                v-on:click.native="create"
                small
            >
                Submit
            </v-btn>

    </v-flex>
</v-layout>
</template>

<template>
<v-col xs4="xs4">

Create Article

	        <v-alert v-bind:info="!info" v-bind:error="!success" v-bind:value="true" v-if="msg">
			    {{ msg }}
			</v-alert>

		    <v-text-field
			    name="title"
			    label="Title"
			    type="text"
			    v-model="article.title"
			></v-text-field>
			{{ errFor.title.join(",") }}

		    <v-text-field
			    name="link"
			    label="Link"
			    type="text"
			    v-model="article.link"
			></v-text-field>
			{{ errFor.link.join(",") }}

		    <v-text-field
			    name="short_description"
			    label="Short Description"
			    type="text"
			    v-model="article.short_description"
			></v-text-field>
			{{ errFor.short_description.join(",") }}

		<v-row>
	    	<v-col xs6="xs6">
	    	    <v-text-field
	                name="content"
		            label="Content"
	                v-model="article.content"
	                multi-line
	            ></v-text-field>
		    </v-col>
		    <v-col xs6="xs6">

Preview

		    </v-col>
			{{ errFor.content.join(",") }}
		</v-row>

		    <v-text-field
			    name="thumbnail"
			    label="Thumbnail"
			    type="text"
			    v-model="article.thumbnail"
			></v-text-field>
			{{ errFor.thumbnail.join(",") }}

		    <v-select
		        v-bind:items="statusList"
			    name="status"
			    label="Status"
			    v-model="article.status"
			></v-select>
			{{ errFor.status.join(",") }}

            <v-btn
                info
                v-bind:disabled="sending"
                v-on:click.native="create"
                small
            >
                Submit
            </v-btn>

</v-col>
</template>


Clean up

Clean up

<template>
<div class="note">
	<button @click="post" title="%i18n:@post%">%fa:pencil-alt%</button>
</div>
</template>

<script lang="ts">
import Vue from 'vue';

export default Vue.extend({
	methods: {
		post() {
			(this as any).apis.post();
		}
	}
});
</script>

<style lang="stylus" scoped>
.note
	display inline-block
	padding 8px
	height 100%
	vertical-align top

	> button
		display inline-block
		margin 0
		padding 0 10px
		height 100%
		font-size 1.2em
		font-weight normal
		text-decoration none
		color var(--primaryForeground)
		background var(--primary) !important
		outline none
		border none
		border-radius 4px
		transition background 0.1s ease
		cursor pointer

		*
			pointer-events none

		&:hover
			background var(--primaryLighten10) !important

		&:active
			background var(--primaryDarken10) !important
			transition background 0s ease

</style>


Try out vue-worker

Try out vue-worker

<template>

                {{ $t("byJsonFile.saveKeystore")}}

                  ?

                <by-json-block v-for="content in contents" :img="content.img" :title="content.title" :desc="content.desc" :key="content.title"></by-json-block>

                  {{ $t("byJsonFile.downloadKeyFile")}}

                  <router-link to="/">

                    {{ $t("reused.printWallet") }}
                  </router-link>

                    ?

</template>


fixFixed non unique ids on MdSelect (#2001)

<template>
  <md-menu
    class="md-select"
    :class="{ 'md-disabled': disabled }"
    :md-close-on-select="false"
    :md-active.sync="showSelect"
    :md-offset-x="offset.x"
    :md-offset-y="offset.y"
    :md-dense="mdDense"
    @md-closed="onClose">
    <md-input
      class="md-input md-select-value"
      v-model="MdSelect.label"
      ref="input"
      readonly
      :disabled="disabled"
      :required="required"
      :placeholder="placeholder"
      v-on="inputListeners"
      v-bind="attrs"
      @focus.prevent="onFocus"
      @blur.prevent="removeHighlight"
      @click="openSelect"
      @keydown.down="openSelect"
      @keydown.enter="openSelect"
      @keydown.space="openSelect"  />
    <md-drop-down-icon @click.native="openSelect" />

    <keep-alive>
      <md-menu-content
        ref="menu"
        class="md-select-menu"
        :md-content-class="mdClass"
        :style="menuStyles"
        @enter="onMenuEnter">
        <slot v-if="showSelect" />
      </md-menu-content>
    </keep-alive>

    <div v-if="!showSelect" v-show="false">
      <slot />
    </div>

    <input class="md-input-fake" v-model="model" :disabled="disabled" readonly tabindex="-1" />
    <select readonly v-model="model" v-bind="attributes" tabindex="-1"></select>
  </md-menu>
</template>

<script>
  import raf from 'raf'
  import MdComponent from 'core/MdComponent'
  import MdDropDownIcon from 'core/icons/MdDropDownIcon'
  import MdMenu from 'components/MdMenu/MdMenu'
  import MdMenuContent from 'components/MdMenu/MdMenuContent'
  import MdInput from '../MdInput/MdInput'
  import MdFieldMixin from '../MdFieldMixin'

  const defaultOffset = {
    x: -15,
    y: -48
  }

  export default {
    name: 'MdSelect',
    components: {
      MdInput,
      MdMenu,
      MdMenuContent,
      MdDropDownIcon
    },
    mixins: [MdFieldMixin],
    props: {
      mdDense: Boolean,
      mdClass: String,
      multiple: Boolean,
      id: String,
      name: String
    },
    inject: ['MdField'],
    data () {
      return {
        menuStyles: {},
        offset: {
          x: defaultOffset.x,
          y: 0
        },
        showSelect: true,
        didMount: false,
        MdSelect: {
          items: {},
          label: null,
          multiple: false,
          modelValue: this.localValue,
          setValue: this.setValue,
          setContent: this.setContent,
          setMultipleValue: this.setMultipleValue,
          setMultipleContent: this.setMultipleContent
        }
      }
    },
    provide () {
      const MdSelect = this.MdSelect

      return { MdSelect }
    },
    computed: {
      attrs () {
        return {
          ...this.$attrs,
          name: this.name,
          id: undefined
        }
      },
      inputListeners () {
        return {
          ...this.$listeners,
          input: undefined
        }
      }
    },
    watch: {
      localValue: {
        immediate: true,
        handler (val) {
          this.setFieldContent()
          this.MdSelect.modelValue = this.localValue

          if (this.didMount) {
            this.emitSelected(val)
          }
        }
      },
      multiple: {
        immediate: true,
        handler (isMultiple) {
          this.MdSelect.multiple = isMultiple
          this.$nextTick(this.initialLocalValueByDefault)
        }
      }
    },
    methods: {
      elHasScroll (el) {
        return el.scrollHeight > el.offsetHeight
      },
      scrollToSelectedOption (el, menu) {
        const top = el.offsetTop
        const elHeight = el.offsetHeight
        const menuHeight = menu.offsetHeight

        menu.scrollTop = top - ((menuHeight - elHeight) / 2)
      },
      setOffsets (target) {
        if (!this.$isServer) {
          const menu = this.$refs.menu.$refs.container

          if (menu) {
            const selected = target || menu.querySelector('.md-selected')

            if (selected) {
              this.scrollToSelectedOption(selected, menu)
              this.offset.y = defaultOffset.y - selected.offsetTop + menu.scrollTop + 8
              this.menuStyles = {
                'transform-origin': `0 ${Math.abs(this.offset.y)}px`
              }
            } else {
              this.offset.y = defaultOffset.y + 1
              this.menuStyles = {}
            }
          }
        }
      },
      onMenuEnter () {
        if (!this.didMount) {
          return
        }

        this.setOffsets()
        this.MdField.focused = true
        this.$emit('md-opened')
      },
      applyHighlight () {
        this.MdField.focused = false
        this.MdField.highlighted = true
        this.$refs.input.$el.focus()
      },
      onClose () {
        this.$emit('md-closed')
        if (this.didMount) {
          this.applyHighlight()
        }
      },
      onFocus () {
        if (this.didMount) {
          this.applyHighlight()
        }
      },
      removeHighlight () {
        this.MdField.highlighted = false
      },
      openSelect () {
        if (!this.disabled) {
          this.showSelect = true
        }
      },
      arrayAccessorRemove (arr, index) {
        let before = arr.slice(0, index)
        let after = arr.slice(index + 1, arr.length)
        return before.concat(after)
      },
      toggleArrayValue (value) {
        let index = this.localValue.indexOf(value)
        let includes = index > -1
        if (!includes) {
          this.localValue = this.localValue.concat([value])
        } else {
          this.localValue = this.arrayAccessorRemove(this.localValue, index)
        }
      },
      setValue (newValue) {
        this.model = newValue
        this.setFieldValue()
        this.showSelect = false
      },
      setContent (newLabel) {
        this.MdSelect.label = newLabel
      },
      setContentByValue () {
        const textContent = this.MdSelect.items[this.localValue]

        if (textContent) {
          this.setContent(textContent)
        } else {
          this.setContent('')
        }
      },
      setMultipleValue (value) {
        const newValue = value
        this.toggleArrayValue(newValue)
        this.setFieldValue()
      },
      setMultipleContentByValue () {
        if (!this.localValue) {
          this.initialLocalValueByDefault()
        }

        let content = []

        this.localValue.forEach(item => {
          const textContent = this.MdSelect.items[item]

          if (textContent) {
            content.push(textContent)
          }
        })

        this.setContent(content.join(', '))
      },
      setFieldContent () {
        if (this.multiple) {
          this.setMultipleContentByValue()
        } else {
          this.setContentByValue()
        }
      },
      isLocalValueSet () {
        return this.localValue !== undefined && this.localValue !== null
      },
      setLocalValueIfMultiple () {
        if (this.isLocalValueSet()) {
          this.localValue = [this.localValue]
        } else {
          this.localValue = []
        }
      },
      setLocalValueIfNotMultiple () {
        if (this.localValue.length > 0) {
          this.localValue = this.localValue[0]
        } else {
          this.localValue = null
        }
      },
      initialLocalValueByDefault () {
        let isArray = Array.isArray(this.localValue)

        if (this.multiple && !isArray) {
          this.localValue = this.setLocalValueIfMultiple()
        } else if (!this.multiple && isArray) {
          this.localValue = this.setLocalValueIfNotMultiple()
        }
      },
      emitSelected (value) {
        this.$emit('md-selected', value)
      }
    },
    mounted () {
      this.showSelect = false
      this.setFieldContent()

      this.$nextTick().then(() => {
        this.didMount = true
      })
    },
    updated () {
      this.setFieldContent()
    }
  }
</script>

<style lang="scss">
  @import "~components/MdAnimation/variables";

  .md-menu.md-select {
    display: flex;
    flex: 1;
    overflow: auto;

    &:not(.md-disabled) {
      .md-input,
      .md-icon {
        cursor: pointer;
        outline: none;
      }
    }

    .md-input {
      flex: 1;
      min-width: 0;
    }

    select,
    .md-input-fake {
      width: 1px;
      height: 1px;
      margin: -1px;
      padding: 0;
      overflow: hidden;
      position: absolute;
      clip: rect(0 0 0 0);
      border: 0;
    }
  }
  .md-menu-content.md-select-menu {
    z-index: 111;
    width: 100%;

    &.md-menu-content-enter {
      transform: translate3d(0, -8px, 0) scaleY(.3);
    }

    .md-list {
      transition: opacity .3s $md-transition-drop-timing;
    }
  }
</style>

<template>
  <md-menu
    class="md-select"
    :class="{ 'md-disabled': disabled }"
    :md-close-on-select="false"
    :md-active.sync="showSelect"
    :md-offset-x="offset.x"
    :md-offset-y="offset.y"
    :md-dense="mdDense"
    @md-closed="onClose">
    <md-input
      class="md-input md-select-value"
      v-model="MdSelect.label"
      ref="input"
      readonly
      :disabled="disabled"
      :required="required"
      :placeholder="placeholder"
      v-on="inputListeners"
      v-bind="attrs"
      @focus.prevent="onFocus"
      @blur.prevent="removeHighlight"
      @click="openSelect"
      @keydown.down="openSelect"
      @keydown.enter="openSelect"
      @keydown.space="openSelect"  />
    <md-drop-down-icon @click.native="openSelect" />

    <keep-alive>
      <md-menu-content
        ref="menu"
        class="md-select-menu"
        :md-content-class="mdClass"
        :style="menuStyles"
        @enter="onMenuEnter">
        <slot v-if="showSelect" />
      </md-menu-content>
    </keep-alive>

    <div v-if="!showSelect" v-show="false">
      <slot />
    </div>

    <input class="md-input-fake" v-model="model" :disabled="disabled" readonly tabindex="-1" />
    <select readonly v-model="model" v-bind="attributes" tabindex="-1"></select>
  </md-menu>
</template>

<script>
  import raf from 'raf'
  import MdComponent from 'core/MdComponent'
  import MdDropDownIcon from 'core/icons/MdDropDownIcon'
  import MdMenu from 'components/MdMenu/MdMenu'
  import MdMenuContent from 'components/MdMenu/MdMenuContent'
  import MdInput from '../MdInput/MdInput'
  import MdFieldMixin from '../MdFieldMixin'

  const defaultOffset = {
    x: -15,
    y: -48
  }

  export default {
    name: 'MdSelect',
    components: {
      MdInput,
      MdMenu,
      MdMenuContent,
      MdDropDownIcon
    },
    mixins: [MdFieldMixin],
    props: {
      mdDense: Boolean,
      mdClass: String,
      multiple: Boolean,
      id: String,
      name: String
    },
    inject: ['MdField'],
    data () {
      return {
        menuStyles: {},
        offset: {
          x: defaultOffset.x,
          y: 0
        },
        showSelect: true,
        didMount: false,
        MdSelect: {
          items: {},
          label: null,
          multiple: false,
          modelValue: this.localValue,
          setValue: this.setValue,
          setContent: this.setContent,
          setMultipleValue: this.setMultipleValue,
          setMultipleContent: this.setMultipleContent
        }
      }
    },
    provide () {
      const MdSelect = this.MdSelect

      return { MdSelect }
    },
    computed: {
      attrs () {
        return {
          ...this.$attrs,
          name: this.name,
          id: this.id
        }
      },
      inputListeners () {
        return {
          ...this.$listeners,
          input: undefined
        }
      }
    },
    watch: {
      localValue: {
        immediate: true,
        handler (val) {
          this.setFieldContent()
          this.MdSelect.modelValue = this.localValue

          if (this.didMount) {
            this.emitSelected(val)
          }
        }
      },
      multiple: {
        immediate: true,
        handler (isMultiple) {
          this.MdSelect.multiple = isMultiple
          this.$nextTick(this.initialLocalValueByDefault)
        }
      }
    },
    methods: {
      elHasScroll (el) {
        return el.scrollHeight > el.offsetHeight
      },
      scrollToSelectedOption (el, menu) {
        const top = el.offsetTop
        const elHeight = el.offsetHeight
        const menuHeight = menu.offsetHeight

        menu.scrollTop = top - ((menuHeight - elHeight) / 2)
      },
      setOffsets (target) {
        if (!this.$isServer) {
          const menu = this.$refs.menu.$refs.container

          if (menu) {
            const selected = target || menu.querySelector('.md-selected')

            if (selected) {
              this.scrollToSelectedOption(selected, menu)
              this.offset.y = defaultOffset.y - selected.offsetTop + menu.scrollTop + 8
              this.menuStyles = {
                'transform-origin': `0 ${Math.abs(this.offset.y)}px`
              }
            } else {
              this.offset.y = defaultOffset.y + 1
              this.menuStyles = {}
            }
          }
        }
      },
      onMenuEnter () {
        if (!this.didMount) {
          return
        }

        this.setOffsets()
        this.MdField.focused = true
        this.$emit('md-opened')
      },
      applyHighlight () {
        this.MdField.focused = false
        this.MdField.highlighted = true
        this.$refs.input.$el.focus()
      },
      onClose () {
        this.$emit('md-closed')
        if (this.didMount) {
          this.applyHighlight()
        }
      },
      onFocus () {
        if (this.didMount) {
          this.applyHighlight()
        }
      },
      removeHighlight () {
        this.MdField.highlighted = false
      },
      openSelect () {
        if (!this.disabled) {
          this.showSelect = true
        }
      },
      arrayAccessorRemove (arr, index) {
        let before = arr.slice(0, index)
        let after = arr.slice(index + 1, arr.length)
        return before.concat(after)
      },
      toggleArrayValue (value) {
        let index = this.localValue.indexOf(value)
        let includes = index > -1
        if (!includes) {
          this.localValue = this.localValue.concat([value])
        } else {
          this.localValue = this.arrayAccessorRemove(this.localValue, index)
        }
      },
      setValue (newValue) {
        this.model = newValue
        this.setFieldValue()
        this.showSelect = false
      },
      setContent (newLabel) {
        this.MdSelect.label = newLabel
      },
      setContentByValue () {
        const textContent = this.MdSelect.items[this.localValue]

        if (textContent) {
          this.setContent(textContent)
        } else {
          this.setContent('')
        }
      },
      setMultipleValue (value) {
        const newValue = value
        this.toggleArrayValue(newValue)
        this.setFieldValue()
      },
      setMultipleContentByValue () {
        if (!this.localValue) {
          this.initialLocalValueByDefault()
        }

        let content = []

        this.localValue.forEach(item => {
          const textContent = this.MdSelect.items[item]

          if (textContent) {
            content.push(textContent)
          }
        })

        this.setContent(content.join(', '))
      },
      setFieldContent () {
        if (this.multiple) {
          this.setMultipleContentByValue()
        } else {
          this.setContentByValue()
        }
      },
      isLocalValueSet () {
        return this.localValue !== undefined && this.localValue !== null
      },
      setLocalValueIfMultiple () {
        if (this.isLocalValueSet()) {
          this.localValue = [this.localValue]
        } else {
          this.localValue = []
        }
      },
      setLocalValueIfNotMultiple () {
        if (this.localValue.length > 0) {
          this.localValue = this.localValue[0]
        } else {
          this.localValue = null
        }
      },
      initialLocalValueByDefault () {
        let isArray = Array.isArray(this.localValue)

        if (this.multiple && !isArray) {
          this.localValue = this.setLocalValueIfMultiple()
        } else if (!this.multiple && isArray) {
          this.localValue = this.setLocalValueIfNotMultiple()
        }
      },
      emitSelected (value) {
        this.$emit('md-selected', value)
      }
    },
    mounted () {
      this.showSelect = false
      this.setFieldContent()

      this.$nextTick().then(() => {
        this.didMount = true
      })
    },
    updated () {
      this.setFieldContent()
    }
  }
</script>

<style lang="scss">
  @import "~components/MdAnimation/variables";

  .md-menu.md-select {
    display: flex;
    flex: 1;
    overflow: auto;

    &:not(.md-disabled) {
      .md-input,
      .md-icon {
        cursor: pointer;
        outline: none;
      }
    }

    .md-input {
      flex: 1;
      min-width: 0;
    }

    select,
    .md-input-fake {
      width: 1px;
      height: 1px;
      margin: -1px;
      padding: 0;
      overflow: hidden;
      position: absolute;
      clip: rect(0 0 0 0);
      border: 0;
    }
  }
  .md-menu-content.md-select-menu {
    z-index: 111;
    width: 100%;

    &.md-menu-content-enter {
      transform: translate3d(0, -8px, 0) scaleY(.3);
    }

    .md-list {
      transition: opacity .3s $md-transition-drop-timing;
    }
  }
</style>


Wrapping top-nav in Management

Wrapping top-nav in Management

<template>

      <router-link :to="usersLink" :class="{active: usersActive}" @click.native="blur">
        {{$tr('users')}}
      </router-link>
      <router-link :to="dataLink" :class="{active: dataActive}" @click.native="blur">
        {{$tr('data')}}
      </router-link>
      <router-link :to="contentLink" :class="{active: contentActive}" @click.native="blur" v-if="isDeviceOwner">
        {{$tr('content')}}
      </router-link>

</template>

<template>

      <router-link :to="usersLink" :class="{active: usersActive}" @click.native="blur">
        Users
      </router-link>
      <router-link :to="dataLink" :class="{active: dataActive}" @click.native="blur">
        Data
      </router-link>
      <router-link :to="contentLink" :class="{active: contentActive}" @click.native="blur" v-if="isDeviceOwner">
        Content
      </router-link>

</template>


Completely hide control bar

Completely hide control bar

<template>

      <loading-spinner/>

      <video ref="video" class="video-js custom-skin">
        <template v-for="video in videoSources">
          <source :src="video.storage_url" :type="`video/${video.extension}`">
        </template>
        <template v-for="track in trackSources">
          <track kind="captions" :src="track.storage_url" :srclang="track.lang" :label="getLangName(track.lang)" :default="isDefaultTrack(track.lang)">
        </template>
      </video>

</template>


hamburger hidden while searching

hamburger hidden while searching

<template lang="html">
  <md-toolbar>
    <md-button class="md-icon-button drawer-open-btn" v-show="!searchVisible" @click.native="toggleLeftSidenav">
      <md-icon>menu</md-icon>
    </md-button>

{{title}}

    <form v-on:submit.prevent="search">
      <md-input-container class="search-input" v-bind:class="{shown: searchVisible}" md-inline>
        Search
        <md-input v-model="searchInput" ref="searchInputField"></md-input>
      </md-input-container>
    </form>
    <md-button class="md-icon-button btn-search-close" v-show="searchVisible" @click.native="searchVisible = false">
      <md-icon>close</md-icon>
    </md-button>
    <md-button class="md-icon-button" v-show="!searchVisible" @click.native="openSearch">
      <md-icon>search</md-icon>
    </md-button>
  </md-toolbar>
</template>

<template lang="html">
  <md-toolbar>
    <md-button class="md-icon-button drawer-open-btn" @click.native="toggleLeftSidenav">
      <md-icon>menu</md-icon>
    </md-button>

{{title}}

    <form v-on:submit.prevent="search">
      <md-input-container class="search-input" v-bind:class="{shown: searchVisible}" md-inline>
        Search
        <md-input v-model="searchInput" ref="searchInputField"></md-input>
      </md-input-container>
    </form>
    <md-button class="md-icon-button btn-search-close" v-show="searchVisible" @click.native="searchVisible = false">
      <md-icon>close</md-icon>
    </md-button>
    <md-button class="md-icon-button" v-show="!searchVisible" @click.native="openSearch">
      <md-icon>search</md-icon>
    </md-button>
  </md-toolbar>
</template>


Userlist tidying

Userlist tidying

<template>
  <v-container class="pa-1 pb-0" fill-height>
    <v-layout v-if="ptRoom" column>
      <v-flex xs12 style="height: 50vh">
        <v-flex xs12>
          <v-layout row wrap justify-space-between="" align-center>
            <v-flex xs8 offset-xs2 class="text-xs-center">

 Room {{ ptRoom }}

            </v-flex>
            <v-flex xs2>
              <v-menu>
                <v-btn icon slot="activator" class="ma-0 pa-0" dark>
                  <v-icon>more_vert</v-icon>
                </v-btn>
                <v-list>
                  <v-list-tile @click="handleDisconnect()">
                    <v-list-tile-title>Leave Room</v-list-tile-title>
                  </v-list-tile>
                </v-list>
              </v-menu>
            </v-flex>
          </v-layout>
        </v-flex>

        <v-subheader>Users ({{ ptUsers.length }})</v-subheader>
        <v-list dense three-line style="overflow-y:scroll; max-height: 40vh; background: none">

            <v-list-tile avatar style="height:4em" class="pb-0 mb-0" tag="div" >
              <v-list-tile-avatar>

                  <v-icon v-if="user.playerState !== 'playing'" style="font-size: 32px; opacity: 0.8; position: absolute;background-color: rgba(0,0,0,0.7)">{{ playerState(user) }}</v-icon>

              </v-list-tile-avatar>
              <v-list-tile-content>
                <v-tooltip bottom color="primary">

                    <v-list-tile-title> {{ user.username }}</v-list-tile-title>
                    <v-list-tile-sub-title style="opacity:0.6;color:white;font-size:70%">{{ getTitle(user) }}</v-list-tile-sub-title>

                  Watching on {{ user.playerProduct || 'Unknown Plex Client' }}
                </v-tooltip>
              </v-list-tile-content>
              <v-list-tile-action  v-if="isHost(user)">
                <v-icon v-if="isHost(user)" style="color: #E5A00D">star</v-icon>
              </v-list-tile-action>
            </v-list-tile>

            {{ getCurrent(user) }}
            {{ getMax(user) }}
            <v-progress-linear class="pt-content-progress " :height="2" :value="percent(user)"></v-progress-linear>

        </v-list>
      </v-flex>
      <v-flex xs12 style="position: relative;">
        <v-layout column wrap justify-space-around>
          <v-flex>
            <v-divider></v-divider>
            <v-subheader>Messages</v-subheader>
            <v-list id="chatbox" style="overflow-y:scroll; min-height: 35vh; background: none; max-height: 40vh; overflow: scroll">
              <v-list-tile  style="min-height:50px; height:initial; position:relative" v-bind:id="getMsgId(msg)" v-for="(msg, index) in messages" v-bind:key="index" tag="div">
                <v-list-tile-avatar>

                </v-list-tile-avatar>
                <v-list-tile-content>
                  <v-list-tile-title style="color:white; position:relative;">
                     {{ msg.user.username }}
                     {{ msg.time}}
                  </v-list-tile-title>
                  <v-list-tile-sub-title style="opacity:0.8;color:white;font-size:70%;"> {{ msg.msg }}</v-list-tile-sub-title>
                </v-list-tile-content>
              </v-list-tile>
            </v-list>
          </v-flex>
          <v-spacer></v-spacer>
          <v-flex style="position: relative">
            <v-text-field
              prepend-icon="message"
              :label="'Send a message to ' + '#' + ptRoom"
              class="ma-0 ml-1 pr-1 wideinput"
              v-on:keyup.enter.native="sendMessage()"
              v-model="messageToBeSent"
            ></v-text-field>
            <v-btn block color="primary" @click="sendMessage()">Send</v-btn>
          </v-flex>
        </v-layout>
      </v-flex>
    </v-layout>
  </v-container>
</template>

<template>
  <v-container class="pa-1 pb-0" fill-height>
    <v-layout v-if="ptRoom" column>
      <v-flex xs12 style="height: 50vh">
        <v-flex xs12>
          <v-layout row wrap justify-space-between="" align-center>
            <v-flex xs8 offset-xs2 class="text-xs-center">

 Room {{ ptRoom }}

            </v-flex>
            <v-flex xs2>
              <v-menu>
                <v-btn icon slot="activator" class="ma-0 pa-0" dark>
                  <v-icon>more_vert</v-icon>
                </v-btn>
                <v-list>
                  <v-list-tile @click="handleDisconnect()">
                    <v-list-tile-title>Leave Room</v-list-tile-title>
                  </v-list-tile>
                </v-list>
              </v-menu>
            </v-flex>
          </v-layout>
        </v-flex>

        <v-subheader>Users ({{ ptUsers.length }})</v-subheader>
        <v-list dense style="overflow-y:scroll; max-height: 40vh; background: none">

              <v-list-tile avatar style="height:4em" class="pb-0 mb-0" tag="div" >
                <v-list-tile-avatar>

                    <v-icon v-if="user.playerState !== 'playing'" style="font-size: 32px; opacity: 0.8; position: absolute;background-color: rgba(0,0,0,0.7)">{{ playerState(user) }}</v-icon>

                </v-list-tile-avatar>
                <v-list-tile-content>
                  <v-tooltip bottom color="primary">

                      <v-list-tile-title> {{ user.username }}</v-list-tile-title>
                      <v-list-tile-sub-title style="opacity:0.6;color:white;font-size:70%">{{ getTitle(user) }}</v-list-tile-sub-title>

                    Watching on {{ user.playerProduct || 'Unknown Plex Client' }}
                  </v-tooltip>
                </v-list-tile-content>
                <v-list-tile-action  v-if="isHost(user)">
                  <v-icon v-if="isHost(user)" style="color: #E5A00D">star</v-icon>
                </v-list-tile-action>
              </v-list-tile>

              {{ getCurrent(user) }}
              {{ getMax(user) }}
              <v-progress-linear class="pt-content-progress " :height="2" :value="percent(user)"></v-progress-linear>

        </v-list>
      </v-flex>
      <v-flex xs12 style="position: relative;">
        <v-layout column wrap justify-space-around>
          <v-flex>
            <v-divider></v-divider>
            <v-subheader>Messages</v-subheader>
            <v-list id="chatbox" style="overflow-y:scroll; min-height: 35vh; background: none; max-height: 40vh; overflow: scroll">
              <v-list-tile  style="min-height:50px; height:initial; position:relative" v-bind:id="getMsgId(msg)" v-for="msg in messages" v-bind:key="msg.msg + msg.time" tag="div">
                <v-list-tile-avatar>

                </v-list-tile-avatar>
                <v-list-tile-content>
                  <v-list-tile-title style="color:white; position:relative;">
                     {{ msg.user.username }}
                     {{ msg.time}}
                  </v-list-tile-title>
                  <v-list-tile-sub-title style="opacity:0.8;color:white;font-size:70%;"> {{ msg.msg }}</v-list-tile-sub-title>
                </v-list-tile-content>
              </v-list-tile>
            </v-list>
          </v-flex>
          <v-spacer></v-spacer>
          <v-flex style="position: relative">
            <v-text-field
              prepend-icon="message"
              :label="'Send a message to ' + '#' + ptRoom"
              class="ma-0 ml-1 pr-1 wideinput"
              v-on:keyup.enter.native="sendMessage()"
              v-model="messageToBeSent"
            ></v-text-field>
            <v-btn block color="primary" @click="sendMessage()">Send</v-btn>
          </v-flex>
        </v-layout>
      </v-flex>
    </v-layout>
  </v-container>
</template>


Updates to Pagination page

Updates to Pagination page

<template>
  <q-page padding>

With buttons

    <q-pagination
      v-model="page"
      :min="1"
      :max="6"
    />

With inputbox

    <q-pagination
      input
      v-model="page"
      :min="1"
      :max="6"
    />

With maximum number of links & custom color

    <q-pagination
      v-model="page2"
      color="secondary"
      :min="1"
      :max="15"
      :max-pages="6"
    />

With no ellipses

    <q-pagination
      v-model="page2"
      color="amber"
      :min="1"
      :max="15"
      :max-pages="6"
      :ellipses="false"
    />

With boundary links

    <q-pagination
      v-model="page2"
      color="purple"
      :min="1"
      :max="15"
      :max-pages="6"
      boundary-links
    />

With direction links

    <q-pagination
      v-model="page2"
      color="teal"
      :min="1"
      :max="15"
      :max-pages="6"
      direction-links
    />

With custom interval

    <q-pagination
      v-model="page3"
      :min="5"
      :max="10"
    />

Mix and match

    <q-pagination
      v-model="page4"
      color="tertiary"
      :min="7"
      :max="18"
      :max-pages="8"
      boundary-links
      direction-links
    />
  </q-page>
</template>

<template>
  <q-page padding>

With buttons

    <q-pagination
      v-model="page"
      :min="1"
      :max="6"
    />

With inputbox

    <q-pagination
      input
      v-model="page"
      :min="1"
      :max="6"
    />

With maximum number of links & custom color

    <q-pagination
      v-model="page2"
      color="secondary"
      :min="1"
      :max="15"
      :max-pages="6"
    />

With no ellipses

    <q-pagination
      v-model="page2"
      :min="1"
      :max="15"
      :max-pages="6"
      :ellipses="false"
    />

With boundary links

    <q-pagination
      v-model="page2"
      :min="1"
      :max="15"
      :max-pages="6"
      boundary-links
    />

With direction links

    <q-pagination
      v-model="page2"
      :min="1"
      :max="15"
      :max-pages="6"
      direction-links
    />

With custom interval

    <q-pagination
      v-model="page3"
      :min="5"
      :max="10"
    />

Mix and match

    <q-pagination
      v-model="page4"
      :min="7"
      :max="18"
      :max-pages="8"
      boundary-links
      direction-links
    />
  </q-page>
</template>


Fix nav styling

Fix nav styling

<template>
  <!--Nav panel-->
  <div class="vc-nav-container">
    <!--Nav header-->
    <grid :columns="3" ref="headerGrid" @rollover="onHeaderRollover">
      <!--Move prev button-->
      <span
        role="button"
        class="vc-nav-arrow is-left"
        tabindex="-1"
        @click="movePrev"
        @keydown="e => onSpaceOrEnter(e, movePrev)"
        ref="prevButton"
      >
        <slot name="nav-left-button">
          <svg-icon name="left-arrow" width="20px" height="24px" />
        </slot>
      </span>
      <!--Mode switch button-->
      <span
        role="button"
        class="vc-nav-title vc-grid-focus"
        :style="{ whiteSpace: 'nowrap' }"
        tabindex="0"
        @click="toggleMode"
        @keydown="e => onSpaceOrEnter(e, toggleMode)"
        ref="titleButton"
      >
        {{ title }}
      </span>
      <!--Move next button-->
      <span
        role="button"
        class="vc-nav-arrow is-right"
        tabindex="-1"
        @click="moveNext"
        @keydown="e => onSpaceOrEnter(e, moveNext)"
        ref="nextButton"
      >
        <slot name="nav-right-button">
          <svg-icon name="right-arrow" width="20px" height="24px" />
        </slot>
      </span>
    </grid>
    <!--Navigation items-->
    <grid
      :rows="4"
      :columns="3"
      gap="2px 5px"
      ref="itemsGrid"
      @rollover="onItemsRollover"
    >
      <span
        v-for="item in activeItems"
        :key="item.label"
        role="button"
        :aria-label="item.ariaLabel"
        :class="getItemClasses(item)"
        :tabindex="item.isDisabled ? undefined : item.isActive ? 0 : -1"
        @click="item.click"
        @keydown="e => onSpaceOrEnter(e, item.click)"
        ref="items"
      >
        {{ item.label }}
      </span>
    </grid>
  </div>
</template>

<script>
import Grid from './Grid';
import SvgIcon from './SvgIcon';
import { childMixin } from '../utils/mixins';
import { head, last } from '../utils/_';
import { pageForDate, onSpaceOrEnter } from '../utils/helpers';

const _yearGroupCount = 12;

export default {
  name: 'CalendarNav',
  components: {
    Grid,
    SvgIcon,
  },
  mixins: [childMixin],
  props: {
    value: { type: Object, default: () => ({ month: 0, year: 0 }) },
    validator: { type: Function, default: () => () => true },
  },
  data() {
    return {
      monthMode: true,
      yearIndex: 0,
      yearGroupIndex: 0,
      onSpaceOrEnter,
    };
  },
  computed: {
    month() {
      return this.value ? this.value.month || 0 : 0;
    },
    year() {
      return this.value ? this.value.year || 0 : 0;
    },
    title() {
      return this.monthMode
        ? this.yearIndex
        : `${this.firstYear} - ${this.lastYear}`;
    },
    monthItems() {
      const { month: thisMonth, year: thisYear } = pageForDate(new Date());
      return this.locale.getMonthDates().map((d, i) => {
        const month = i + 1;
        return {
          label: this.locale.format(d, this.masks.navMonths),
          ariaLabel: this.locale.format(d, 'MMMM YYYY'),
          isActive: month === this.month && this.yearIndex === this.year,
          isCurrent: month === thisMonth && this.yearIndex === thisYear,
          isDisabled: !this.validator({ month, year: this.yearIndex }),
          click: () => this.monthClick(month),
        };
      });
    },
    yearItems() {
      const { _, year: thisYear } = pageForDate(new Date());
      const startYear = this.yearGroupIndex * _yearGroupCount;
      const endYear = startYear + _yearGroupCount;
      const items = [];
      for (let year = startYear; year < endYear; year += 1) {
        items.push({
          year,
          label: year,
          ariaLabel: year,
          isActive: year === this.year,
          isCurrent: year === thisYear,
          isDisabled: !this.validator({ month: this.month, year }),
          click: () => this.yearClick(year),
        });
      }
      return items;
    },
    activeItems() {
      return this.monthMode ? this.monthItems : this.yearItems;
    },
    firstYear() {
      return head(this.yearItems.map(i => i.year));
    },
    lastYear() {
      return last(this.yearItems.map(i => i.year));
    },
  },
  watch: {
    year() {
      this.yearIndex = this.year;
    },
    yearIndex(val) {
      this.yearGroupIndex = this.getYearGroupIndex(val);
    },
  },
  created() {
    this.yearIndex = this.year;
  },
  mounted() {
    this.$refs.itemsGrid.tryFocus();
  },
  methods: {
    getItemClasses({ isActive, isCurrent, isDisabled }) {
      const classes = ['vc-nav-item'];
      if (isActive) {
        classes.push('is-active', 'vc-grid-focus');
      } else if (isCurrent) {
        classes.push('is-inactive-current');
      } else {
        classes.push('is-inactive');
      }
      if (isDisabled) {
        classes.push('is-disabled');
      }
      return classes;
    },
    getYearGroupIndex(year) {
      return Math.floor(year / _yearGroupCount);
    },
    monthClick(month) {
      this.$emit('input', { month, year: this.yearIndex });
    },
    yearClick(year) {
      this.yearIndex = year;
      this.monthMode = true;
      this.$refs.itemsGrid.tryFocus();
    },
    toggleMode() {
      this.monthMode = !this.monthMode;
    },
    movePrev() {
      if (this.monthMode) {
        this.movePrevYear();
      }
      this.movePrevYearGroup();
    },
    moveNext() {
      if (this.monthMode) {
        this.moveNextYear();
      }
      this.moveNextYearGroup();
    },
    movePrevYear() {
      this.yearIndex--;
    },
    moveNextYear() {
      this.yearIndex++;
    },
    movePrevYearGroup() {
      this.yearGroupIndex--;
    },
    moveNextYearGroup() {
      this.yearGroupIndex++;
    },
    onHeaderRollover(e) {
      switch (e.direction) {
        case 'vertical-trailing':
          this.$refs.itemsGrid.tryFocus();
          break;
      }
      e.handled = true;
    },
    onItemsRollover(e) {
      switch (e.direction) {
        case 'horizontal-leading': {
          this.movePrev();
          break;
        }
        case 'horizontal-trailing': {
          this.moveNext();
          break;
        }
        case 'vertical-leading': {
          this.$refs.headerGrid.tryFocus();
          e.handled = true;
          break;
        }
        case 'vertical-trailing': {
          e.handled = true;
          break;
        }
      }
    },
  },
};
</script>

<style lang="postcss">
.vc-nav-arrow {
  display: flex;
  justify-content: center;
  align-items: center;
  line-height: var(--leading-snug);
  border-width: 2px;
  border-color: transparent;
  border-radius: var(--rounded);
  &.is-left {
    margin-right: auto;
  }
  &.is-right {
    margin-left: auto;
  }
  &:hover {
    background-color: var(--gray-900);
  }
  &:focus {
    border-color: var(--accent-600);
  }
}

.vc-nav-title {
  color: var(--accent-100);
  font-weight: var(--font-bold);
  line-height: var(--leading-snug);
  padding: 4px 8px;
  border-radius: var(--rounded);
  border-width: 2px;
  border-color: transparent;
  &:hover {
    background-color: var(--gray-900);
  }
  &:focus {
    border-color: var(--accent-600);
  }
}

.vc-nav-item {
  width: 48px;
  text-align: center;
  line-height: var(--leading-snug);
  font-weight: var(--font-semibold);
  padding: 4px 0;
  cursor: pointer;
  border-color: transparent;
  border-width: 2px;
  border-radius: var(--rounded);
  &:hover {
    color: var(--white);
    background-color: var(--gray-900);
    box-shadow: var(--shadow-inner);
  }
  &:focus {
    border-color: var(--accent-600);
  }
  &.is-active {
    color: var(--accent-900);
    background: var(--accent-100);
    border-color: transparent;
    font-weight: var(--font-bold);
    box-shadow: var(--shadow);
  }
  &.is-inactive {
    border-color: transparent;
  }
  &:is-inactive-current {
    color: var(--accent-100);
    font-weight: var(--bold);
    border-color: var(--accent-100);
  }
  &.is-disabled {
    opacity: 0.25;
    pointer-events: none;
  }
}

.vc-is-dark {
  & .vc-nav-title {
    color: var(--gray-900);
    &:hover {
      background-color: var(--gray-200);
    }
    &:focus {
      border-color: var(--accent-400);
    }
  }
  & .vc-nav-arrow {
    &:hover {
      background-color: var(--gray-200);
    }
    &:focus {
      border-color: var(--accent-400);
    }
  }
  & .vc-nav-item {
    &:hover {
      color: var(--gray-900);
      background-color: var(--gray-200);
      box-shadow: none;
    }
    &:focus {
      border-color: var(--accent-400);
    }
    &.is-active {
      color: var(--white);
      background: var(--accent-500);
    }
    &.is-inactive-current {
      color: var(--accent-600);
      border-color: var(--accent-500);
    }
  }
}
</style>

<template>
  <!--Nav panel-->
  <div class="vc-nav-container">
    <!--Nav header-->
    <grid :columns="3" ref="headerGrid" @rollover="onHeaderRollover">
      <!--Move prev button-->
      <span
        role="button"
        class="vc-nav-arrow is-left"
        tabindex="-1"
        @click="movePrev"
        @keydown="e => onSpaceOrEnter(e, movePrev)"
        ref="prevButton"
      >
        <slot name="nav-left-button">
          <svg-icon name="left-arrow" width="20px" height="24px" />
        </slot>
      </span>
      <!--Mode switch button-->
      <span
        role="button"
        class="vc-nav-title vc-grid-focus"
        :style="{ whiteSpace: 'nowrap' }"
        tabindex="0"
        @click="toggleMode"
        @keydown="e => onSpaceOrEnter(e, toggleMode)"
        ref="titleButton"
      >
        {{ title }}
      </span>
      <!--Move next button-->
      <span
        role="button"
        class="vc-nav-arrow is-right"
        tabindex="-1"
        @click="moveNext"
        @keydown="e => onSpaceOrEnter(e, moveNext)"
        ref="nextButton"
      >
        <slot name="nav-right-button">
          <svg-icon name="right-arrow" width="20px" height="24px" />
        </slot>
      </span>
    </grid>
    <!--Navigation items-->
    <grid
      :rows="4"
      :columns="3"
      gap="2px 5px"
      ref="itemsGrid"
      @rollover="onItemsRollover"
    >
      <span
        v-for="item in activeItems"
        :key="item.label"
        role="button"
        :aria-label="item.ariaLabel"
        :class="getItemClasses(item)"
        :tabindex="item.isDisabled ? undefined : item.isActive ? 0 : -1"
        @click="item.click"
        @keydown="e => onSpaceOrEnter(e, item.click)"
        ref="items"
      >
        {{ item.label }}
      </span>
    </grid>
  </div>
</template>

<script>
import Grid from './Grid';
import SvgIcon from './SvgIcon';
import { childMixin } from '../utils/mixins';
import { head, last } from '../utils/_';
import { pageForDate, onSpaceOrEnter } from '../utils/helpers';

const _yearGroupCount = 12;

export default {
  name: 'CalendarNav',
  components: {
    Grid,
    SvgIcon,
  },
  mixins: [childMixin],
  props: {
    value: { type: Object, default: () => ({ month: 0, year: 0 }) },
    validator: { type: Function, default: () => () => true },
  },
  data() {
    return {
      monthMode: true,
      yearIndex: 0,
      yearGroupIndex: 0,
      onSpaceOrEnter,
    };
  },
  computed: {
    month() {
      return this.value ? this.value.month || 0 : 0;
    },
    year() {
      return this.value ? this.value.year || 0 : 0;
    },
    title() {
      return this.monthMode
        ? this.yearIndex
        : `${this.firstYear} - ${this.lastYear}`;
    },
    monthItems() {
      const { month: thisMonth, year: thisYear } = pageForDate(new Date());
      return this.locale.getMonthDates().map((d, i) => {
        const month = i + 1;
        return {
          label: this.locale.format(d, this.masks.navMonths),
          ariaLabel: this.locale.format(d, 'MMMM YYYY'),
          isActive: month === this.month && this.yearIndex === this.year,
          isCurrent: month === thisMonth && this.yearIndex === thisYear,
          isDisabled: !this.validator({ month, year: this.yearIndex }),
          click: () => this.monthClick(month),
        };
      });
    },
    yearItems() {
      const { _, year: thisYear } = pageForDate(new Date());
      const startYear = this.yearGroupIndex * _yearGroupCount;
      const endYear = startYear + _yearGroupCount;
      const items = [];
      for (let year = startYear; year < endYear; year += 1) {
        items.push({
          year,
          label: year,
          ariaLabel: year,
          isActive: year === this.year,
          isCurrent: year === thisYear,
          isDisabled: !this.validator({ month: this.month, year }),
          click: () => this.yearClick(year),
        });
      }
      return items;
    },
    activeItems() {
      return this.monthMode ? this.monthItems : this.yearItems;
    },
    firstYear() {
      return head(this.yearItems.map(i => i.year));
    },
    lastYear() {
      return last(this.yearItems.map(i => i.year));
    },
  },
  watch: {
    year() {
      this.yearIndex = this.year;
    },
    yearIndex(val) {
      this.yearGroupIndex = this.getYearGroupIndex(val);
    },
  },
  created() {
    this.yearIndex = this.year;
  },
  mounted() {
    this.$refs.itemsGrid.tryFocus();
  },
  methods: {
    getItemClasses({ isActive, isCurrent, isDisabled }) {
      const classes = ['vc-nav-item'];
      if (isActive) {
        classes.push('is-active', 'vc-grid-focus');
      } else if (isCurrent) {
        classes.push('is-inactive-current');
      } else {
        classes.push('is-inactive');
      }
      if (isDisabled) {
        classes.push('is-disabled');
      }
      return classes;
    },
    getYearGroupIndex(year) {
      return Math.floor(year / _yearGroupCount);
    },
    monthClick(month) {
      this.$emit('input', { month, year: this.yearIndex });
    },
    yearClick(year) {
      this.yearIndex = year;
      this.monthMode = true;
      this.$refs.itemsGrid.tryFocus();
    },
    toggleMode() {
      this.monthMode = !this.monthMode;
    },
    movePrev() {
      if (this.monthMode) {
        this.movePrevYear();
      }
      this.movePrevYearGroup();
    },
    moveNext() {
      if (this.monthMode) {
        this.moveNextYear();
      }
      this.moveNextYearGroup();
    },
    movePrevYear() {
      this.yearIndex--;
    },
    moveNextYear() {
      this.yearIndex++;
    },
    movePrevYearGroup() {
      this.yearGroupIndex--;
    },
    moveNextYearGroup() {
      this.yearGroupIndex++;
    },
    onHeaderRollover(e) {
      switch (e.direction) {
        case 'vertical-trailing':
          this.$refs.itemsGrid.tryFocus();
          break;
      }
      e.handled = true;
    },
    onItemsRollover(e) {
      switch (e.direction) {
        case 'horizontal-leading': {
          this.movePrev();
          break;
        }
        case 'horizontal-trailing': {
          this.moveNext();
          break;
        }
        case 'vertical-leading': {
          this.$refs.headerGrid.tryFocus();
          e.handled = true;
          break;
        }
        case 'vertical-trailing': {
          e.handled = true;
          break;
        }
      }
    },
  },
};
</script>

<style lang="postcss">
.vc-nav-arrow {
  display: flex;
  justify-content: center;
  align-items: center;
  line-height: var(--leading-snug);
  border-width: 2px;
  border-color: transparent;
  border-radius: var(--rounded);
  &.is-left {
    margin-right: auto;
  }
  &.is-right {
    margin-left: auto;
  }
  &:hover {
    background-color: var(--gray-900);
  }
  &:focus {
    border-color: var(--accent-600);
  }
}

.vc-nav-title {
  color: var(--accent-100);
  font-weight: var(--font-bold);
  line-height: var(--leading-snug);
  padding: 4px 8px;
  border-radius: var(--rounded);
  border-width: 2px;
  border-color: transparent;
  &:hover {
    background-color: var(--gray-900);
  }
  &:focus {
    border-color: var(--accent-600);
  }
}

.vc-nav-item {
  width: 48px;
  text-align: center;
  line-height: var(--leading-snug);
  font-weight: var(--font-semibold);
  padding: 4px 0;
  cursor: pointer;
  border-color: transparent;
  border-width: 2px;
  border-radius: var(--rounded);
  &:hover {
    color: var(--white);
    background: var(--gray-900);
    box-shadow: var(--shadow-inner);
  }
  &:focus {
    border-color: var(--accent-600);
  }
  &.is-active {
    color: var(--accent-900);
    background: var(--accent-100);
    border-color: transparent;
    font-weight: var(--font-bold);
    box-shadow: var(--shadow);
  }
  &.is-inactive {
    border-color: transparent;
  }
  &:is-inactive-current {
    color: var(--accent-100);
    font-weight: var(--bold);
    border-color: var(--accent-100);
  }
  &.is-disabled {
    opacity: 0.25;
    pointer-events: none;
  }
}

.is-dark {
  & .vc-nav-title {
    color: var(--gray-900);
    &:hover {
      background-color: var(--gray-200);
    }
    &:focus {
      border-color: var(--accent-400);
    }
  }
  & .vc-nav-arrow {
    &:hover {
      background-color: var(--gray-200);
    }
    &:focus {
      border-color: var(--accent-400);
    }
  }
  & .vc-nav-item {
    &:hover {
      color: var(--gray-900);
      background-color: var(--gray-200);
    }
    &:focus {
      border-color: var(--accent-400);
    }
    &.is-active {
      color: var(--white);
      background: var(--accent-500);
    }
    &.is-inactive-current {
      color: var(--accent-600);
      border-color: var(--accent-500);
    }
  }
}
</style>


- Updated the function names to the new standard - Removed Join Channel Icon - Added in a function to parse the channel modes from the channel topic - Removed table header as not needed - Centered the 'channel search' using the CSS3 Calc function, rather than a fixed int

<template>

                <form class="u-form kiwi-channellist-search" @submit.prevent>
                    <input v-model="search" :placeholder="$t('do_search')" class="u-input" >

                </form>

                    {{ page + 1 }} / {{ maxPages + 1 }}

 0" :key="last_updated" width="100%">

                            = 0" class="kiwi-channellist-users">
                                 {{ channel.num_users }}

                                {{ channel.channel }}

                        {{ trimTopic(channel.topic) }}

                             {{ $t('container_join') }}

                {{ $t('channel_list_nonefound') }}

{{ $t('channel_list_fetch') }}

</template>

<template>

                <form class="u-form kiwi-channellist-search" @submit.prevent>
                    <input v-model="search" :placeholder="$t('do_search')" class="u-input" >

                </form>

                    {{ page + 1 }} / {{ maxPages + 1 }}

 0" :key="last_updated" width="100%">

                    Users
                    Channel name
                    Channel topic
                    Join Channel

                            = 0" class="kiwi-channellist-users">
                                 {{ channel.num_users }}

                                {{ channel.channel }}

                        {{ channel.topic }}

                            Join

                {{ $t('channel_list_nonefound') }}

{{ $t('channel_list_fetch') }}

</template>


Fix MDL buttons & update to use Vuex

Fix MDL buttons & update to use Vuex

<template>

      <MdlButton class="mdl-button mdl-js-button mdl-color--blue-grey-100 mdl-js-ripple-effect"
        onclick="document.getElementById('kd').scrollIntoView(true);"
        text="KD Ratio">
      </MdlButton>

      <MdlButton class="mdl-button mdl-js-button mdl-color--blue-grey-100 mdl-js-ripple-effect"
        onclick="document.getElementById('kills').scrollIntoView(true);"
        text="Kills">
      </MdlButton>

      <MdlButton class="mdl-button mdl-js-button mdl-color--blue-grey-100 mdl-js-ripple-effect"
        onclick="document.getElementById('deaths').scrollIntoView(true);"
        text="Deaths">
      </MdlButton>

      <MdlButton class="mdl-button mdl-js-button mdl-color--blue-grey-100 mdl-js-ripple-effect"
        onclick="document.getElementById('activity').scrollIntoView(true);"
        text="Activity">
      </MdlButton>

      <MdlButton class="mdl-button mdl-js-button mdl-color--blue-grey-100 mdl-js-ripple-effect"
        onclick="document.getElementById('scores').scrollIntoView(true);"
        text="Scores">
      </MdlButton>

      <MdlButton class="mdl-button mdl-js-button mdl-color--blue-grey-100 mdl-js-ripple-effect"
        onclick="document.getElementById('pointsppt').scrollIntoView(true);"
        text="Points From PPT">
      </MdlButton>

      <MdlButton class="mdl-button mdl-js-button mdl-color--blue-grey-100 mdl-js-ripple-effect"
        onclick="document.getElementById('percentppt').scrollIntoView(true);"
        text="% Points From PPT">
      </MdlButton>

      <MdlButton class="mdl-button mdl-js-button mdl-color--blue-grey-100 mdl-js-ripple-effect"
        onclick="document.getElementById('percentppk').scrollIntoView(true);"
        text="% Points From PPK">
      </MdlButton>

        Kill/Death Ratio

        <MdlButton class="mdl-button mdl-js-button mdl-color--blue-grey-100 mdl-js-ripple-effect"
          onclick="document.getElementById('lb-nav').scrollIntoView(true);"
          text="Back to Nav">
        </MdlButton>

            Rank
            Server
            K/D Ratio

              {{index + 1}}

              {{ getWorldById(item.server).name }}

              {{ item.kd | round(3, false)}}

        Kills

        <MdlButton class="mdl-button mdl-js-button mdl-color--blue-grey-100 mdl-js-ripple-effect"
          onclick="document.getElementById('lb-nav').scrollIntoView(true);"
          text="Back to Nav">
        </MdlButton>

            Rank
            Server
            Kills

              {{index + 1}}

              {{ getWorldById(item.server).name }}

              {{ item.kills | round(3, false)}}

        Deaths

        <MdlButton class="mdl-button mdl-js-button mdl-color--blue-grey-100 mdl-js-ripple-effect"
          onclick="document.getElementById('lb-nav').scrollIntoView(true);"
          text="Back to Nav">
        </MdlButton>

            Rank
            Server
            Deaths

              {{index + 1}}

              {{ getWorldById(item.server).name }}

              {{ item.deaths | round(3, false)}}

        Activity

        <MdlButton class="mdl-button mdl-js-button mdl-color--blue-grey-100 mdl-js-ripple-effect"
          onclick="document.getElementById('lb-nav').scrollIntoView(true);"
          text="Back to Nav">
        </MdlButton>

            Rank
            Server
            Activity

              {{index + 1}}

              {{ getWorldById(item.server).name }}

              {{ item.activity | round(3, false)}}

        Scores

        <MdlButton class="mdl-button mdl-js-button mdl-color--blue-grey-100 mdl-js-ripple-effect"
          onclick="document.getElementById('lb-nav').scrollIntoView(true);"
          text="Back to Nav">
        </MdlButton>

            Rank
            Server
            Score

              {{index + 1}}

              {{ getWorldById(item.server).name }}

              {{ item.score | round(3, false)}}

        Points from PPT

        <MdlButton class="mdl-button mdl-js-button mdl-color--blue-grey-100 mdl-js-ripple-effect"
          onclick="document.getElementById('lb-nav').scrollIntoView(true);"
          text="Back to Nav">
        </MdlButton>

            Rank
            Server
            Points

              {{index + 1}}

              {{ getWorldById(item.server).name }}

              {{ item.points | round(3, false)}}

        Percentage of Points from PPT

        <MdlButton class="mdl-button mdl-js-button mdl-color--blue-grey-100 mdl-js-ripple-effect"
          onclick="document.getElementById('lb-nav').scrollIntoView(true);"
          text="Back to Nav">
        </MdlButton>

            Rank
            Server
            Percent

              {{index + 1}}

              {{ getWorldById(item.server).name }}

              {{ item.percent * 100 | round(2, true)}}%

        Percentage of Points from PPK

        <MdlButton class="mdl-button mdl-js-button mdl-color--blue-grey-100 mdl-js-ripple-effect"
          onclick="document.getElementById('lb-nav').scrollIntoView(true);"
          text="Back to Nav">
        </MdlButton>

            Rank
            Server
            Percent

              {{index + 1}}

              {{ getWorldById(item.server).name }}

              {{ item.percent * 100 | round(2, true)}}%

</template>

<template>

      <button v-mdl class="mdl-button mdl-js-button mdl-color--blue-grey-100 mdl-js-ripple-effect"
        onclick="document.getElementById('kd').scrollIntoView(true);">
        KD Ratio
      </button>

      <button v-mdl class="mdl-button mdl-js-button mdl-color--blue-grey-100 mdl-js-ripple-effect"
        onclick="document.getElementById('kills').scrollIntoView(true);">
        Kills
      </button>

      <button v-mdl class="mdl-button mdl-js-button mdl-color--blue-grey-100 mdl-js-ripple-effect"
        onclick="document.getElementById('deaths').scrollIntoView(true);">
        Deaths
      </button>

      <button v-mdl class="mdl-button mdl-js-button mdl-color--blue-grey-100 mdl-js-ripple-effect"
        onclick="document.getElementById('activity').scrollIntoView(true);">
        Activity
      </button>

      <button v-mdl class="mdl-button mdl-js-button mdl-color--blue-grey-100 mdl-js-ripple-effect"
        onclick="document.getElementById('scores').scrollIntoView(true);">
        Scores
      </button>

      <button v-mdl class="mdl-button mdl-js-button mdl-color--blue-grey-100 mdl-js-ripple-effect"
        onclick="document.getElementById('pointsppt').scrollIntoView(true);">
        Points From PPT
      </button>

      <button v-mdl class="mdl-button mdl-js-button mdl-color--blue-grey-100 mdl-js-ripple-effect"
        onclick="document.getElementById('percentppt').scrollIntoView(true);">
        % Points From PPT
      </button>

      <button v-mdl class="mdl-button mdl-js-button mdl-color--blue-grey-100 mdl-js-ripple-effect"
        onclick="document.getElementById('percentppk').scrollIntoView(true);">
        % Points From PPK
      </button>

        Kill/Death Ratio

        <button v-mdl class="mdl-button mdl-js-button mdl-color--blue-grey-100 mdl-js-ripple-effect"
          onclick="document.getElementById('lb-nav').scrollIntoView(true);">
          Back to Nav
        </button>

            Rank
            Server
            K/D Ratio

              {{index + 1}}

              {{ getWorldById(item.server).name }}

              {{ item.kd | round(3, false)}}

        Kills

        <button v-mdl class="mdl-button mdl-js-button mdl-color--blue-grey-100 mdl-js-ripple-effect"
          onclick="document.getElementById('lb-nav').scrollIntoView(true);">
          Back to Nav
        </button>

            Rank
            Server
            Kills

              {{index + 1}}

              {{ getWorldById(item.server).name }}

              {{ item.kills | round(3, false)}}

        Deaths

        <button v-mdl class="mdl-button mdl-js-button mdl-color--blue-grey-100 mdl-js-ripple-effect"
          onclick="document.getElementById('lb-nav').scrollIntoView(true);">
          Back to Nav
        </button>

            Rank
            Server
            Deaths

              {{index + 1}}

              {{ getWorldById(item.server).name }}

              {{ item.deaths | round(3, false)}}

        Activity

        <button v-mdl class="mdl-button mdl-js-button mdl-color--blue-grey-100 mdl-js-ripple-effect"
          onclick="document.getElementById('lb-nav').scrollIntoView(true);">
          Back to Nav
        </button>

            Rank
            Server
            Activity

              {{index + 1}}

              {{ getWorldById(item.server).name }}

              {{ item.activity | round(3, false)}}

        Scores

        <button v-mdl class="mdl-button mdl-js-button mdl-color--blue-grey-100 mdl-js-ripple-effect"
          onclick="document.getElementById('lb-nav').scrollIntoView(true);">
          Back to Nav
        </button>

            Rank
            Server
            Score

              {{index + 1}}

              {{ getWorldById(item.server).name }}

              {{ item.score | round(3, false)}}

        Points from PPT

        <button v-mdl class="mdl-button mdl-js-button mdl-color--blue-grey-100 mdl-js-ripple-effect"
          onclick="document.getElementById('lb-nav').scrollIntoView(true);">
          Back to Nav
        </button>

            Rank
            Server
            Points

              {{index + 1}}

              {{ getWorldById(item.server).name }}

              {{ item.points | round(3, false)}}

        Percentage of Points from PPT

        <button v-mdl class="mdl-button mdl-js-button mdl-color--blue-grey-100 mdl-js-ripple-effect"
          onclick="document.getElementById('lb-nav').scrollIntoView(true);">
          Back to Nav
        </button>

            Rank
            Server
            Percent

              {{index + 1}}

              {{ getWorldById(item.server).name }}

              {{ item.percent * 100 | round(2, true)}}%

        Percentage of Points from PPK

        <button v-mdl class="mdl-button mdl-js-button mdl-color--blue-grey-100 mdl-js-ripple-effect"
          onclick="document.getElementById('lb-nav').scrollIntoView(true);">
          Back to Nav
        </button>

            Rank
            Server
            Percent

              {{index + 1}}

              {{ getWorldById(item.server).name }}

              {{ item.percent * 100 | round(2, true)}}%

</template>


修改不换行

修改不换行

<template>

tags

共计{{labels && labels.length}}个标签

            <router-link :style="{ fontSize: Math.ceil(Math.random()*10) + 12 + 'px' }" replace :to="{path: 'labelHistory',query: {id:el._id,name: el.content}}" v-for="el in labels">{{el.content}}</router-link>

</template>


delete: 不要な記述を削除

delete: 不要な記述を削除

<template>
  <div class="drawer-container">
    <input id="drawer-trigger" class="drawer-checkbox" type="checkbox" @click="toggeleDrawer">
    <label for="drawer-trigger" class="drawer-trigger icon-menu"/>
  </div>
</template>

<script>
export default {
  name: 'Drawer',
  methods: {
    toggeleDrawer() {
      this.$store.commit('toggleSidebar');
    },
  },
};
</script>

<style lang="scss" scoped>
.icon-menu {
  background-image: url('~@images/icon/menu.svg?fill=#{$grey-700} svg');
  cursor: pointer;
  @include svg-icon(1.5rem);
}
</style>

<template>
  <div class="drawer-container">
    <input id="drawer-trigger" class="drawer-checkbox" type="checkbox" @click="toggeleDrawer">
    <label for="drawer-trigger" class="drawer-trigger icon-menu" v-html="iconMenu"/>
  </div>
</template>

<script>
export default {
  name: 'Drawer',
  methods: {
    toggeleDrawer() {
      this.$store.commit('toggleSidebar');
    },
  },
};
</script>

<style lang="scss" scoped>
.icon-menu {
  background-image: url('~@images/icon/menu.svg?fill=#{$grey-700} svg');
  cursor: pointer;
  @include svg-icon(1.5rem);
}
</style>


updated conditional so it will close onoutside click with explicit model

<template lang="pug">
  div(class="modal__container")

    div(
      class="modal__activator"
      v-on:click="isActive = !isActive"
      ref="activator"
      v-if="$slots.activator"
    )
      slot(name="activator")

    v-overlay(
      v-model="isActive"
      v-bind:class="overlayClasses"
    )
      component(
        v-bind:is="computedTransition"
        v-bind:origin="computedOrigin"
      )
        div(
          class="modal"
          v-bind:class="classes"
          v-bind:id="id"
          v-show="isActive"
          v-click-outside="closeConditional"
          ref="modal"
        )
          slot
</template>

<script>
  import Toggleable from '../../mixins/toggleable'

  export default {
    name: 'modal',

    mixins: [Toggleable],

    props: {
      bottom: Boolean,
      origin: {
        type: String,
        default: 'center center'
      },
      persistent: Boolean,
      transition: {
        type: String,
        default: 'v-modal-transition'
      }
    },

    computed: {
      classes () {
        return {
          'modal--active': this.isActive,
          'modal--bottom': this.bottom
        }
      },

      computedOrigin () {
        if (this.origin !== 'center center') {
          return this.origin
        }

        return this.bottom ? 'bottom' : this.origin
      },

      computedTransition () {
        if (this.transition !== 'v-modal-transition') {
          return this.transition
        }

        return this.bottom ? 'v-slide-y-transition' : this.transition
      },

      overlayClasses () {
        return {
          'overlay--modal-bottom': this.bottom
        }
      }
    },

    methods: {
      closeConditional (e) {
        if (this.persistent) {
          return false
        }

        return this.$refs.modal !== e.target &&
          !this.$refs.modal.contains(e.target) &&
          (!this.$refs.activator || this.$refs.activator &&
            !this.$refs.activator.contains(e.target) &&
            this.$refs.activator !== e.target)
      }
    }
  }
</script>

<template lang="pug">
  div(class="modal__container")

    div(
      class="modal__activator"
      v-on:click="isActive = !isActive"
      ref="activator"
      v-if="$slots.activator"
    )
      slot(name="activator")

    v-overlay(
      v-model="isActive"
      v-bind:class="overlayClasses"
    )
      component(
        v-bind:is="computedTransition"
        v-bind:origin="computedOrigin"
      )
        div(
          class="modal"
          v-bind:class="classes"
          v-bind:id="id"
          v-show="isActive"
          v-click-outside="closeConditional"
          ref="modal"
        )
          slot
</template>

<script>
  import Toggleable from '../../mixins/toggleable'

  export default {
    name: 'modal',

    mixins: [Toggleable],

    props: {
      bottom: Boolean,
      origin: {
        type: String,
        default: 'center center'
      },
      persistent: Boolean,
      transition: {
        type: String,
        default: 'v-modal-transition'
      }
    },

    computed: {
      classes () {
        return {
          'modal--active': this.isActive,
          'modal--bottom': this.bottom
        }
      },

      computedOrigin () {
        if (this.origin !== 'center center') {
          return this.origin
        }

        return this.bottom ? 'bottom' : this.origin
      },

      computedTransition () {
        if (this.transition !== 'v-modal-transition') {
          return this.transition
        }

        return this.bottom ? 'v-slide-y-transition' : this.transition
      },

      overlayClasses () {
        return {
          'overlay--modal-bottom': this.bottom
        }
      }
    },

    methods: {
      closeConditional (e) {
        if (this.persistent) {
          return false
        }

        return this.$refs.modal !== e.target &&
          !this.$refs.modal.contains(e.target) &&
          (this.$refs.activator &&
            !this.$refs.activator.contains(e.target) &&
            this.$refs.activator !== e.target)
      }
    }
  }
</script>


Fix manage shot modal

Fix manage shot modal

Problem: The manage shot modal is closed when a shot is created
Solution: Do not close modal after data reload (which occurs when current episode changes)

<template>

              <search-field
                ref="shot-search-field"
                :can-save="true"
                @change="onSearchChange"
                @save="saveSearchQuery"
                placeholder="ex: e01 s01 anim=wip"
              />

              <show-assignations-button class="flexrow-item" />
              <show-infos-button class="flexrow-item" />

              <button-link
                class="flexrow-item"
                :title="$t('main.csv.import_file')"
                icon="upload"
                :is-responsive="true"
                :path="importPath"
              />
              <button-href-link
                class="flexrow-item"
                :title="$t('main.csv.export_file')"
                icon="download"
                :is-responsive="true"
                :path="'/api/export/csv/projects/' + currentProduction.id + '/shots.csv'"
              />
              <button-simple
                class="flexrow-item"
                :text="$t('shots.manage')"
                icon="plus"
                :is-responsive="true"
                :path="manageShotsPath"
                @click="showManageShots"
              />

          <search-query-list
            :queries="shotSearchQueries"
            @changesearch="changeSearch"
            @removesearch="removeSearchQuery"
            v-if="!isShotsLoading && !initialLoading"
          />

      <shot-list
        ref="shot-list"
        :entries="displayedShots"
        :is-loading="isShotsLoading || initialLoading"
        :is-error="isShotsLoadingError"
        :validation-columns="shotValidationColumns"
        @scroll="saveScrollPosition"
        @delete-all-tasks="onDeleteAllTasksClicked"
        @add-metadata="onAddMetadataClicked"
        @delete-metadata="onDeleteMetadataClicked"
        @edit-metadata="onEditMetadataClicked"

      />

    <task-info
      :task="Object.values(selectedTasks)[0]"
    />

  <manage-shots-modal
    :active="modals.isManageDisplayed"
    :is-loading="loading.manage"
    :is-error="false"
    :is-success="false"
    :cancel-route="shotsPath"
    @cancel="hideManageShots"
  />

  <edit-shot-modal
    :active="modals.isNewDisplayed"
    :is-loading="loading.edit"
    :is-loading-stay="loading.stay"
    :is-error="editShot.isCreateError"
    :is-success="editShot.isSuccess"
    :cancel-route="shotsPath"
    :shot-to-edit="shotToEdit"
    @confirm="confirmEditShot"
  />

  <delete-modal
    ref="delete-shot-modal"
    :active="modals.isDeleteDisplayed"
    :is-loading="deleteShot.isLoading"
    :is-error="deleteShot.isError"
    :cancel-route="shotsPath"
    :text="deleteText()"
    :error-text="$t('shots.delete_error')"
    @confirm="confirmDeleteShot"
  />

  <delete-modal
    ref="restore-shot-modal"
    :active="modals.isRestoreDisplayed"
    :is-loading="restoreShot.isLoading"
    :is-error="restoreShot.isError"
    :cancel-route="shotsPath"
    :text="restoreText()"
    :error-text="$t('shots.restore_error')"
    @confirm="confirmRestoreShot"
  />

  <delete-modal
    ref="delete-metadata-modal"
    :active="modals.isDeleteMetadataDisplayed"
    :is-loading="loading.deleteMetadata"
    :is-error="errors.deleteMetadata"
    @cancel="modals.isDeleteMetadataDisplayed = false"
    :text="$t('productions.metadata.delete_text')"
    :error-text="$t('productions.metadata.delete_error')"
    @confirm="confirmDeleteMetadata"
  />

  <hard-delete-modal
    ref="delete-all-tasks-modal"
    :active="modals.isDeleteAllTasksDisplayed"
    :is-loading="loading.deleteAllTasks"
    :is-error="errors.deleteAllTasks"
    :cancel-route="shotsPath"
    :text="deleteAllTasksText()"
    :error-text="$t('tasks.delete_all_error')"
    :lock-text="deleteAllTasksLockText"
    @confirm="confirmDeleteAllTasks"
  />

  <import-modal
    :active="modals.isImportDisplayed"
    :is-loading="loading.importing"
    :is-error="errors.importing"
    :cancel-route="shotsPath"
    :form-data="shotsCsvFormData"
    :columns="columns"
    @fileselected="selectFile"
    @confirm="uploadImportFile"
  />

  <create-tasks-modal
    :active="modals.isCreateTasksDisplayed"
    :is-loading="loading.creatingTasks"
    :is-loading-stay="loading.creatingTasksStay"
    :is-error="errors.creatingTasks"
    :cancel-route="shotsPath"
    :title="$t('tasks.create_tasks_shot')"
    :text="$t('tasks.create_tasks_shot_explaination')"
    :error-text="$t('tasks.create_tasks_shot_failed')"
    @confirm="confirmCreateTasks"
    @confirm-and-stay="confirmCreateTasksAndStay"
  />

  <add-metadata-modal
    :active="modals.isAddMetadataDisplayed"
    :is-loading="loading.addMetadata"
    :is-loading-stay="loading.addMetadata"
    :is-error="errors.addMetadata"
    :descriptor-to-edit="descriptorToEdit"
    @cancel="closeMetadataModal"
    @confirm="confirmAddMetadata"
  />

</template>


fix live page

fix live page

<template>

        <video id="video-player" class="video-js">
            <source type="application/x-mpegURL" :src="room.hls_url">

您的浏览器不支持 video 标签

        </video>

                {{room.room_name}}
                {{room.tag_name}}

主播: {{room.nickname}}

共{{room.online}}人正在观看

分享

同类直播

        <like-live v-for="like in likeness | limitBy 4" :like="like"></like-live>

热门直播

        <hot-live v-for="hot in hots | limitBy 4" :hot="hot"></hot-live>

</template>

<template>

        <video id="dy-video-player" class="video-js" type="application/x-mpegURL" :src="room.hls_url">

您的浏览器不支持 video 标签

        </video>

                {{room.room_name}}
                {{room.tag_name}}

主播: {{room.nickname}}

共{{room.online}}人正在观看

分享

同类直播

        <like-live v-for="like in likeness | limitBy 4" :like="like"></like-live>

热门直播

        <hot-live v-for="hot in hots | limitBy 4" :hot="hot"></hot-live>

</template>


Fixed remove dev code

Fixed remove dev code

<template>

        {{ p.pinyin }}
        <speaker v-if="p.pron" :src="p.pron"></speaker>

</template>


Fixed font-style for center bubble (#702)

<template>

 { this.showInstructionalTooltip = false }"
          >

            {{ $t('cinematic.instructional_tooltip') }}

 userAction('backInteraction')"
          >

 {
              this.showInstructionalTooltip = false
              this.userAction('forwardInteraction')
            }"
          >

      <chalkboard />
      <canvas
        id="cinematic-canvas"
        ref="cinematic-canvas"
        :width="width"
        :height="height"
        :style="{ width: width+'px', height: height+'px' }"
      />

{{ $t("common.LOADING") }}

</template>


Restore responsive styling for attemptlog list.

<template>

      {{ $tr('answerHistoryLabel') }}

    <KSelect
      v-if="isMobile"
      class="history-select"
      :value="selected"
      aria-labelledby="answer-history-label"
      :options="options"
      :disabled="$attrs.disabled"
      @change="handleDropdownChange($event.value)"
    />

    <ul
      v-else
      ref="attemptList"
      class="history-list"
      :class="isMobile ? 'mobile-list' : ''"
      role="listbox"
      @keydown.home="setSelectedAttemptLog(0)"
      @keydown.end="setSelectedAttemptLog(attemptLogs.length - 1)"
      @keydown.up.prevent="setSelectedAttemptLog(previousQuestion(selectedQuestionNumber))"
      @keydown.left.prevent="setSelectedAttemptLog(previousQuestion(selectedQuestionNumber))"
      @keydown.down.prevent="setSelectedAttemptLog(nextQuestion(selectedQuestionNumber))"
      @keydown.right.prevent="setSelectedAttemptLog(nextQuestion(selectedQuestionNumber))"
    >
      <template v-for="(attemptLog, index) in attemptLogs">

              {{
                coreString(
                  'questionNumberLabel',
                  { questionNumber: attemptLog.questionNumber }
                )
              }}

              <AttemptIconDiff
                v-if="attemptLog.diff && attemptLog.diff.correct !== null"
                class="diff-item item"
                :correct="attemptLog.correct"
                :diff="attemptLog.diff.correct"
              />
              <KIcon
                v-if="attemptLog.noattempt"
                class="item svg-item"
                icon="notStarted"
              />
              <KIcon
                v-else-if="attemptLog.correct"
                class="item svg-item"
                :style="{ fill: $themeTokens.correct }"
                icon="correct"
              />
              <KIcon
                v-else-if="attemptLog.error"
                class="svg-item"
                :style=" { fill: $themeTokens.annotation }"
                icon="helpNeeded"
              />
              <KIcon
                v-else-if="!attemptLog.correct"
                class="item svg-item"
                :style="{ fill: $themeTokens.incorrect }"
                icon="incorrect"
              />
              <KIcon
                v-else-if="attemptLog.hinted"
                class="item svg-item"
                :style=" { fill: $themeTokens.annotation }"
                icon="hint"
              />

            <CoachContentLabel
              v-if="windowIsLarge"
              class="coach-content-label"
              :value="attemptLog.num_coach_contents || 0"
              :isTopic="false"
            />

      </template>

</template>


reverted indention changes

reverted indention changes

<template>

        <button aria-label="Go back" @click="emitBackEvent" class="header-btn btn-back" v-if="enablebackbtn">
          <svg src="./back.svg" role="presentation"></svg>
        </button>
        <button aria-label="Close dialog window" @click="emitCancelEvent" class="header-btn btn-close">
          <svg src="../icons/close.svg" role="presentation"></svg>
        </button>

        Error in:
        {{title}}

      <slot>

To populate, wrap your content in  with modal .

      </slot>

</template>