Implement super minimal working tabs component

<!--
  TAB GROUP COMPONENT

  README:
    TODO: Write me.

  USAGE:
    TODO: Write me.

-->

<!-- TODO: Add ARIA recommendations + role=tablist might not be correct on <nav> -->

<template>
  <div>
    <nav class="df mb4" role="tablist">
      <a
        v-for="(slot, name) in $slots"
        @click="active = name"
        :href="`#${name}`"
        class="btn-tab ttu"
        :class="{ 'active': active === name }"
        role="tab"
      >
        {{ slot[0].data.attrs.title }}
      </a>
    </nav>

    <div
      v-for="(slot, name) in $slots"
      v-show="active === name"
      :id="name"
      role="tabpanel"
    >
      <slot :name="slot[0].data.slot"/>
    </div>
  </div>
</template>

<script>
export default {
  name: 'TabGroup',
  data() {
    return {
      // XXX: Since hash is used, vue-router must not be running in hash mode.
      active: window.location.hash.substr(1) || Object.keys(this.$slots)[0],
    };
  },
};
</script>

<style>
@import "@wearegenki/ui/import";

.btn-tab {
  padding: 1rem 1.5rem;
  font-size: 1.2rem;
  font-weight: bold;
  border-bottom: 4px solid transparent;

  &:hover,
  &:focus {
    text-decoration: none;
    border-bottom-color: var(--grey-400);
  }

  &.active {
    border-bottom-color: var(--primary);
    cursor: default;
  }
}
</style>

<template>

      Tab title 1
      Tab title 2
      Tab title 3

      Content 1

      Content 2

      Content 3

</template>


Client logo positioning in client select

<template>
  <v-layout row wrap justify-center>
    <v-flex xs12 lg8 style="background: rgba(0,0,0,0.1); border-radius: 10px" class="pa-4">
      <v-layout row wrap justify-center>
        <v-flex xs12 md8 lg4 xl6>

        </v-flex>
      </v-layout>
      <v-stepper style="background: rgba(0,0,0,0.3); color: white !important" v-model="e1" class="mb-4">
        <v-stepper-header dark>
          <v-stepper-step step="1" dark :complete="true">Select a client</v-stepper-step>
          <v-divider></v-divider>
          <v-stepper-step step="2" dark :complete="false">Join a room</v-stepper-step>
          <v-divider></v-divider>
          <v-stepper-step step="3">Sync</v-stepper-step>
        </v-stepper-header>
      </v-stepper>

        <v-layout class="mt-2" row wrap>
          <v-flex xs12>

Choose your Plex player

          </v-flex>
          <v-flex xs12>
            Choose a client from the list below. Once you've found the client you would like to use, click the connect button. SyncLounge will test to see if it can connect with the client and will let you know if it cannot.
          </v-flex>
        </v-layout>
        <v-divider></v-divider>

          <v-progress-circular indeterminate color="primary"></v-progress-circular>

        <v-layout v-else row wrap>
          <v-flex xs12 md6 lg6 v-if="!doReverse">
            <v-subheader>Plex Players {{ playercount }}</v-subheader>
            <v-list dense style="background: none">
              <plexclient v-for="i in recentClients" :key="i.clientIdentifier" @click.native="previewClient(i); ; gotResponse = true" :selected="isClientSelected(i)" :object="i" style="cursor: pointer"></plexclient>
            </v-list>
          </v-flex>
          <v-flex xs12 md6 lg6>

              <v-subheader>
                Selected Player
              </v-subheader>
              <v-layout row wrap>
                <v-flex md3 class="text-xs-center" style="position: relative">

                </v-flex>
                <v-flex xs12 md9>

{{ testClient.name }}

                      Last seen  {{ lastSeenAgo(testClient.lastSeenAt) }}

                      Device  {{ testClient.device }}

                      Running  {{ testClient.product }}

                      Platform  {{ testClient.platform }}

 -1" class="warning--text">
                      Note: Plex Web is currently not supported

                      Note: You may not be able to connect to external Plex Clients while loading the page via HTTPS. Click here to load the page via HTTP. More info here.

                      {{ testClientErrorMsg }}

                </v-flex>
              </v-layout>
              <v-layout row wrap class="pt-2">
                <v-flex xs12>

                        <v-progress-circular indeterminate v-bind:size="50" class="amber--text" style="display:inline-block"></v-progress-circular>

                      <v-btn block color="primary" v-on:click.native="clientClicked()">Connect</v-btn>

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

          </v-flex>
          <v-flex xs12 md6 lg7 v-if="doReverse">
            <v-subheader>Plex Players {{ playercount }}</v-subheader>
            <v-list dense style="background: none">
              <plexclient v-for="i in recentClients" :key="i.clientIdentifier" @click.native="previewClient(i); ; gotResponse = true" :selected="isClientSelected(i)" :object="i" style="cursor: pointer"></plexclient>
            </v-list>
          </v-flex>
        </v-layout>

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

<template>
  <v-layout row wrap justify-center>
    <v-flex xs12 lg8 style="background: rgba(0,0,0,0.1); border-radius: 10px" class="pa-4">
      <v-layout row wrap justify-center>
        <v-flex xs12 md8 lg4 xl6>

        </v-flex>
      </v-layout>
      <v-stepper style="background: rgba(0,0,0,0.3); color: white !important" v-model="e1" class="mb-4">
        <v-stepper-header dark>
          <v-stepper-step step="1" dark :complete="true">Select a client</v-stepper-step>
          <v-divider></v-divider>
          <v-stepper-step step="2" dark :complete="false">Join a room</v-stepper-step>
          <v-divider></v-divider>
          <v-stepper-step step="3">Sync</v-stepper-step>
        </v-stepper-header>
      </v-stepper>

        <v-layout class="mt-2" row wrap>
          <v-flex xs12>

Choose your Plex player

          </v-flex>
          <v-flex xs12>
            Choose a client from the list below. Once you've found the client you would like to use, click the connect button. SyncLounge will test to see if it can connect with the client and will let you know if it cannot.
          </v-flex>
        </v-layout>
        <v-divider></v-divider>

          <v-progress-circular indeterminate color="primary"></v-progress-circular>

        <v-layout v-else row wrap>
          <v-flex xs12 md6 lg6 v-if="!doReverse">
            <v-subheader>Plex Players {{ playercount }}</v-subheader>
            <v-list dense style="background: none">
              <plexclient v-for="i in recentClients" :key="i.clientIdentifier" @click.native="previewClient(i); ; gotResponse = true" :selected="isClientSelected(i)" :object="i" style="cursor: pointer"></plexclient>
            </v-list>
          </v-flex>
          <v-flex xs12 md6 lg6>

              <v-subheader>
                Selected Player
              </v-subheader>
              <v-layout row wrap>
                <v-flex md3 class="text-xs-center">

                </v-flex>
                <v-flex xs12 md9>

{{ testClient.name }}

                      Last seen  {{ lastSeenAgo(testClient.lastSeenAt) }}

                      Device  {{ testClient.device }}

                      Running  {{ testClient.product }}

                      Platform  {{ testClient.platform }}

 -1" class="warning--text">
                      Note: Plex Web is currently not supported

                      Note: You may not be able to connect to external Plex Clients while loading the page via HTTPS. Click here to load the page via HTTP. More info here.

                      {{ testClientErrorMsg }}

                </v-flex>
              </v-layout>
              <v-layout row wrap class="pt-2">
                <v-flex xs12>

                        <v-progress-circular indeterminate v-bind:size="50" class="amber--text" style="display:inline-block"></v-progress-circular>

                      <v-btn block color="primary" v-on:click.native="clientClicked()">Connect</v-btn>

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

          </v-flex>
          <v-flex xs12 md6 lg7 v-if="doReverse">
            <v-subheader>Plex Players {{ playercount }}</v-subheader>
            <v-list dense style="background: none">
              <plexclient v-for="i in recentClients" :key="i.clientIdentifier" @click.native="previewClient(i); ; gotResponse = true" :selected="isClientSelected(i)" :object="i" style="cursor: pointer"></plexclient>
            </v-list>
          </v-flex>
        </v-layout>

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


Remove temp border change

Remove temp border change

<template>

%fa:users%%i18n:@title%

%fa:spinner .pulse .fw%%i18n:@loading%<mk-ellipsis/>

	<template v-if="!fetching && users.length != 0">

			<mk-avatar class="avatar" :user="friend"/>

				<router-link class="name" :to="friend | userPage" v-user-preview="friend.id">{{ friend.name }}</router-link>

@{{ friend | acct }}

			<mk-follow-button :user="friend"/>

	</template>

%i18n:@no-users%

</template>


:art:

:art:

<template>

		<mk-follow-button :user="user" block/>

{{ $t('follows-you') }}

		<ui-button @click="menu" ref="menu">{{ $t('menu') }}</ui-button>

</template>


SimulationKernel: Move functions and options to <script> section

<template>
  <div class="simulationKernel">
    <v-row class="full-height" no-gutters>
      <v-col>
        <v-sheet class="ma-1" color="primary" outlined>
          <v-card class="ml-1" outlined tile>
            <v-card-title class="pa-0">
              <v-btn
                :dark="projectView.config.coloredToolbar"
                :ripple="false"
                :text="!projectView.config.coloredToolbar"
                block
                color="primary"
                depressed
                flat
                height="40"
                tile
              >
                Simulation kernel
                <v-spacer />
              </v-btn>
            </v-card-title>

            <v-card-text class="pa-0">
              <ParameterEdit
                :options="options.threadSettings"
                :value.sync="simulation.kernel.localNumThreads"
                @update:value="paramChange"
                class="mx-1 py-2"
              />

              <ParameterEdit
                :options="options.resolutionSettings"
                :value.sync="simulation.kernel.resolution"
                @update:value="paramChange"
                class="mx-1 py-2"
              />

              <ParameterEdit
                :options="options.rngSeedSettings"
                :value.sync="simulation.kernel.rngSeed"
                @update:value="paramChange"
                class="mx-1 py-1"
              />

              <ParameterEdit
                :options="options.autoRNGSeedSettings"
                :value.sync="state.autoRNGSeed"
                class="mx-1"
                @update:value="updateAutoRNGSeed"
              />
            </v-card-text>
          </v-card>
        </v-sheet>

        <v-sheet class="ma-1" color="primary" outlined>
          <v-card class="ml-1" outlined tile>
            <v-card-title class="pa-0">
              <v-btn
                :dark="projectView.config.coloredToolbar"
                :ripple="false"
                :text="!projectView.config.coloredToolbar"
                block
                color="primary"
                depressed
                height="40"
                tile
              >
                Simulation
                <v-spacer />
              </v-btn>
            </v-card-title>

            <v-card-text class="pa-0">
              <ParameterEdit
                :options="options.simulationTimeSettings"
                :value.sync="simulation.time"
                @update:value="paramChange"
                class="mx-1 py-2"
              />
            </v-card-text>
          </v-card>
        </v-sheet>
      </v-col>
    </v-row>
  </div>
</template>

<script lang="ts">
import Vue from 'vue';
import { reactive, onMounted } from '@vue/composition-api';

import { Simulation } from '@/core/simulation/simulation';
import core from '@/core';
import ParameterEdit from '@/components/parameter/ParameterEdit.vue';

export default Vue.extend({
  name: 'SimulationKernel',
  components: {
    ParameterEdit,
  },
  props: {
    simulation: Simulation,
  },
  setup(props) {
    const options = {
      autoRNGSeedSettings: {
        input: 'checkbox',
        label: 'randomize seed',
      },
      resolutionSettings: {
        id: 'simulationResolution',
        input: 'tickSlider',
        label: 'simulation resolution',
        ticks: [0.01, 0.1, 1, 10],
        unit: 'ms',
        rules: [
          [
            'value < 1',
            'Small simulation resolution produces many data points which could cause a high system load and thus freezes and lags!',
            'warning',
          ],
        ],
      },
      rngSeedSettings: {
        input: 'valueSlider',
        label: 'seed of the random number generator',
        max: 1000,
        min: 1,
        value: 1,
      },
      simulationTimeSettings: {
        id: 'simulationTime',
        input: 'valueSlider',
        label: 'simulation time',
        max: 2000,
        min: 0,
        unit: 'ms',
        value: 1000,
        rules: [
          [
            'value >= 2000',
            'Large simulation time produces many data points which could cause a high system load and thus freezes and lags!',
            'warning',
          ],
        ],
      },
      threadSettings: {
        input: 'tickSlider',
        label: 'local number of threads',
        ticks: [1, 2, 4, 8],
      },
    };
    const projectView = core.app.project.view;
    const state = reactive({
      autoRNGSeed: false,
      simulation: props.simulation as Simulation,
    });

    /**
     * Triggers when parameter is changed.
     */
    const paramChange = () => {
      state.simulation.project.code.generate();
    };

    /**
     * Updates when the usage of automatic RNG seed is switched on/off.
     */
    function updateAutoRNGSeed() {
      state.simulation.kernel.updateConfig({
        autoRNGSeed: state.autoRNGSeed,
      });
    }

    onMounted(() => {
      state.simulation = props.simulation as Simulation;
      state.autoRNGSeed = state.simulation.kernel.config.autoRNGSeed;
    });

    return {
      options,
      paramChange,
      projectView,
      state,
      updateAutoRNGSeed,
    };
  },
});
</script>

<style>
.simulationKernel .v-sheet {
  border-color: #e0e0e0 !important;
  border-width: 1px 1px 1px 0;
}
.simulationKernel .v-card {
  border-width: 0;
}
</style>

<template>
  <div class="simulationKernel">
    <v-row class="full-height" no-gutters>
      <v-col>
        <v-sheet class="ma-1" color="primary" outlined>
          <v-card class="ml-1" outlined tile>
            <v-card-title class="pa-0">
              <v-btn
                :dark="projectView.config.coloredToolbar"
                :ripple="false"
                :text="!projectView.config.coloredToolbar"
                block
                color="primary"
                depressed
                flat
                height="40"
                tile
              >
                Simulation kernel
                <v-spacer />
              </v-btn>
            </v-card-title>

            <v-card-text class="pa-0">
              <ParameterEdit
                :options="{
                  input: 'tickSlider',
                  label: 'local number of threads',
                  ticks: [1, 2, 4, 8],
                }"
                :value.sync="simulation.kernel.localNumThreads"
                @update:value="paramChange"
                class="mx-1 py-2"
              />

              <ParameterEdit
                :options="{
                  id: 'simulationResolution',
                  input: 'tickSlider',
                  label: 'simulation resolution',
                  ticks: [0.01, 0.1, 1, 10],
                  unit: 'ms',
                  rules: [
                    [
                      'value < 1',
                      'Small simulation resolution produces many data points which could cause a high system load and thus freezes and lags!',
                      'warning',
                    ],
                  ],
                }"
                :value.sync="simulation.kernel.resolution"
                @update:value="paramChange"
                class="mx-1 py-2"
              />

              <ParameterEdit
                :options="{
                  input: 'valueSlider',
                  label: 'seed of the random number generator',
                  max: 1000,
                  min: 1,
                  value: 1,
                }"
                :value.sync="simulation.kernel.rngSeed"
                @update:value="paramChange"
                class="mx-1 py-1"
              />

              <ParameterEdit
                :options="{
                  input: 'checkbox',
                  label: 'randomize seed',
                }"
                :value.sync="state.autoRNGSeed"
                class="mx-1"
                @update:value="
                  state.simulation.kernel.updateConfig({
                    autoRNGSeed: state.autoRNGSeed,
                  })
                "
              />
            </v-card-text>
          </v-card>
        </v-sheet>

        <v-sheet class="ma-1" color="primary" outlined>
          <v-card class="ml-1" outlined tile>
            <v-card-title class="pa-0">
              <v-btn
                :dark="projectView.config.coloredToolbar"
                :ripple="false"
                :text="!projectView.config.coloredToolbar"
                block
                color="primary"
                depressed
                height="40"
                tile
              >
                Simulation
                <v-spacer />
              </v-btn>
            </v-card-title>

            <v-card-text class="pa-0">
              <ParameterEdit
                :options="{
                  id: 'simulationTime',
                  input: 'valueSlider',
                  label: 'simulation time',
                  max: 2000,
                  min: 0,
                  unit: 'ms',
                  value: 1000,
                  rules: [
                    [
                      'value >= 2000',
                      'Large simulation time produces many data points which could cause a high system load and thus freezes and lags!',
                      'warning',
                    ],
                  ],
                }"
                :value.sync="simulation.time"
                @update:value="paramChange"
                class="mx-1 py-2"
              />
            </v-card-text>
          </v-card>
        </v-sheet>
      </v-col>
    </v-row>
  </div>
</template>

<script lang="ts">
import Vue from 'vue';
import { reactive, onMounted } from '@vue/composition-api';

import { Simulation } from '@/core/simulation/simulation';
import core from '@/core';
import ParameterEdit from '@/components/parameter/ParameterEdit.vue';

export default Vue.extend({
  name: 'SimulationKernel',
  components: {
    ParameterEdit,
  },
  props: {
    simulation: Simulation,
  },
  setup(props) {
    const projectView = core.app.project.view;
    const state = reactive({
      autoRNGSeed: false,
      simulation: props.simulation as Simulation,
    });

    /**
     * Triggers when parameter is changed.
     */
    const paramChange = () => {
      state.simulation.project.code.generate();
    };

    onMounted(() => {
      state.simulation = props.simulation as Simulation;
      state.autoRNGSeed = state.simulation.kernel.config.autoRNGSeed;
    });

    return {
      paramChange,
      projectView,
      state,
    };
  },
});
</script>

<style>
.simulationKernel .v-sheet {
  border-color: #e0e0e0 !important;
  border-width: 1px 1px 1px 0;
}
.simulationKernel .v-card {
  border-width: 0;
}
</style>


Added infinite loading on the talks page

<template>

 0">Upcoming meetups

There are no available upcoming meetups so far.

 0">

              {{formatDate(meetup.date)}}

                    {{talk.topic}}

                    <router-link id="read-more" :to="`/meetups/${meetup.id}/talks/${talk.id}`">
                      Read more
                    </router-link>

                      Posted by
                      <router-link :to="'/profile/' + talk.author.id">

                            {{talk.author.username}}

                      </router-link>

                      Votes: {{talk.votes_count}}

                    <button class="btn btn-lg btn-success center-block" id="vote" v-on:click="upvote(meetup.id, talk.id)" v-if="!talk.has_voted">Vote</button>
                    <button class="btn btn-lg btn-danger center-block" id="vote" v-on:click="downvote(meetup.id, talk.id)" v-else>Downvote</button>

 0">Past meetups

There are no available past meetups so far.

 0">

              {{formatDate(meetup.date)}}

 0" id="accordion">

                    {{talk.topic}}

                      Posted by
                      <router-link :to="'/profile/' + talk.author.id">

                            {{talk.author.username}}

                      </router-link>

                      Votes: {{talk.votes_count}}

                    <button class="btn btn-lg btn-info center-block" v-on:click="$router.push(`/meetups/${meetup.id}/talks/${talk.id}`)">Read more</button>

There are no available talks for this meetup.

        <infinite-loading @infinite="onInfinite" ref="infiniteLoading">

        </infinite-loading>

    <button class="btn btn-circle btn-lg btn-bottom" id="add-talk" v-on:click="$router.push('/meetups/addTalk')">

    </button>

</template>

<template>

 0">Upcoming meetups

There are no available upcoming meetups so far.

 0">

              {{formatDate(meetup.date)}}

                    {{talk.topic}}

                    <router-link id="read-more" :to="`/meetups/${meetup.id}/talks/${talk.id}`">
                      Read more
                    </router-link>

                      Posted by
                      <router-link :to="'/profile/' + talk.author.id">

                            {{talk.author.username}}

                      </router-link>

                      Votes: {{talk.votes_count}}

                    <button class="btn btn-lg btn-success center-block" id="vote" v-on:click="upvote(meetup.id, talk.id)" v-if="!talk.has_voted">Vote</button>
                    <button class="btn btn-lg btn-danger center-block" id="vote" v-on:click="downvote(meetup.id, talk.id)" v-else>Downvote</button>

 0">Past meetups

There are no available past meetups so far.

 0">

              {{formatDate(meetup.date)}}

                    {{talk.topic}}

                      Posted by
                      <router-link :to="'/profile/' + talk.author.id">

                            {{talk.author.username}}

                      </router-link>

                      Votes: {{talk.votes_count}}

                    <button class="btn btn-lg btn-info center-block" v-on:click="$router.push(`/meetups/${meetup.id}/talks/${talk.id}`)">Read more</button>

    <button class="btn btn-circle btn-lg btn-bottom" id="add-talk" v-on:click="$router.push('/meetups/addTalk')">

    </button>

</template>


update roadmap

update roadmap

<template>
    <doc-guide
        :page="page"
        :hasPadding="true"
    >

    </doc-guide>
</template>


feature: add more friendly language menu

<template>

              {{ $t("bookmarkletLink") }}

                {{ $t("createLink") }}

                  {{ $t("pinLink") }}

                  {{ $t("boardLink") }}

                {{ $t("myLink") }}

                <router-link
                  :to="{ name: 'boards4user', params: {username: user.meta.username} }"
                  class="navbar-item">
                  {{ $t("boardsLink") }}
                </router-link>
                <router-link
                  :to="{ name: 'user', params: {user: user.meta.username} }"
                  class="navbar-item">
                  {{ $t("pinsLink") }}
                </router-link>
                <router-link
                  :to="{ name: 'profile4user', params: {username: user.meta.username} }"
                  class="navbar-item">
                  {{ $t("profileLink") }}
                </router-link>

                {{ $t("browserExtensionsLink") }}

                  {{ $t("chromeLink") }}

                  {{ $t("firefoxLink") }}

            <router-link
              :to="{ name: 'search' }"
              class="navbar-item">

            </router-link>

                Language

                <a
                  v-for="locale in $i18n.availableLocales"
                  :key="`locale-${locale}`"
                  @click="setLocale(locale)"
                  class="navbar-item">
                  {{ locale }}

                  {{ $t("signUpLink") }}

                  {{ $t("logInLink") }}

                  {{ $t("logOutLink") }}

</template>

<template>

              {{ $t("bookmarkletLink") }}

                {{ $t("createLink") }}

                  {{ $t("pinLink") }}

                  {{ $t("boardLink") }}

                {{ $t("myLink") }}

                <router-link
                  :to="{ name: 'boards4user', params: {username: user.meta.username} }"
                  class="navbar-item">
                  {{ $t("boardsLink") }}
                </router-link>
                <router-link
                  :to="{ name: 'user', params: {user: user.meta.username} }"
                  class="navbar-item">
                  {{ $t("pinsLink") }}
                </router-link>
                <router-link
                  :to="{ name: 'profile4user', params: {username: user.meta.username} }"
                  class="navbar-item">
                  {{ $t("profileLink") }}
                </router-link>

                {{ $t("browserExtensionsLink") }}

                  {{ $t("chromeLink") }}

                  {{ $t("firefoxLink") }}

            <router-link
              :to="{ name: 'search' }"
              class="navbar-item">

            </router-link>

                  {{ $t("signUpLink") }}

                  {{ $t("logInLink") }}

                  {{ $t("logOutLink") }}

              <select v-model="$i18n.locale" @change="setLocale($event)">
                 <option v-for="locale in $i18n.availableLocales" :key="`locale-${locale}`" :value="locale">{{ locale }}</option>
              </select>

</template>


Always hides editor in Web IDE for binary files

<template>

            <template v-if="viewer === $options.viewerTypes.edit">
              {{ __('Edit') }}
            </template>
            <template v-else>
              {{ __('Review') }}
            </template>

            {{ file.previewMode.previewTitle }}

      <external-link :file="file" />

    <file-templates-bar v-if="showFileTemplatesBar(file.name)" />

    <content-viewer
      v-if="showContentViewer"
      :content="file.content || file.raw"
      :path="file.rawPath || file.path"
      :file-size="file.size"
      :project-path="file.projectId"
      :type="fileType"
    />
    <diff-viewer
      v-if="showDiffViewer"
      :diff-mode="file.mrChange.diffMode"
      :new-path="file.mrChange.new_path"
      :new-sha="currentMergeRequest.sha"
      :old-path="file.mrChange.old_path"
      :old-sha="currentMergeRequest.baseCommitSha"
      :project-path="file.projectId"
    />

</template>


Restyle-codeblocks (#406)

Restyle-codeblocks (#406)

* Code blocks font weight set to normal

<template>
  <v-app>
    <toolbar />
    <v-content class="app-content">
      <router-view />
    </v-content>
    <a-footer />
  </v-app>
</template>

<script lang="ts">
import Vue from 'vue'
import Component from 'vue-class-component'
import { Watch } from 'vue-property-decorator'
import AFooter from 'client/components/AFooter.vue'
import Toolbar from 'client/components/Toolbar.vue'
import * as nprogress from 'nprogress'
import 'nprogress/nprogress.css'

nprogress.configure({ showSpinner: false })

@Component({
  components: {
    Toolbar,
    AFooter
  }
})
export default class RootComponent extends Vue {
  beforeMount () {
    // This package can only be loaded after mounted (on client only) cause it uses "document"
    // it is used in MarkdownEditor.vue and to make it work faster in that component we preload it here
    import('easymde')
  }

  get isPageLoading () {
    return this.$store.state.isPageLoading
  }

  @Watch('isPageLoading')
  toogleLoading (isPageLoading: boolean) {
    isPageLoading ? nprogress.start() : nprogress.done()
  }
}
</script>

<style lang="postcss">
*,
*:before,
*:after {
  box-sizing: border-box;
}
body, .v-application {
  font-family: -apple-system, system-ui, BlinkMacSystemFont, "Segoe UI", Roboto,
    "Helvetica Neue", Arial, sans-serif;
}

html {
  /*
    Formula is: calc(
      [min-font-size] +
      ([max-font-size] - [min-font-size] * (100vw - [min-vw]) / ([max-vw] - [min-vw])
    );
  */
  font-size: calc(16px + (18 - 16) * (100vw - 320px) / (1920 - 320));
}
:root {
  --h1-font-size: 1.8rem;
  --h2-font-size: 1.4rem;
  --h3-font-size: 1.17rem;
  --h4-font-size: 1rem;
  --h5-font-size: 0.83rem;
  --h6-font-size: 0.75rem;
}

h1 {
  font-size: var(--h1-font-size);
  margin: 1.95rem 0 0.6rem;
}
h2 {
  font-size: var(--h2-font-size);
  margin: 1.65rem 0 0.45rem;
}
h3 {
  font-size: var(--h3-font-size);
  margin: 1.35rem 0 0.35rem;
}
h4 {
  font-size: var(--h4-font-size);
  margin: 1rem 0 0.25rem;
}
h5 {
  font-size: var(--h5-font-size);
  margin: 0.8rem 0 0.2rem;
}
h6 {
  font-size: var(--h6-font-size);
  margin: 0.5rem 0 0;
}

.text-h1 {
  font-size: var(--h1-font-size);
}
.text-h2 {
  font-size: var(--h2-font-size);
}
.text-h3 {
  font-size: var(--h3-font-size);
}
.text-h4 {
  font-size: var(--h4-font-size);
}
.text-h5 {
  font-size: var(--h5-font-size);
}
.text-h6 {
  font-size: var(--h6-font-size);
}

p {
  margin: 0.2rem 0 1.2rem 0;
}

ul,
ol {
  margin: 0.8rem 0;
}

ul > li:not(:last-child),
ol > li:not(:last-child) {
  margin-bottom: 5px;
}

li p {
  margin-top: 0;
}

code {
  color: inherit;
  text-decoration: inherit;
  padding: 2px 4px;
  font-weight: 500;
  box-shadow: none;
  background: rgba(10, 10, 10, 0.04);

  &:not(.sourceCode) {
    font-weight: 800;
    line-height: 125%;
    padding: 2px 3px;
    border: 1px solid rgba(29, 28, 29, 0.13);
    border-radius: 3px;
    background-color: #fafafa;
    box-shadow: none;
  }
}
/* Vuetify css adds unwanted space to start and end of code and kbd tag */
.v-application code:after,
.v-application kbd:after,
.v-application code:before,
.v-application kbd:before {
  content: "";
  letter-spacing: initial;
}
pre code {
  background-color: rgba(10, 10, 10, 0.04);
  color: #bd4147;
  border-radius: 5px;
  border-width: 1px;
  border-style: solid;
  border-color: rgba(10, 10, 10, 0.05);
}
code.sourceCode {
  min-width: 100%;
  padding: 8px;
}
.sourceCode:not(:last-child) code.sourceCode {
  margin: 0 0 0.2rem;
}
a,
/* So much selectors is required to overwrite vuetify styles */
.v-application .app-content a {
  text-decoration-line: none;
  color: #005ebd;

  &:hover {
    text-decoration-line: underline;
  }
}
.text-transform-none {
  text-transform: none !important;
}
.position-relative {
  position: relative;
}
div.sourceCode {
  overflow-x: auto;
}
table.sourceCode,
tr.sourceCode,
td.lineNumbers,
td.sourceCode {
  margin: 0;
  padding: 0;
  vertical-align: baseline;
  border: none;
}
table.sourceCode {
  width: 100%;
  line-height: 100%;
}
td.lineNumbers {
  text-align: right;
  padding-right: 4px;
  padding-left: 4px;
  color: #aaaaaa;
  border-right: 1px solid #aaaaaa;
}
td.sourceCode {
  padding-left: 5px;
}
.v-input--has-state.error--text .v-label {
  animation: none;
}
blockquote {
  background: rgba(10, 10, 10, 0.075);
  border-left: 5px solid rgba(10, 10, 10, 0.15);
  padding: 0.4em 1em;
  margin-left: 0;
  margin-right: 0;
  border-radius: 5px;

  > *:first-child {
    margin-top: 0;
  }
  > *:last-child {
    margin-bottom: 0;
  }
}
.svg-inline--fa {
  width: 1.25em !important;
  text-align: center !important;
}
.v-toolbar__title {
  letter-spacing: inherit;
}
/* Some useless and obstructive div that vuetify add along with "v-menu" component */
.v-menu--inline {
  display: none !important;
}
.v-btn--text {
  text-transform: none;
  letter-spacing: 0.03rem;
  font-size: 1em;
}
</style>

<style lang="postcss" scoped>
/* Should be same padding on any screen cause it changes with toolbar and we fixed toolbar's height (see Toolbar.vue) height on each screen  */
.app-content {
  padding: 100px 0px 36px !important;
  line-height: 150%;
}
.app-content >>> > .v-content__wrap > .container {
  padding: 12px;
}
.app-content >>> code, kbd {
  font-weight: normal;
}
</style>

<template>
  <v-app>
    <toolbar />
    <v-content class="app-content">
      <router-view />
    </v-content>
    <a-footer />
  </v-app>
</template>

<script lang="ts">
import Vue from 'vue'
import Component from 'vue-class-component'
import { Watch } from 'vue-property-decorator'
import AFooter from 'client/components/AFooter.vue'
import Toolbar from 'client/components/Toolbar.vue'
import * as nprogress from 'nprogress'
import 'nprogress/nprogress.css'

nprogress.configure({ showSpinner: false })

@Component({
  components: {
    Toolbar,
    AFooter
  }
})
export default class RootComponent extends Vue {
  beforeMount () {
    // This package can only be loaded after mounted (on client only) cause it uses "document"
    // it is used in MarkdownEditor.vue and to make it work faster in that component we preload it here
    import('easymde')
  }

  get isPageLoading () {
    return this.$store.state.isPageLoading
  }

  @Watch('isPageLoading')
  toogleLoading (isPageLoading: boolean) {
    isPageLoading ? nprogress.start() : nprogress.done()
  }
}
</script>

<style lang="postcss">
*,
*:before,
*:after {
  box-sizing: border-box;
}
body, .v-application {
  font-family: -apple-system, system-ui, BlinkMacSystemFont, "Segoe UI", Roboto,
    "Helvetica Neue", Arial, sans-serif;
}

html {
  /*
    Formula is: calc(
      [min-font-size] +
      ([max-font-size] - [min-font-size] * (100vw - [min-vw]) / ([max-vw] - [min-vw])
    );
  */
  font-size: calc(16px + (18 - 16) * (100vw - 320px) / (1920 - 320));
}
:root {
  --h1-font-size: 1.8rem;
  --h2-font-size: 1.4rem;
  --h3-font-size: 1.17rem;
  --h4-font-size: 1rem;
  --h5-font-size: 0.83rem;
  --h6-font-size: 0.75rem;
}

h1 {
  font-size: var(--h1-font-size);
  margin: 1.95rem 0 0.6rem;
}
h2 {
  font-size: var(--h2-font-size);
  margin: 1.65rem 0 0.45rem;
}
h3 {
  font-size: var(--h3-font-size);
  margin: 1.35rem 0 0.35rem;
}
h4 {
  font-size: var(--h4-font-size);
  margin: 1rem 0 0.25rem;
}
h5 {
  font-size: var(--h5-font-size);
  margin: 0.8rem 0 0.2rem;
}
h6 {
  font-size: var(--h6-font-size);
  margin: 0.5rem 0 0;
}

.text-h1 {
  font-size: var(--h1-font-size);
}
.text-h2 {
  font-size: var(--h2-font-size);
}
.text-h3 {
  font-size: var(--h3-font-size);
}
.text-h4 {
  font-size: var(--h4-font-size);
}
.text-h5 {
  font-size: var(--h5-font-size);
}
.text-h6 {
  font-size: var(--h6-font-size);
}

p {
  margin: 0.2rem 0 1.2rem 0;
}

ul,
ol {
  margin: 0.8rem 0;
}

ul > li:not(:last-child),
ol > li:not(:last-child) {
  margin-bottom: 5px;
}

li p {
  margin-top: 0;
}

code {
  color: inherit;
  text-decoration: inherit;
  padding: 2px 4px;
  font-weight: 500;
  box-shadow: none;
  background: rgba(10, 10, 10, 0.04);

  &:not(.sourceCode) {
    font-weight: 800;
    line-height: 125%;
    padding: 2px 3px;
    border: 1px solid rgba(29, 28, 29, 0.13);
    border-radius: 3px;
    background-color: #fafafa;
    box-shadow: none;
    font-weight: normal;
  }
}
/* Vuetify css adds unwanted space to start and end of code and kbd tag */
.v-application code:after,
.v-application kbd:after,
.v-application code:before,
.v-application kbd:before {
  content: "";
  letter-spacing: initial;
}
pre code {
  background-color: rgba(10, 10, 10, 0.04);
  color: #bd4147;
  font-weight: 900;
  border-radius: 5px;
  border-width: 1px;
  border-style: solid;
  border-color: rgba(10, 10, 10, 0.05);
}
code.sourceCode {
  min-width: 100%;
  padding: 8px;
}
.sourceCode:not(:last-child) code.sourceCode {
  margin: 0 0 0.2rem;
}
a,
/* So much selectors is required to overwrite vuetify styles */
.v-application .app-content a {
  text-decoration-line: none;
  color: #005ebd;

  &:hover {
    text-decoration-line: underline;
  }
}
.text-transform-none {
  text-transform: none !important;
}
.position-relative {
  position: relative;
}
div.sourceCode {
  overflow-x: auto;
}
table.sourceCode,
tr.sourceCode,
td.lineNumbers,
td.sourceCode {
  margin: 0;
  padding: 0;
  vertical-align: baseline;
  border: none;
}
table.sourceCode {
  width: 100%;
  line-height: 100%;
}
td.lineNumbers {
  text-align: right;
  padding-right: 4px;
  padding-left: 4px;
  color: #aaaaaa;
  border-right: 1px solid #aaaaaa;
}
td.sourceCode {
  padding-left: 5px;
}
.v-input--has-state.error--text .v-label {
  animation: none;
}
blockquote {
  background: rgba(10, 10, 10, 0.075);
  border-left: 5px solid rgba(10, 10, 10, 0.15);
  padding: 0.4em 1em;
  margin-left: 0;
  margin-right: 0;
  border-radius: 5px;

  > *:first-child {
    margin-top: 0;
  }
  > *:last-child {
    margin-bottom: 0;
  }
}
.svg-inline--fa {
  width: 1.25em !important;
  text-align: center !important;
}
.v-toolbar__title {
  letter-spacing: inherit;
}
/* Some useless and obstructive div that vuetify add along with "v-menu" component */
.v-menu--inline {
  display: none !important;
}
.v-btn--text {
  text-transform: none;
  letter-spacing: 0.03rem;
  font-size: 1em;
}
</style>

<style lang="postcss" scoped>
/* Should be same padding on any screen cause it changes with toolbar and we fixed toolbar's height (see Toolbar.vue) height on each screen  */
.app-content {
  padding: 100px 0px 36px !important;
  line-height: 150%;
}
.app-content >>> > .v-content__wrap > .container {
  padding: 12px;
}
</style>


Add a flash message for reply deletion

Add a flash message for reply deletion

<template>

				<avatar :user="reply.author"></avatar>

					Replied {{ createdAt('relative') }}

					<avatar :user="reply.author" :has-label="true">
						{{ createdAt('relative') }}
					</avatar>

							<svg class="icon">
								<use xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="#icon-dots-three-vertical"></use>
							</svg>

							Edit
							Delete

							Mark as Answer

							Copy Link
							Message {{ reply.author.name }}
							Report

							<textarea class="form-control" v-model="body">{{ body }}</textarea>

								<button class="btn btn-primary" @click.prevent="update">Update</button>

								<button class="btn btn-link-secondary" @click.prevent="editing = false">Cancel</button>

								<favorite :reply="reply"></favorite>

								<svg class="icon">
									<use xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="#icon-check"></use>
								</svg>

								<svg class="icon">
									<use xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="#icon-dots-three-vertical"></use>
								</svg>

								Edit
								Delete

								Copy Link
								Message {{ reply.author.name }}
								Report

						<favorite :reply="reply" v-if="signedIn"></favorite>

							<svg class="icon">
								<use xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="#icon-check"></use>
							</svg>

</template>


fix(h5): layout

fix(h5): layout

<template>
  <uni-layout
    v-if="responsive"
    :class="{'uni-app--showlayout':showLayout}"
  >
    <uni-top-window
      v-if="topWindow"
      v-show="showTopWindow && topWindowMediaQuery"
    >
      <div
        ref="topWindow"
        class="uni-top-window"
        :style="topWindowStyle"
      >
        <v-uni-top-window
          ref="top"
          @hook:mounted="onTopWindowInit"
        />
      </div>
      <div
        class="uni-top-window--placeholder"
        :style="{height:topWindowHeight}"
      />
    </uni-top-window>
    <uni-content>
      <uni-main>
        <keep-alive :include="keepAliveInclude">
          <router-view :key="routerKey" />
        </keep-alive>
      </uni-main>
      <uni-left-window
        v-if="leftWindow"
        v-show="showLeftWindow && leftWindowMediaQuery"
        ref="leftWindow"
        :style="leftWindowStyle"
      >
        <v-uni-left-window
          ref="left"
          @hook:mounted="onLeftWindowInit"
        />
      </uni-left-window>
      <uni-right-window
        v-if="rightWindow"
        v-show="showRightWindow && rightWindowMediaQuery"
        ref="rightWindow"
        :style="rightWindowStyle"
      >
        <v-uni-right-window
          ref="right"
          @hook:mounted="onRightWindowInit"
        />
      </uni-right-window>
    </uni-content>
    <!--TODO footer-->
  </uni-layout>
  <keep-alive
    v-else
    :include="keepAliveInclude"
  >
    <router-view :key="routerKey" />
  </keep-alive>
</template>

<script>
import Vue from 'vue'

import {
  RESPONSIVE_MIN_WIDTH
} from 'uni-helpers/constants'

const windowTypes = ['top', 'left', 'right']
const documentElement = document.documentElement

let styleObj

function updateCssVar (name, value) {
  if (!styleObj) {
    styleObj = documentElement.style
  }
  styleObj.setProperty(name, value)
}

function checkMinWidth (minWidth) {
  const screen = window.screen
  const sizes = [
    window.outerWidth,
    window.outerHeight,
    screen.width,
    screen.height,
    documentElement.clientWidth,
    documentElement.clientHeight
  ]
  return Math.max.apply(null, sizes) > minWidth
}

export default {
  name: 'Layout',
  props: {
    routerKey: {
      type: String,
      default: ''
    },
    keepAliveInclude: {
      type: Array,
      default: function () {
        return []
      }
    }
  },
  data () {
    return {
      leftWindowStyle: '',
      rightWindowStyle: '',
      topWindowStyle: '',
      topWindowMediaQuery: false,
      leftWindowMediaQuery: false,
      rightWindowMediaQuery: false,
      topWindowHeight: '0px'
    }
  },
  computed: {
    showLayout () {
      return this.showTopWindow || this.showLeftWindow || this.showRightWindow
    },
    showTopWindow () {
      return this.$route.meta.topWindow !== false
    },
    showLeftWindow () {
      return this.$route.meta.leftWindow !== false
    },
    showRightWindow () {
      return this.$route.meta.rightWindow !== false
    }
  },
  watch: {
    showTopWindow (newVal, val) {
      if (newVal) {
        this.$nextTick(this.onTopWindowInit)
      } else {
        updateCssVar('--window-top', '0px')
      }
    },
    showLeftWindow (newVal, val) {
      if (newVal) {
        this.$nextTick(this.onLeftWindowInit)
      } else {
        updateCssVar('--window-left', '0px')
      }
    },
    showRightWindow (newVal, val) {
      if (newVal) {
        this.$nextTick(this.onRightWindowInit)
      } else {
        updateCssVar('--window-right', '0px')
      }
    }
  },
  beforeCreate () {
    updateCssVar('--window-top', '0px')
    updateCssVar('--window-left', '0px')
    updateCssVar('--window-right', '0px')
  },
  created () {
    this.topWindow = Vue.component('VUniTopWindow')
    this.leftWindow = Vue.component('VUniLeftWindow')
    this.rightWindow = Vue.component('VUniRightWindow')
    if ( // 低版本不提供 responsive 支持
      (this.topWindow || this.leftWindow || this.rightWindow) &&
        uni.canIUse('css.var') &&
        window.matchMedia
    ) {
      windowTypes.forEach(type => this.initWindowMinWidth(type))
      this.responsive = checkMinWidth(this.minWidth)
      if (this.responsive) {
        if (this.topWindow && this.topWindow.options.style) {
          this.topWindowStyle = this.topWindow.options.style
        }
        if (this.leftWindow && this.leftWindow.options.style) {
          this.leftWindowStyle = this.leftWindow.options.style
        }
        if (this.rightWindow && this.rightWindow.options.style) {
          this.rightWindowStyle = this.rightWindow.options.style
        }
        windowTypes.forEach(type => this.initMediaQuery(type))
      }
    }
  },
  methods: {
    initWindowMinWidth (type) {
      const name = type + 'Window'
      if (this[name]) {
        const minWidthName = type + 'WindowMinWidth'
        this[minWidthName] = RESPONSIVE_MIN_WIDTH
        const windowOptions = __uniConfig[name]
        if (windowOptions && windowOptions.matchMedia && windowOptions.matchMedia.minWidth) {
          this[minWidthName] = windowOptions.matchMedia.minWidth
        }
        if (!this.minWidth || this.minWidth > this[minWidthName]) {
          this.minWidth = this[minWidthName]
        }
      }
    },
    initMediaQuery (type) {
      if (this[type + 'Window']) {
        const name = type + 'WindowMediaQuery'
        const mediaQueryList = window.matchMedia('(min-width: ' + this[type + 'WindowMinWidth'] + 'px)')
        mediaQueryList.addListener((e) => {
          this[name] = e.matches
          this.$nextTick(() => {
            this['on' + (type.substr(0, 1).toUpperCase() + type.substr(1)) + 'WindowInit']()
          })
        })
        this[name] = mediaQueryList.matches
      }
    },
    onTopWindowInit () {
      if (!(this.responsive && this.topWindow)) {
        return
      }
      // TODO page header
      let windowTopHeight = '0px'
      if (this.topWindowStyle && this.topWindowStyle.height) {
        windowTopHeight = this.$refs.topWindow.offsetHeight + 'px'
      } else {
        windowTopHeight = this.$refs.top.$el.offsetHeight + 'px'
      }
      this.topWindowHeight = windowTopHeight
      updateCssVar('--window-top', windowTopHeight)
    },
    onLeftWindowInit () {
      if (!(this.responsive && this.leftWindow)) {
        return
      }
      if (this.leftWindowStyle && this.leftWindowStyle.width) {
        updateCssVar('--window-left', this.$refs.leftWindow.offsetWidth + 'px')
      } else {
        updateCssVar('--window-left', this.$refs.left.$el.offsetWidth + 'px')
      }
    },
    onRightWindowInit () {
      if (!(this.responsive && this.rightWindow)) {
        return
      }
      if (this.rightWindowStyle && this.rightWindowStyle.width) {
        updateCssVar('--window-right', this.$refs.rightWindow.offsetWidth + 'px')
      } else {
        updateCssVar('--window-right', this.$refs.right.$el.offsetWidth + 'px')
      }
    }
  }
}
</script>

<style>
  uni-content {
    display: flex;
    flex: 1 0 auto;
    height: 100%;
  }

  uni-main {
    flex: 1;
  }

  uni-top-window+uni-content {
    height: calc(100vh - var(--window-top));
  }

  uni-left-window {
    position: relative;
    width: var(--window-left);
    order: -1;
    overflow-x: hidden;
  }

  uni-right-window {
    position: relative;
    width: var(--window-right);
    overflow-x: hidden;
  }

  .uni-app--showlayout+uni-tabbar {
    display: none;
  }

  .uni-top-window {
    position: fixed;
    left: 0;
    right: 0;
    top: 0;
    z-index: 998;
    overflow: hidden;
  }
</style>

<template>
  <uni-layout
    v-if="responsive"
    :class="{'uni-app--showlayout':showLayout}"
  >
    <uni-top-window
      v-if="topWindow"
      v-show="showTopWindow && topWindowMediaQuery"
    >
      <div
        ref="topWindow"
        class="uni-top-window"
        :style="topWindowStyle"
      >
        <v-uni-top-window
          ref="top"
          @hook:mounted="onTopWindowInit"
        />
      </div>
      <div
        class="uni-top-window--placeholder"
        :style="{height:topWindowHeight}"
      />
    </uni-top-window>
    <uni-content>
      <uni-main>
        <keep-alive :include="keepAliveInclude">
          <router-view :key="routerKey" />
        </keep-alive>
      </uni-main>
      <uni-left-window
        v-if="leftWindow"
        v-show="showLeftWindow && leftWindowMediaQuery"
        ref="leftWindow"
        :style="leftWindowStyle"
      >
        <v-uni-left-window
          ref="left"
          @hook:mounted="onLeftWindowInit"
        />
      </uni-left-window>
      <uni-right-window
        v-if="rightWindow"
        v-show="showRightWindow && rightWindowMediaQuery"
        ref="rightWindow"
        :style="rightWindowStyle"
      >
        <v-uni-right-window
          ref="right"
          @hook:mounted="onRightWindowInit"
        />
      </uni-right-window>
    </uni-content>
    <!--TODO footer-->
  </uni-layout>
  <keep-alive
    v-else
    :include="keepAliveInclude"
  >
    <router-view :key="routerKey" />
  </keep-alive>
</template>

<script>
import Vue from 'vue'

import {
  RESPONSIVE_MIN_WIDTH
} from 'uni-helpers/constants'

const windowTypes = ['top', 'left', 'right']
const documentElement = document.documentElement

let styleObj

function updateCssVar (name, value) {
  if (!styleObj) {
    styleObj = documentElement.style
  }
  styleObj.setProperty(name, value)
}

function checkMinWidth (minWidth) {
  const screen = window.screen
  const sizes = [
    window.outerWidth,
    window.outerHeight,
    screen.width,
    screen.height,
    documentElement.clientWidth,
    documentElement.clientHeight
  ]
  return Math.max.apply(null, sizes) > minWidth
}

export default {
  name: 'Layout',
  props: {
    routerKey: {
      type: String,
      default: ''
    },
    keepAliveInclude: {
      type: Array,
      default: function () {
        return []
      }
    }
  },
  data () {
    return {
      leftWindowStyle: '',
      rightWindowStyle: '',
      topWindowStyle: '',
      topWindowMediaQuery: false,
      leftWindowMediaQuery: false,
      rightWindowMediaQuery: false,
      topWindowHeight: '0px'
    }
  },
  computed: {
    showLayout () {
      return this.showTopWindow || this.showLeftWindow || this.showRightWindow
    },
    showTopWindow () {
      return this.$route.meta.topWindow !== false
    },
    showLeftWindow () {
      return this.$route.meta.leftWindow !== false
    },
    showRightWindow () {
      return this.$route.meta.rightWindow !== false
    }
  },
  watch: {
    showTopWindow (newVal, val) {
      if (newVal) {
        this.$nextTick(this.onTopWindowInit)
      } else {
        updateCssVar('--window-top', '0px')
      }
    },
    showLeftWindow (newVal, val) {
      if (newVal) {
        this.$nextTick(this.onLeftWindowInit)
      } else {
        updateCssVar('--window-left', '0px')
      }
    },
    showRightWindow (newVal, val) {
      if (newVal) {
        this.$nextTick(this.onRightWindowInit)
      } else {
        updateCssVar('--window-right', '0px')
      }
    }
  },
  beforeCreate () {
    updateCssVar('--window-top', '0px')
    updateCssVar('--window-left', '0px')
    updateCssVar('--window-right', '0px')
  },
  created () {
    this.topWindow = Vue.component('VUniTopWindow')
    this.leftWindow = Vue.component('VUniLeftWindow')
    this.rightWindow = Vue.component('VUniRightWindow')
    if ( // 低版本不提供 responsive 支持
      (this.topWindow || this.leftWindow || this.rightWindow) &&
        uni.canIUse('css.var') &&
        window.matchMedia
    ) {
      windowTypes.forEach(type => this.initWindowMinWidth(type))
      this.responsive = checkMinWidth(this.minWidth)
      if (this.responsive) {
        if (this.topWindow && this.topWindow.options.style) {
          this.topWindowStyle = this.topWindow.options.style
        }
        if (this.leftWindow && this.leftWindow.options.style) {
          this.leftWindowStyle = this.leftWindow.options.style
        }
        if (this.rightWindow && this.rightWindow.options.style) {
          this.rightWindowStyle = this.rightWindow.options.style
        }
        windowTypes.forEach(type => this.initMediaQuery(type))
      }
    }
  },
  methods: {
    initWindowMinWidth (type) {
      const name = type + 'Window'
      if (this[name]) {
        const minWidthName = type + 'WindowMinWidth'
        this[minWidthName] = RESPONSIVE_MIN_WIDTH
        const windowOptions = __uniConfig[name]
        if (windowOptions && windowOptions.matchMedia && windowOptions.matchMedia.minWidth) {
          this[minWidthName] = windowOptions.matchMedia.minWidth
        }
        if (!this.minWidth || this.minWidth > this[minWidthName]) {
          this.minWidth = this[minWidthName]
        }
      }
    },
    initMediaQuery (type) {
      if (this[type + 'Window']) {
        const name = type + 'WindowMediaQuery'
        const mediaQueryList = window.matchMedia('(min-width: ' + this[type + 'WindowMinWidth'] + 'px)')
        mediaQueryList.addListener((e) => {
          this[name] = e.matches
          this.$nextTick(() => {
            this['on' + (type.substr(0, 1).toUpperCase() + type.substr(1)) + 'WindowInit']()
          })
        })
        this[name] = mediaQueryList.matches
      }
    },
    onTopWindowInit () {
      // TODO page header
      let windowTopHeight = '0px'
      if (this.topWindowStyle && this.topWindowStyle.height) {
        windowTopHeight = this.$refs.topWindow.offsetHeight + 'px'
      } else {
        windowTopHeight = this.$refs.top.$el.offsetHeight + 'px'
      }
      this.topWindowHeight = windowTopHeight
      updateCssVar('--window-top', windowTopHeight)
    },
    onLeftWindowInit () {
      if (this.leftWindowStyle && this.leftWindowStyle.width) {
        updateCssVar('--window-left', this.$refs.leftWindow.offsetWidth + 'px')
      } else {
        updateCssVar('--window-left', this.$refs.left.$el.offsetWidth + 'px')
      }
    },
    onRightWindowInit () {
      if (this.rightWindowStyle && this.rightWindowStyle.width) {
        updateCssVar('--window-right', this.$refs.rightWindow.offsetWidth + 'px')
      } else {
        updateCssVar('--window-right', this.$refs.right.$el.offsetWidth + 'px')
      }
    }
  }
}
</script>

<style>
  uni-content {
    display: flex;
    flex: 1 0 auto;
    height: 100%;
  }

  uni-main {
    flex: 1;
  }

  uni-top-window+uni-content {
    height: calc(100vh - var(--window-top));
  }

  uni-left-window {
    position: relative;
    width: var(--window-left);
    order: -1;
    overflow-x: hidden;
  }

  uni-right-window {
    position: relative;
    width: var(--window-right);
    overflow-x: hidden;
  }

  .uni-app--showlayout+uni-tabbar {
    display: none;
  }

  .uni-top-window {
    position: fixed;
    left: 0;
    right: 0;
    top: 0;
    z-index: 998;
    overflow: hidden;
  }
</style>


Fix incomplete-looking numQuestionsExceed message

<template>

  <CoreBase
    :immersivePage="true"
    immersivePageIcon="close"
    :immersivePagePrimary="false"
    :immersivePageRoute="toolbarRoute"
    :appBarTitle="$tr('createNewExamLabel')"
    :authorized="userIsAuthorized"
    authorizedRole="adminOrCoach"
    :pageTitle="$tr('createNewExamLabel')"
    :marginBottom="72"
  >

    <KPageContainer>

{{ $tr('createNewExamLabel') }}

      <UiAlert
        v-if="showError && !inSearchMode"
        type="error"
        :dismissible="false"
      >
        {{ selectionIsInvalidText }}
      </UiAlert>

{{ coachString('detailsLabel') }}

      <KGrid>
        <KGridItem :layout12="{ span: 6 }">
          <KTextbox
            ref="title"
            v-model.trim="examTitle"
            :label="coachString('titleLabel')"
            :autofocus="true"
            :maxlength="100"
          />
        </KGridItem>
        <KGridItem :layout12="{ span: 6 }">
          <KGrid>
            <KGridItem
              :layout4="{ span: 2 }"
              :layout8="{ span: 5 }"
              :layout12="{ span: 8 }"
            >
              <KTextbox
                ref="questionsInput"
                v-model.trim.number="numQuestions"
                type="number"
                :min="1"
                :max="maxQs"
                :invalid="Boolean(showError && numQuestIsInvalidText)"
                :invalidText="numQuestIsInvalidText"
                :label="$tr('numQuestions')"
                @blur="numQuestionsBlurred = true"
              />
            </KGridItem>
            <KGridItem
              :layout4="{ span: 2 }"
              :layout8="{ span: 3 }"
              :layout12="{ span: 4 }"
              :style="{ marginTop: '16px' }"
            >
              <KIconButton
                icon="minus"
                aria-hidden="true"
                class="number-btn"
                :disabled="numQuestions === 1"
                @click="numQuestions -= 1"
              />
              <KIconButton
                icon="plus"
                aria-hidden="true"
                class="number-btn"
                :disabled="numQuestions === maxQs"
                @click="numQuestions += 1"
              />
            </KGridItem>
          </KGrid>
        </KGridItem>
      </KGrid>

{{ $tr('chooseExercises') }}

      <LessonsSearchBox
        class="search-box"
        @searchterm="handleSearchTerm"
      />

      <LessonsSearchFilters
        v-if="inSearchMode"
        v-model="filters"
        :searchTerm="searchTerm"
        :searchResults="searchResults"
      />
      <ResourceSelectionBreadcrumbs
        v-else
        :ancestors="ancestors"
        :channelsLink="channelsLink"
        :topicsLink="topicsLink"
      />

{{ topicTitle }}

{{ topicDescription }}

      <ContentCardList
        :contentList="filteredContentList"
        :showSelectAll="selectAllIsVisible"
        :viewMoreButtonState="viewMoreButtonState"
        :selectAllChecked="selectAllChecked"
        :selectAllIndeterminate="selectAllIndeterminate"
        :contentIsChecked="contentIsSelected"
        :contentIsIndeterminate="contentIsIndeterminate"
        :contentHasCheckbox="contentHasCheckbox"
        :contentCardMessage="selectionMetadata"
        :contentCardLink="contentLink"
        @changeselectall="toggleTopicInWorkingResources"
        @change_content_card="toggleSelected"
        @moreresults="handleMoreResults"
      />

      <BottomAppBar v-if="inSearchMode">
        <KRouterLink
          appearance="raised-button"
          :text="$tr('exitSearchButtonLabel')"
          primary
          :to="toolbarRoute"
        />
      </BottomAppBar>
      <BottomAppBar v-else>
        <KButtonGroup>
          <KRouterLink
            appearance="flat-button"
            :text="coreString('goBackAction')"
            :to="toolbarRoute"
          />
          <KButton
            :text="coreString('continueAction')"
            primary
            @click="continueProcess"
          />
        </KButtonGroup>
      </BottomAppBar>

    </KPageContainer>

  </CoreBase>

</template>


update Index.vue

update Index.vue

<template>

      Karabiner-Elements complex_modifications rules

            <icon name="external-link-alt"></icon> GitHub

Table of Contents

          {{ group.name }}
          {{ group.files.length }}

        Expand/Collapse All

{{ group.name }}

                    {{ file.title }}
                    Import

{{ rule }}

        Other examples

</template>


mining

mining

<template>

Хэшрейт

<v-btn
        flat
        v-for="item in menuItems"
        :key="item.title"
        :to="item.link">
        <v-icon left dark>{{ item.icon }}</v-icon>
        {{ item.title }}
        </v-btn>

        <v-data-table
          v-bind:headers="headers"
          v-bind:items="balance"
          v-bind:pagination.sync="pagination"
          v-bind:rows-per-page-items="massiv"
          :total-items="totalItems"
          rows-per-page-text="Количество строк:"
          no-data-text="Нет данных для отображения"
          class="elevation-1 clients-table"
        >
      <template slot="items" scope="props" >
            {{ props.item.day }}
            {{ props.item.date }}
            {{ props.item.mining }}
            {{ props.item.balanceItem }}
            {{ props.item.coursesBTC }}

        </template>
        </v-data-table>

</template>


Finished component for individual server stats page

<template>

 0"
    class="mdl-shadow--2dp mdl-color--white mdl-cell mdl-cell--12-col mdl-grid">

      {{getWorldById($route.params.server).name}}

        {{timezone.name | format}}

            Date
            Kills
            Deaths
            Scores
            Rating Metric

              {{item.time | date}}

            {{item.kills}}
            {{item.deaths}}
            {{item.scores}}
            {{item.metric | round 3 true}}

</template>

<template>
  <div class="mdl-shadow--2dp mdl-color--white mdl-cell mdl-cell--12-col mdl-grid">
    {{$route.params.server}}
  </div>
</template>


bugfix: missing return type setting

bugfix: missing return type setting

<template>

        {{ symbol.name }}

{{ module }}!{{ symbol.name }}

Address:

              0x{{ symbol.address.value.toString(16) }}
            {{ symbol.address }}

Arguments:

              <option v-for="(t, j) in types" :key="j" :value="t">{{ t }}</option>

              <button class="button is-danger" @click="removeArg(index)">

              </button>

            <button @click="addArg" class="button">

              Add
            </button>

Returns:

              <option v-for="(t, j) in types" :key="j" :value="t">{{ t }}</option>

            {{ expr }}

          <button class="button" type="button" @click="dismiss">Dismiss</button>
          <button class="button is-primary" @click="hook">Send to Interceptor</button>

</template>

<template>

        {{ symbol.name }}

{{ module }}!{{ symbol.name }}

Address:

              0x{{ symbol.address.value.toString(16) }}
            {{ symbol.address }}

Arguments:

              <option v-for="(t, j) in types" :key="j" :value="t">{{ t }}</option>

              <button class="button is-danger" @click="removeArg(index)">

              </button>

            <button @click="addArg" class="button">

              Add
            </button>

            {{ expr }}

          <button class="button" type="button" @click="dismiss">Dismiss</button>
          <button class="button is-primary" @click="hook">Send to Interceptor</button>

</template>


make js more concise

make js more concise

<template>

{{ bucket.heading }}

      <radios
        :options="bucket.options"
        :store-module="storeModule"
        base-classes="form__radios form__radios--always-stack form__radios--serif"
        name="level"
        @updateCallback="onUpdate"
      />

</template>


ditch fieldsets in favor of divs

ditch fieldsets in favor of divs

<template>
  <form
    ref="form"
    action="/charge"
    method="post"
    class="form"
    @submit="$event.preventDefault()"
  >

              <text-input
                :show-error="showAllErrors"
                :validation="validation.amount"
                has-label
                label-text="amount ($)"
                base-classes="form__text form__text--heavy"
                error-classes="form__error"
                label-classes="form__label grid_separator--xs"
                name="amount"
                store-module="baseForm"
                @markErrorValidity="markErrorValidity"
              />

              <radios
                :options="frequencyOptions"
                base-classes="grid_row form__radios"
                name="installment_period"
                store-module="baseForm"
                @updateCallback="onFrequencyUpdate"
              />

              <text-input
                :show-error="showAllErrors"
                :validation="validation.stripeEmail"
                has-label
                label-text="email address"
                type="email"
                base-classes="form__text form__text--standard"
                error-classes="form__error"
                label-classes="form__label grid_separator--xs"
                name="stripeEmail"
                store-module="baseForm"
                @markErrorValidity="markErrorValidity"
              />

              <text-input
                :show-error="showAllErrors"
                :validation="validation.first_name"
                has-label
                label-text="first name"
                base-classes="form__text form__text--standard"
                error-classes="form__error"
                label-classes="form__label grid_separator--xs"
                name="first_name"
                store-module="baseForm"
                @markErrorValidity="markErrorValidity"
              />

              <text-input
                :show-error="showAllErrors"
                :validation="validation.last_name"
                has-label
                label-text="last name"
                base-classes="form__text form__text--standard"
                error-classes="form__error"
                label-classes="form__label grid_separator--xs"
                name="last_name"
                store-module="baseForm"
                @markErrorValidity="markErrorValidity"
              />

              <text-input
                has-label
                label-text="encouraged to give by"
                base-classes="form__text form__text--standard"
                error-classes="form__error"
                label-classes="form__label grid_separator--xs"
                name="reason"
                store-module="baseForm"
              />

              <text-input
                :show-error="showAllErrors"
                :validation="validation.zipcode"
                has-label
                label-text="zip code"
                maxlength="5"
                base-classes="form__text form__text--standard"
                error-classes="form__error"
                label-classes="form__label grid_separator--xs"
                name="zipcode"
                store-module="baseForm"
                @markErrorValidity="markErrorValidity"
              />

            <pay-fees
              base-classes="form__fees"
              amount-store-module="baseForm"
              pay-fees-value-store-module="baseForm"
            />

            <native-pay
              :form-is-valid="nativeIsValid"
              :supported="nativeIsSupported"
              base-classes="form__native"
              amount-store-module="baseForm"
              token-store-module="baseForm"
              @setValue="setValue"
              @onSubmit="onSubmit"
            />

              <manual-pay
                :show-error="showManualErrors"
                :validation="validation.card"
                base-classes="form__manual"
                token-store-module="baseForm"
                error-classes="form__error"
                @markErrorValidity="markErrorValidity"
              />

              <manual-submit
                :form-is-valid="manualIsValid"
                base-classes="col button button--yellow button--l"
                value="Pay by card"
                @onSubmit="onSubmit"
                @setValue="setValue"
              />

    <hidden
      name="installments"
      store-module="baseForm"
    />
    <hidden
      name="stripeToken"
      store-module="baseForm"
    />
    <hidden
      name="description"
      store-module="baseForm"
    />
    <hidden
      name="campaign_id"
      store-module="baseForm"
    />
    <hidden
      name="openended_status"
      store-module="baseForm"
    />
    <hidden
      name="pay_fees_value"
      store-module="baseForm"
    />
  </form>
</template>

<template>
  <form
    ref="form"
    action="/charge"
    method="post"
    class="form"
    @submit="$event.preventDefault()"
  >

        <fieldset class="form__group">

              <text-input
                :show-error="showAllErrors"
                :validation="validation.amount"
                has-label
                label-text="amount ($)"
                base-classes="form__text form__text--heavy"
                error-classes="form__error"
                label-classes="form__label grid_separator--xs"
                name="amount"
                store-module="baseForm"
                @markErrorValidity="markErrorValidity"
              />

              <radios
                :options="frequencyOptions"
                base-classes="grid_row form__radios"
                name="installment_period"
                store-module="baseForm"
                @updateCallback="onFrequencyUpdate"
              />

        </fieldset>

        <fieldset class="form__group">

              <text-input
                :show-error="showAllErrors"
                :validation="validation.stripeEmail"
                has-label
                label-text="email address"
                type="email"
                base-classes="form__text form__text--standard"
                error-classes="form__error"
                label-classes="form__label grid_separator--xs"
                name="stripeEmail"
                store-module="baseForm"
                @markErrorValidity="markErrorValidity"
              />

              <text-input
                :show-error="showAllErrors"
                :validation="validation.first_name"
                has-label
                label-text="first name"
                base-classes="form__text form__text--standard"
                error-classes="form__error"
                label-classes="form__label grid_separator--xs"
                name="first_name"
                store-module="baseForm"
                @markErrorValidity="markErrorValidity"
              />

              <text-input
                :show-error="showAllErrors"
                :validation="validation.last_name"
                has-label
                label-text="last name"
                base-classes="form__text form__text--standard"
                error-classes="form__error"
                label-classes="form__label grid_separator--xs"
                name="last_name"
                store-module="baseForm"
                @markErrorValidity="markErrorValidity"
              />

              <text-input
                has-label
                label-text="encouraged to give by"
                base-classes="form__text form__text--standard"
                error-classes="form__error"
                label-classes="form__label grid_separator--xs"
                name="reason"
                store-module="baseForm"
              />

              <text-input
                :show-error="showAllErrors"
                :validation="validation.zipcode"
                has-label
                label-text="zip code"
                maxlength="5"
                base-classes="form__text form__text--standard"
                error-classes="form__error"
                label-classes="form__label grid_separator--xs"
                name="zipcode"
                store-module="baseForm"
                @markErrorValidity="markErrorValidity"
              />

        </fieldset>

        <fieldset class="donate-form__payment-box">

            <pay-fees
              base-classes="form__fees"
              amount-store-module="baseForm"
              pay-fees-value-store-module="baseForm"
            />

            <native-pay
              :form-is-valid="nativeIsValid"
              :supported="nativeIsSupported"
              base-classes="form__native"
              amount-store-module="baseForm"
              token-store-module="baseForm"
              @setValue="setValue"
              @onSubmit="onSubmit"
            />

              <manual-pay
                :show-error="showManualErrors"
                :validation="validation.card"
                base-classes="form__manual"
                token-store-module="baseForm"
                error-classes="form__error"
                @markErrorValidity="markErrorValidity"
              />

              <manual-submit
                :form-is-valid="manualIsValid"
                base-classes="col button button--yellow button--l"
                value="Pay by card"
                @onSubmit="onSubmit"
                @setValue="setValue"
              />

        </fieldset>

    <hidden
      name="installments"
      store-module="baseForm"
    />
    <hidden
      name="stripeToken"
      store-module="baseForm"
    />
    <hidden
      name="description"
      store-module="baseForm"
    />
    <hidden
      name="campaign_id"
      store-module="baseForm"
    />
    <hidden
      name="openended_status"
      store-module="baseForm"
    />
    <hidden
      name="pay_fees_value"
      store-module="baseForm"
    />
  </form>
</template>


fix datepicker hour min definde

fix datepicker hour min definde

<template>

      {{currentValue}}
      {{placeholder}}
      <faFont name="calendar" v-show="!showClearBtn"></faFont>

        <faFont
          style="top: 11px;"
          v-show="showClearBtn"
          name="times-circle">
        </faFont>

    <transition name="fade-in-linear" mode="out-in">

          {{year}}年
          {{getMonth}}月

            <faFont name="angle-right"></faFont>

              日
              一
              二
              三
              四
              五
              六

            {{cell.day}}

          <ctTimePicker
            v-if="type === 'datetime'"
            class="left"
            :hour="getHour"
            :minutes="getMinutes"
            :hourList="hourList"
            :minutesList="minutesList"
            v-on:on-hour-change="handleHourChange"
            v-on:on-minutes-change="handleMinutesChange"
          ></ctTimePicker>
          <ctButton class="right" type="primary" @click="hidePicker">确定</ctButton>

    </transition>

</template>

<template>

      {{currentValue}}
      {{placeholder}}
      <faFont name="calendar" v-show="!showClearBtn"></faFont>

        <faFont
          style="top: 11px;"
          v-show="showClearBtn"
          name="times-circle">
        </faFont>

    <transition name="fade-in-linear" mode="out-in">

          {{year}}年
          {{getMonth}}月

            <faFont name="angle-right"></faFont>

              日
              一
              二
              三
              四
              五
              六

            {{cell.day}}

          <ctTimePicker
            v-if="type === 'datetime'"
            class="left"
            :hour="getHour"
            :minutes="getMinutes"
            v-on:on-hour-change="handleHourChange"
            v-on:on-minutes-change="handleMinutesChange"
          ></ctTimePicker>
          <ctButton class="right" type="primary" @click="hidePicker">确定</ctButton>

    </transition>

</template>


Edit template of Control component

Edit template of Control component

<template>
  <div class="control">
    <component v-bind:is="type" v-bind="options"></component>
  </div>
</template>

<script>
  export default {
    props: {
      type: Object,
      options: {
        type: Object,
        default: () => ({})
      }
    }
  }
</script>

<template>
  <p class="control">
    <component v-bind:is="type" v-bind="options"></component>
  </p>
</template>

<script>
  export default {
    props: {
      type: Object,
      options: {
        type: Object,
        default: () => ({})
      }
    }
  }
</script>


Update CdrPopover.vue

Update CdrPopover.vue

<template>

      <slot name="trigger"/>

    <cdr-popup
      role="dialog"
      @closed="closePopover"
      :class="$style[baseClass]"
      :ref="popupEl"
      :position="position"
      :autoPosition="autoPosition"
      :opened="isOpen"
      :ariaExpanded="`${isOpen}`"
      :id="id"
      :contentClass="contentClass"
    >

            <slot name="title"/>

              {{ label }}

          <slot />

        <cdr-button
          :class="$style['cdr-popover__close-button']"
          icon-only
          @click="closePopover"
          aria-label="Close"
          size="small"
        >
          <icon-x-sm
            slot="icon"
            inherit-color
          />
        </cdr-button>

    </cdr-popup>

</template>

<template>

      { this.$slots.trigger }

    <cdr-popup
      class={this.style['cdr-popover']}
      role="dialog"
      ref="popup"
      position={ this.position }
      autoPosition={ this.autoPosition }
      opened={ this.isOpen }
      onClosed={ this.closePopover }
      aria-expanded={ `${this.isOpen}` }
      id={ this.id }
      contentClass={ this.contentClass }
    >

          {
            (this.$slots.title || this.label) && (

                {
                  this.$slots.title
                }
                {
                  !this.$slots.title && this.label && (

                      {this.label}

                  )
                }

            )
          }
          { this.$slots.default }

        <cdr-button
          class={this.style['cdr-popover__close-button']}
          icon-only
          on-click={this.closePopover}
          aria-label="Close"
          size="small"
        >
          <icon-x-sm
            slot="icon"
            inherit-color
          />
        </cdr-button>

    </cdr-popup>

</template>


Update mobile responsive view and re-add breadcrumbs

<template>

      <CustomContentRenderer :topic="topic" />

        <KGrid>
          <KGridItem
            v-if="!displayingSearchResults"
            class="breadcrumbs"
            :layout4="{ span: 4 }"
            :layout8="{ span: 8 }"
            :layout12="{ span: 12 }"
          >
            <KBreadcrumbs v-if="breadcrumbs.length" :items="breadcrumbs" />
          </KGridItem>
          <KGridItem
            :layout4="{ span: 4 }"
            :layout8="{ span: 8 }"
            :layout12="{ span: 12 }"
          >

              <TextTruncator
                :text="topic.title"
                :maxHeight="maxTitleHeight"
              />

          </KGridItem>

          <KGridItem
            v-if="topic.thumbnail"
            class="thumbnail"
            :layout4="{ span: 1 }"
            :layout8="{ span: 2 }"
            :layout12="{ span: 2 }"
          >
            <CardThumbnail
              class="thumbnail"
              :thumbnail="topic.thumbnail"
              :isMobile="windowIsSmall"
              :showTooltip="false"
              kind="channel"
              :showContentIcon="false"
            />
          </KGridItem>

          <KGridItem
            v-if="topic.description"
            class="text"
            :layout4="{ span: topic.thumbnail ? 3 : 4 }"
            :layout8="{ span: topic.thumbnail ? 6 : 8 }"
            :layout12="{ span: topic.thumbnail ? 10 : 12 }"
          >
            <TextTruncator
              :text="topic.description"
              :maxHeight="maxDescriptionHeight"
            />
          </KGridItem>
        </KGrid>

        <HeaderTabs>
          <HeaderTab
            v-if="topics.length"
            :text="coreString('folders')"
            :to="foldersLink"
          />
          <HeaderTab
            :text="coreString('searchLabel')"
            :to="topics.length ? searchTabLink : {} "
          />
        </HeaderTabs>

          <HeaderTabs>
            <HeaderTab
              v-if="topics.length"
              :text="coreString('folders')"
              :to="foldersLink"
            />
            <HeaderTab
              :text="coreString('searchLabel')"
              :to="searchLink "
            />
          </HeaderTabs>

        <KBreadcrumbs v-if="breadcrumbs.length && windowIsSmall" :items="breadcrumbs" />

            <KButton
              icon="filter"
              class="filter-overlay-toggle-button"
              :text="filterTranslator.$tr('filter')"
              :primary="false"
              @click="$router.push(searchLink)"
            />

                <KRouterLink
                  :text="t.title"
                  :to="genContentLink(t.id)"
                  class="folder-header-link"
                  iconAfter="chevronRight"
                  :appearanceOverrides="{ color: $themeTokens.text }"
                />

              <HybridLearningCardGrid
                v-if="t.children && t.children.length"
                :contents="t.children"
                :numCols="numCols"
                :genContentLink="genContentLink"
                cardViewStyle="card"
                @toggleInfoPanel="toggleInfoPanel"
              />
              <KButton
                v-if="t.showMore"
                class="more-after-grid"
                appearance="basic-link"
                @click="handleShowMore(t.id)"
              >
                {{ $tr('showMore') }}
              </KButton>
              <KRouterLink v-else-if="t.viewAll" class="more-after-grid" :to="t.viewAll">
                {{ $tr('viewAll') }}
              </KRouterLink>
              <KButton
                v-else-if="t.viewMore && t.id !== subTopicLoading"
                class="more-after-grid"
                appearance="basic-link"
                @click="handleLoadMoreinSubtopic(t.id)"
              >
                {{ coreString('viewMoreAction') }}
              </KButton>
              <KCircularLoader v-if="t.id === subTopicLoading" />

            <HybridLearningCardGrid
              v-if="resources.length"
              :contents="resources"
              :numCols="numCols"
              :genContentLink="genContentLink"
              cardViewStyle="card"
              @toggleInfoPanel="toggleInfoPanel"
            />

              <KButton
                v-if="!topicMoreLoading"
                :text="coreString('viewMoreAction')"
                appearance="raised-button"
                :disabled="topicMoreLoading"
                @click="handleLoadMoreInTopic"
              />
              <KCircularLoader v-else />

              {{ more ?
                coreString('overCertainNumberOfSearchResults', { num: results.length }) :
                translator.$tr('results', { results: results.length })
              }}

            <SearchChips
              :searchTerms="searchTerms"
              @removeItem="removeFilterTag"
              @clearSearch="clearSearch"
            />

            <HybridLearningCardGrid
              v-if="results.length"
              :numCols="numCols"
              :cardViewStyle="currentViewStyle"
              :genContentLink="genContentLink"
              :contents="results"
              @toggleInfoPanel="toggleInfoPanel"
            />

              <KButton
                v-if="!moreLoading"
                :text="coreString('viewMoreAction')"
                appearance="basic-link"
                :disabled="moreLoading"
                class="filter-action-button"
                @click="searchMore"
              />
              <KCircularLoader v-else />

            <KCircularLoader
              v-if="searchLoading"
              class="loader"
              type="indeterminate"
              :delay="false"
            />

      <EmbeddedSidePanel
        v-if="!!windowIsLarge || (windowIsMedium && !searchActive)"
        v-model="searchTerms"
        :topicsListDisplayed="!searchActive"
        topicPage="True"
        :topics="topics"
        :activeActivityButtons="activeActivityButtons"
        :activeCategories="activeCategories"
        :topicsLoading="topicMoreLoading"
        :more="topicMore"
        :genContentLink="genContentLink"
        :width="`${sidePanelWidth}px`"
        :availableLabels="labels"
        :showChannels="false"
        position="embedded"
        :style="{ position: 'fixed',
                  marginTop: stickyTop,
                  paddingTop: '24px',
                  paddingBottom: '200px' }"
        @currentCategory="handleShowSearchModal"
        @loadMoreTopics="handleLoadMoreInTopic"
      />

      <FullScreenSidePanel
        v-if="!windowIsLarge && sidePanelIsOpen"
        class="full-screen-side-panel"
        alignment="left"
        :closeButtonHidden="true"
        :sidePanelOverrideWidth="`${sidePanelOverlayWidth}px`"
        @closePanel="$router.push(currentLink)"
      >
        <KIconButton
          v-if="windowIsSmall && !currentCategory"
          class="overlay-close-button"
          icon="close"
          :ariaLabel="coreString('closeAction')"
          :color="$themeTokens.text"
          :tooltip="coreString('closeAction')"
          @click="$router.push(currentLink)"
        />
        <KIconButton
          v-if="windowIsSmall && currentCategory"
          icon="back"
          :ariaLabel="coreString('back')"
          :color="$themeTokens.text"
          :tooltip="coreString('back')"
          @click="closeCategoryModal"
        />
        <EmbeddedSidePanel
          v-if="!currentCategory"
          v-model="searchTerms"
          :topicsListDisplayed="!searchActive"
          topicPage="True"
          :topics="topics"
          :topicsLoading="topicMoreLoading"
          :more="topicMore"
          :genContentLink="genContentLink"
          :width="`${sidePanelOverlayWidth - 64}px`"
          :availableLabels="labels"
          :activeActivityButtons="activeActivityButtons"
          :activeCategories="activeCategories"
          :showChannels="false"
          position="overlay"
          @currentCategory="handleShowSearchModal"
          @loadMoreTopics="handleLoadMoreInTopic"
        />
        <CategorySearchModal
          v-if="currentCategory && windowIsSmall"
          :selectedCategory="currentCategory"
          :numCols="numCols"
          :availableLabels="labels"
          position="fullscreen"
          @cancel="currentCategory = null"
          @input="handleCategory"
        />
      </FullScreenSidePanel>
      <CategorySearchModal
        v-if="(windowIsMedium || windowIsLarge) && currentCategory"
        :selectedCategory="currentCategory"
        :numCols="numCols"
        :availableLabels="labels"
        position="modal"
        @cancel="currentCategory = null"
        @input="handleCategory"
      />

    <FullScreenSidePanel
      v-if="sidePanelContent"
      alignment="right"
      @closePanel="sidePanelContent = null"
    >
      <BrowseResourceMetadata :content="sidePanelContent" :showLocationsInChannel="true" />
    </FullScreenSidePanel>

</template>

<template>

      <CustomContentRenderer :topic="topic" />

        <KGrid>
          <KGridItem
            v-if="!displayingSearchResults"
            class="breadcrumbs"
            :layout4="{ span: 4 }"
            :layout8="{ span: 8 }"
            :layout12="{ span: 12 }"
          >
            <KBreadcrumbs v-if="breadcrumbs.length" :items="breadcrumbs" />
          </KGridItem>
          <KGridItem
            :layout4="{ span: 4 }"
            :layout8="{ span: 8 }"
            :layout12="{ span: 12 }"
          >

              <TextTruncator
                :text="topic.title"
                :maxHeight="maxTitleHeight"
              />

          </KGridItem>

          <KGridItem
            v-if="topic.thumbnail"
            class="thumbnail"
            :layout4="{ span: 1 }"
            :layout8="{ span: 2 }"
            :layout12="{ span: 2 }"
          >
            <CardThumbnail
              class="thumbnail"
              :thumbnail="topic.thumbnail"
              :isMobile="windowIsSmall"
              :showTooltip="false"
              kind="channel"
              :showContentIcon="false"
            />
          </KGridItem>

          <KGridItem
            v-if="topic.description"
            class="text"
            :layout4="{ span: topic.thumbnail ? 3 : 4 }"
            :layout8="{ span: topic.thumbnail ? 6 : 8 }"
            :layout12="{ span: topic.thumbnail ? 10 : 12 }"
          >
            <TextTruncator
              :text="topic.description"
              :maxHeight="maxDescriptionHeight"
            />
          </KGridItem>
        </KGrid>

        <HeaderTabs>
          <HeaderTab
            v-if="topics.length"
            :text="coreString('folders')"
            :to="foldersLink"
          />
          <HeaderTab
            :text="coreString('searchLabel')"
            :to="topics.length ? searchLink : {} "
          />
        </HeaderTabs>

          <HeaderTabs>
            <HeaderTab
              :text="coreString('folders')"
              :to="foldersLink"
            />
            <HeaderTab
              :text="coreString('searchLabel')"
              :to="searchLink "
            />
          </HeaderTabs>

        <slot v-if="windowIsSmall" name="breadcrumbs" class="breadcrumbs"></slot>

            <KButton
              icon="filter"
              class="filter-overlay-toggle-button"
              :text="filterTranslator.$tr('filter')"
              :primary="false"
              @click="$router.push(searchButtonLink)"
            />

                <KRouterLink
                  :text="t.title"
                  :to="genContentLink(t.id)"
                  class="folder-header-link"
                  iconAfter="chevronRight"
                  :appearanceOverrides="{ color: $themeTokens.text }"
                />

              <HybridLearningCardGrid
                v-if="t.children && t.children.length"
                :contents="t.children"
                :numCols="numCols"
                :genContentLink="genContentLink"
                cardViewStyle="card"
                @toggleInfoPanel="toggleInfoPanel"
              />
              <KButton
                v-if="t.showMore"
                class="more-after-grid"
                appearance="basic-link"
                @click="handleShowMore(t.id)"
              >
                {{ $tr('showMore') }}
              </KButton>
              <KRouterLink v-else-if="t.viewAll" class="more-after-grid" :to="t.viewAll">
                {{ $tr('viewAll') }}
              </KRouterLink>
              <KButton
                v-else-if="t.viewMore && t.id !== subTopicLoading"
                class="more-after-grid"
                appearance="basic-link"
                @click="handleLoadMoreinSubtopic(t.id)"
              >
                {{ coreString('viewMoreAction') }}
              </KButton>
              <KCircularLoader v-if="t.id === subTopicLoading" />

            <HybridLearningCardGrid
              v-if="resources.length"
              :contents="resources"
              :numCols="numCols"
              :genContentLink="genContentLink"
              cardViewStyle="card"
              @toggleInfoPanel="toggleInfoPanel"
            />

              <KButton
                v-if="!topicMoreLoading"
                :text="coreString('viewMoreAction')"
                appearance="raised-button"
                :disabled="topicMoreLoading"
                @click="handleLoadMoreInTopic"
              />
              <KCircularLoader v-else />

              {{ more ?
                coreString('overCertainNumberOfSearchResults', { num: results.length }) :
                translator.$tr('results', { results: results.length })
              }}

            <SearchChips
              :searchTerms="searchTerms"
              @removeItem="removeFilterTag"
              @clearSearch="clearSearch"
            />

            <HybridLearningCardGrid
              v-if="results.length"
              :numCols="numCols"
              :cardViewStyle="currentViewStyle"
              :genContentLink="genContentLink"
              :contents="results"
              @toggleInfoPanel="toggleInfoPanel"
            />

              <KButton
                v-if="!moreLoading"
                :text="coreString('viewMoreAction')"
                appearance="basic-link"
                :disabled="moreLoading"
                class="filter-action-button"
                @click="searchMore"
              />
              <KCircularLoader v-else />

            <KCircularLoader
              v-if="searchLoading"
              class="loader"
              type="indeterminate"
              :delay="false"
            />

      <EmbeddedSidePanel
        v-if="!!windowIsLarge || (windowIsMedium && !searchActive)"
        v-model="searchTerms"
        :topicsListDisplayed="!searchActive"
        topicPage="True"
        :topics="topics"
        :activeActivityButtons="activeActivityButtons"
        :activeCategories="activeCategories"
        :topicsLoading="topicMoreLoading"
        :more="topicMore"
        :genContentLink="genContentLink"
        :width="`${sidePanelWidth}px`"
        :availableLabels="labels"
        :showChannels="false"
        position="embedded"
        :style="{ position: 'fixed',
                  marginTop: stickyTop,
                  paddingTop: '24px',
                  paddingBottom: '200px' }"
        @currentCategory="handleShowSearchModal"
        @loadMoreTopics="handleLoadMoreInTopic"
      />

      <FullScreenSidePanel
        v-if="!windowIsLarge && sidePanelIsOpen"
        class="full-screen-side-panel"
        alignment="left"
        :closeButtonHidden="true"
        :sidePanelOverrideWidth="`${sidePanelOverlayWidth}px`"
        @closePanel="$router.push(currentLink)"
      >
        <KIconButton
          v-if="windowIsSmall && !currentCategory"
          class="overlay-close-button"
          icon="close"
          :ariaLabel="coreString('closeAction')"
          :color="$themeTokens.text"
          :tooltip="coreString('closeAction')"
          @click="$router.push(currentLink)"
        />
        <KIconButton
          v-if="windowIsSmall && currentCategory"
          icon="back"
          :ariaLabel="coreString('back')"
          :color="$themeTokens.text"
          :tooltip="coreString('back')"
          @click="closeCategoryModal"
        />
        <EmbeddedSidePanel
          v-if="!currentCategory"
          v-model="searchTerms"
          :topicsListDisplayed="!searchActive"
          topicPage="True"
          :topics="topics"
          :topicsLoading="topicMoreLoading"
          :more="topicMore"
          :genContentLink="genContentLink"
          :width="`${sidePanelOverlayWidth - 64}px`"
          :availableLabels="labels"
          :activeActivityButtons="activeActivityButtons"
          :activeCategories="activeCategories"
          :showChannels="false"
          position="overlay"
          @currentCategory="handleShowSearchModal"
          @loadMoreTopics="handleLoadMoreInTopic"
        />
        <CategorySearchModal
          v-if="currentCategory && windowIsSmall"
          :selectedCategory="currentCategory"
          :numCols="numCols"
          :availableLabels="labels"
          position="fullscreen"
          @cancel="currentCategory = null"
          @input="handleCategory"
        />
      </FullScreenSidePanel>
      <CategorySearchModal
        v-if="(windowIsMedium || windowIsLarge) && currentCategory"
        :selectedCategory="currentCategory"
        :numCols="numCols"
        :availableLabels="labels"
        position="modal"
        @cancel="currentCategory = null"
        @input="handleCategory"
      />

    <FullScreenSidePanel
      v-if="sidePanelContent"
      alignment="right"
      @closePanel="sidePanelContent = null"
    >
      <BrowseResourceMetadata :content="sidePanelContent" :showLocationsInChannel="true" />
    </FullScreenSidePanel>

</template>


fix(menu): Access vue instance instead of rendered node (#126)

<template>
  <div
    class="mdc-menu"
    tabindex="-1"
    @MDCMenu:selected="onSelect"
    @MDCMenu:cancel="onCancel">
    <slot />
  </div>
</template>

<script>
import { MDCMenu } from '@material/menu'

import { baseComponentMixin, themeClassMixin } from '../base'

export default {
  mixins: [baseComponentMixin, themeClassMixin],
  model: {
    prop: 'open',
    event: 'change'
  },
  props: {
    open: {
      type: Boolean,
      default: false
    },
    quickOpen: {
      type: Boolean,
      default: false
    }
  },
  data () {
    return {
      mdcMenu: undefined,
      slotObserver: undefined
    }
  },
  computed: {
    model: {
      get () {
        return this.open
      },
      set (value) {
        this.$emit('change', value)
      }
    }
  },
  watch: {
    open () {
      this.mdcMenu.open = this.open
    },
    quickOpen () {
      this.mdcMenu.quickOpen = this.quickOpen
    }
  },
  mounted () {
    this.updateSlot()
    this.slotObserver = new MutationObserver(() => this.updateSlot())
    this.slotObserver.observe(this.$el, {
      childList: true,
      subtree: true
    })
    this.mdcMenu = MDCMenu.attachTo(this.$el)
  },
  beforeDestroy () {
    this.slotObserver.disconnect()
    this.mdcMenu.destroy()
  },
  methods: {
    updateSlot () {
      if (this.$slots.default) {
        this.$slots.default.map(n => {
          n.componentInstance.$el.classList.add('mdc-menu__items')
          n.componentInstance.$el.setAttribute('role', 'menu')
          n.componentInstance.$el.setAttribute('aria-hidden', 'true')
          n.componentInstance.$children
            .filter(n => n.$el.classList.contains('mdc-list-item'))
            .map(n => {
              n.$el.setAttribute('tabindex', '0')
              n.$el.setAttribute('role', 'menuitem')
            })
        })
      }
    },
    onSelect (event) {
      this.model = false
      this.$emit('select', event.detail)
    },
    onCancel () {
      this.model = false
      this.$emit('cancel')
    }
  }
}
</script>

<template>
  <div
    class="mdc-menu"
    tabindex="-1"
    @MDCMenu:selected="onSelect"
    @MDCMenu:cancel="onCancel">
    <slot />
  </div>
</template>

<script>
import { MDCMenu } from '@material/menu'

import { baseComponentMixin, themeClassMixin } from '../base'

export default {
  mixins: [baseComponentMixin, themeClassMixin],
  model: {
    prop: 'open',
    event: 'change'
  },
  props: {
    open: {
      type: Boolean,
      default: false
    },
    quickOpen: {
      type: Boolean,
      default: false
    }
  },
  data () {
    return {
      mdcMenu: undefined,
      slotObserver: undefined
    }
  },
  computed: {
    model: {
      get () {
        return this.open
      },
      set (value) {
        this.$emit('change', value)
      }
    }
  },
  watch: {
    open () {
      this.mdcMenu.open = this.open
    },
    quickOpen () {
      this.mdcMenu.quickOpen = this.quickOpen
    }
  },
  mounted () {
    this.updateSlot()
    this.slotObserver = new MutationObserver(() => this.updateSlot())
    this.slotObserver.observe(this.$el, {
      childList: true,
      subtree: true
    })
    this.mdcMenu = MDCMenu.attachTo(this.$el)
  },
  beforeDestroy () {
    this.slotObserver.disconnect()
    this.mdcMenu.destroy()
  },
  methods: {
    updateSlot () {
      if (this.$slots.default) {
        this.$slots.default.map(n => {
          n.elm.classList.add('mdc-menu__items')
          n.elm.setAttribute('role', 'menu')
          n.elm.setAttribute('aria-hidden', 'true')
          n.componentOptions.children
            .filter(n => n.elm.className.indexOf('mdc-list-item') > -1)
            .map(n => {
              n.elm.setAttribute('tabindex', '0')
              n.elm.setAttribute('role', 'menuitem')
            })
        })
      }
    },
    onSelect (event) {
      this.model = false
      this.$emit('select', event.detail)
    },
    onCancel () {
      this.model = false
      this.$emit('cancel')
    }
  }
}
</script>