<template>
  <div :class="style['cdr-label-wrapper__container']">
    <label
      :class="mapClasses(style,
                         'cdr-label-wrapper',
                         `cdr-label-wrapper--${background}`,
                         disabledClass,
                         modifierClass,
                         sizeClass,
      ).concat(` ${labelClass || ''}`)"
    >
      <slot name="input" />
      <span :class="style['cdr-label-wrapper__figure']" />
      <div :class="mapClasses(style, 'cdr-label-wrapper__content', contentClass)">
        <slot />
      </div>
    </label>
  </div>
</template>
<script>
import { defineComponent, useCssModule, computed } from 'vue';
import { responsiveModifyClass } from '../../utils/buildClass';
import backgroundProps from '../../props/background';
import mapClasses from '../../utils/mapClasses';

export default defineComponent({
  name: 'CdrLabelWrapper',
  props: {
    labelClass: String,
    contentClass: String,
    background: backgroundProps,
    disabled: Boolean,
    modifier: String,
    size: String,
  },
  setup(props) {
    const baseClass = 'cdr-label-wrapper';
    const modifierClass = computed(() => props.modifier && `${baseClass}--${props.modifier}`);
    const sizeClass = computed(() => props.size && responsiveModifyClass(baseClass, '', props.size));
    const disabledClass = computed(() => props.disabled && `${baseClass}--disabled`);
    return {
      style: useCssModule(),
      mapClasses,
      baseClass,
      modifierClass,
      sizeClass,
      disabledClass,
    };
  },
});
</script>

<style lang="scss" module src="./styles/CdrLabelWrapper.module.scss">
</style>

Update cdr-labelWrapper for <script setup> syntax. Fix CDR-2349 accessibility issue

<template>
  <div :class="style['cdr-label-wrapper__container']">
    <label
      :class="mapClasses(style,
                         'cdr-label-wrapper',
                         `cdr-label-wrapper--${background}`,
                         disabledClass,
                         modifierClass,
                         sizeClass,
      ).concat(` ${labelClass || ''}`)"
    >
      <slot name="input" />
      <span :class="style['cdr-label-wrapper__figure']" />
      <span :class="mapClasses(style, 'cdr-label-wrapper__content', contentClass)">
        <slot />
      </span>
    </label>
  </div>
</template>
<script setup>
  import { useCssModule, computed } from 'vue';
  import { responsiveModifyClass } from '../../utils/buildClass';
  import backgroundProps from '../../props/background';
  import mapClasses from '../../utils/mapClasses';

  const props = defineProps({
      labelClass: String,
      contentClass: String,
      background: backgroundProps,
      disabled: Boolean,
      modifier: String,
      size: String,
  })
  const style = useCssModule();
  const baseClass = 'cdr-label-wrapper';
  const modifierClass = computed(() => props.modifier && `${baseClass}--${props.modifier}`);
  const sizeClass = computed(() => props.size && responsiveModifyClass(baseClass, '', props.size));
  const disabledClass = computed(() => props.disabled && `${baseClass}--disabled`);
</script>

<style lang="scss" module src="./styles/CdrLabelWrapper.module.scss">
</style>


<template>

QR Code

QR Code generator.

      <qrcode-vue :value="value" size="200" level="H"></qrcode-vue>

    <page-footer></page-footer>

</template>

Implement the error correction level select

<template>

QR Code

QR Code generator.

      <qrcode-vue :value="value" size="200" :level="level"></qrcode-vue>

    <page-footer></page-footer>

</template>


<template>

      Text to translate

      Use a space to separate words. Use a full-stop to separate
      sentences.

      <textarea class="textarea"
                placeholder="Text to translate"
                v-model="text">
      </textarea>

        Translate

</template>

Move translate button to right


<template>

    <aui-tabs>
      <aui-tab name="Example">

          <aui-badge>1</aui-badge>
          <aui-badge>2</aui-badge>
          <aui-badge>3</aui-badge>
          <aui-badge>4</aui-badge>
          <aui-badge>5</aui-badge>

      </aui-tab>
      <aui-tab name="Code">

      </aui-tab>
    </aui-tabs>

</template>

Add example of primary badge

<template>

    <aui-tabs>
      <aui-tab name="Example">

          <aui-badge>1</aui-badge>
          <aui-badge class="aui-badge-primary">2</aui-badge>
          <aui-badge>3</aui-badge>
          <aui-badge class="aui-badge-primary">4</aui-badge>
          <aui-badge>5</aui-badge>

      </aui-tab>
      <aui-tab name="Code">

      </aui-tab>
    </aui-tabs>

</template>


<template>

    <button @click="$parent.scrollToTop()">

      Top
    </button>

</template>

Fix back-to-top button transition effect


<template>
  <div>
    <v-toolbar
      fixed
      dark
      dense
      app
      class="primary pointers-please"
      >
      <router-link to='/'>
        <v-toolbar-title class="white--text">
          AIDA
        </v-toolbar-title>
      </router-link>

      <v-spacer/>

      <!-- Link to Dashboard -->
      <v-btn
        icon
        to="/dashboard">
        <v-icon>
          apps
        </v-icon>
      </v-btn>

      <!-- Link to Docs -->
      <v-btn
        icon
        href="https://github.com/alanaberdeen/AIDA/wiki" >
        <v-icon small> fa-book </v-icon>
      </v-btn>

    </v-toolbar>
    <v-container fluid>
      <v-layout row wrap>
        <v-flex xs10 offset-xs1>
          <Treeview />
        </v-flex>
      </v-layout>
    </v-container>
  </div>
</template>

<script>
import { mapState, mapActions } from 'vuex'
import Toolbar from '../header/Toolbar.vue'
import Treeview from './Treeview.vue'

export default {
  components: {
    Toolbar,
    Treeview
  },

  mounted() {
    this.getArrayOfImages()
  },

  methods: {
    ...mapActions({
      getArrayOfImages: 'backend/getArrayOfImages'
    })
  }
}
</script>

Correct for toolbar file refactoring

<template>
  <div>
    <v-toolbar
      fixed
      dark
      dense
      app
      class="primary pointers-please"
      >
      <router-link to='/'>
        <v-toolbar-title class="white--text">
          AIDA
        </v-toolbar-title>
      </router-link>

      <v-spacer/>

      <!-- Link to Dashboard -->
      <v-btn
        icon
        to="/dashboard">
        <v-icon>
          apps
        </v-icon>
      </v-btn>

      <!-- Link to Docs -->
      <v-btn
        icon
        href="https://github.com/alanaberdeen/AIDA/wiki" >
        <v-icon small> fa-book </v-icon>
      </v-btn>

    </v-toolbar>
    <v-container fluid>
      <v-layout row wrap>
        <v-flex xs10 offset-xs1>
          <Treeview />
        </v-flex>
      </v-layout>
    </v-container>
  </div>
</template>

<script>
import { mapState, mapActions } from 'vuex'
import Toolbar from '../toolbar/Toolbar.vue'
import Treeview from './Treeview.vue'

export default {
  components: {
    Toolbar,
    Treeview
  },

  mounted() {
    this.getArrayOfImages()
  },

  methods: {
    ...mapActions({
      getArrayOfImages: 'backend/getArrayOfImages'
    })
  }
}
</script>


<template>

    <div v-for="(column, idx) in columns"
         :key="idx"
         class="flex-md-grow-1"
         :class="{ 'mr-md-2': idx < columns.length - 1, 'ml-md-2': idx > 0 }">
      <content-node v-for="childNode in columnContents[column.slot]"
                    :key="childNode.id"
                    :content-node="childNode" />

</template>

Switch to the Vuetify grid system to avoid resizing columns based on content

<template>
  <v-container fluid class="px-0 py-0">
    <v-row class="mx-0 my-0">
      <v-col v-for="(column, idx) in columns"
             :key="idx"
             cols="12"
             class="col-md">
        <content-node v-for="childNode in columnContents[column.slot]"
                      :key="childNode.id"
                      :content-node="childNode" />
      </v-col>
    </v-row>
  </v-container>
</template>

<script>

import { groupBy, sortBy } from 'lodash'

export default {
  name: 'ColumnLayout',
  components: {
    // Lazy import necessary due to recursive component structure
    ContentNode: () => import('@/components/activity/ContentNode')
  },
  props: {
    contentNode: { type: Object, required: true }
  },
  computed: {
    columns () {
      return this.contentNode.jsonConfig?.columns || []
    },
    columnContents () {
      return groupBy(sortBy(this.contentNode.children().items, 'position'), 'slot')
    }
  }
}
</script>


<template>

 0">
    <ui-icon icon="local_library" />

      {{ value }}

</template>

Add tooltip over “coach” icon

<template>

 0">
    <ui-tooltip trigger="icon" position="top center">
      {{ $tr('coachLabel') }}
    </ui-tooltip>
    <ui-icon
      class="coach-mat-icon"
      ref="icon"
      icon="local_library"
    />

      {{ value }}

</template>


<template>

Admin

</template>

Modify admin dashboard vue component


<template>

            <a href="https://naver.github.io/ngrinder" target="_blank" v-text="`nGrinder v${version}`">

</template>

Make footer have fixed height


<template>

    {{firstWho}}
    <template v-if="hasWhoElse">
      <i18n>and</i18n>
      <tooltip>
        <button type="button" class="gi-is-unstyled gi-is-link-inherit">{{notFirstWho.length}}<i18n>others</i18n></button>
        <template slot="tooltip">
          <p v-for="(name, index) in notFirstWho" :key="`name-${index}`">{{name}}

        </template>
      </tooltip>
    </template>

</template>

Rewrite 'who' variable to avoid confusion


<template>
  <div class="q-pa-md">
    <q-btn color="primary" class="block" icon="alarm" label="Block" />
    <q-btn color="teal" class="block q-mt-md" label="Block" />

    <q-btn color="black" class="full-width q-mt-md" label="Full-width" />

    <q-btn color="primary" label="With Tooltip" class="q-mt-md">
      <q-tooltip>I'm a tooltip</q-tooltip>
    </q-btn>
  </div>
</template>

Tweak buttons "Other options" example

<template>
  <div class="q-pa-md">
    <q-btn color="black" class="full-width" label="Full-width" />

    <q-btn color="primary" label="With Tooltip" class="q-mt-md">
      <q-tooltip content-class="bg-accent">I'm a tooltip</q-tooltip>
    </q-btn>
  </div>
</template>


<template>

        {{ s.label }}

{{ role }}

        <button class="btn btn-primary btn-icon">

        </button>

</template>

Fix “manage users” button in list of services on user page


<template>
  <div>
    <component :is="markdownContent" />
  </div>
</template>

<script>
import ArticleButton from '~/components/ArticleButton.vue';
import ArticleCallout from '~/components/ArticleCallout.vue';
import ArticleDivider from '~/components/ArticleDivider.vue';
import ArticleImage from '~/components/ArticleImage.vue';
import ArticleSpacer from '~/components/ArticleSpacer.vue';
import ArticleSvg from '~/components/ArticleSvg.vue';

export default {
  props: {
    slug: {
      type: String,
      required: true,
    },
    dir: {
      type: String,
      required: true,
    },
  },
  data() {
    return {
      markdownContent: null,
      attributes: null,
    };
  },
  created() {
    this.markdownContent = () => import(`~/content/${this.dir}/${this.slug}.md`).then((md) => {
      this.attributes = md.attributes;
      return {
        extends: md.vue.component,
        components: {
          ArticleButton,
          ArticleCallout,
          ArticleDivider,
          ArticleImage,
          ArticleSpacer,
          ArticleSvg,
        },
      };
    });
  },
};
</script>

Add comment about frontmatter issue.

<template>

    <component :is="markdownContent" />

</template>


<template>

  <UiToolbar
    class="progress-toolbar"
    :style="{ backgroundColor: $themeTokens.appBar }"
    type="clear"
    textColor="white"
    :removeNavIcon="$attrs.removeNavIcon"
  >
    <KIconButton
      slot="icon"
      icon="back"
      color="white"
      :class="{ 'rtl-icon': isRtl }"
      @click="$emit('click_back')"
    >
      <KIcon
        icon="back"
        :color="$themeTokens.textInverted"
        style="top: 0; width: 24px; height: 24px;"
      />
      <span>
        {{ title || coreString('kolibriLabel') }}
      </span>
    </KIconButton>
  </UiToolbar>

</template>

<script>

  import commonCoreStrings from 'kolibri.coreVue.mixins.commonCoreStrings';
  import UiToolbar from 'kolibri.coreVue.components.UiToolbar';

  export default {
    name: 'ProgressToolbar',
    components: {
      UiToolbar,
    },
    mixins: [commonCoreStrings],
    props: {
      title: {
        type: String,
        required: false,
      },
    },
  };

</script>

<style lang="scss" scoped>

  // TODO - refactor to use theme
  .progress-toolbar {
    fill: white;
  }

</style>

Fix AppBar in setup wizard

<template>

  <UiToolbar
    class="progress-toolbar"
    :style="{ backgroundColor: $themeTokens.appBar }"
    type="clear"
    textColor="white"
    :removeNavIcon="$attrs.removeNavIcon"
  >
    <KIconButton
      slot="icon"
      icon="back"
      color="white"
      :class="{ 'rtl-icon': isRtl }"
      @click="$emit('click_back')"
    />
    <span>
      {{ title || coreString('kolibriLabel') }}
    </span>
  </UiToolbar>

</template>

<script>

  import commonCoreStrings from 'kolibri.coreVue.mixins.commonCoreStrings';
  import UiToolbar from 'kolibri.coreVue.components.UiToolbar';

  export default {
    name: 'ProgressToolbar',
    components: {
      UiToolbar,
    },
    mixins: [commonCoreStrings],
    props: {
      title: {
        type: String,
        required: false,
      },
    },
  };

</script>

<style lang="scss" scoped>

  // TODO - refactor to use theme
  .progress-toolbar {
    fill: white;
  }

</style>


<template>
  <component
    :is="tag"
    :class="style[baseClass]"
  >
    <slot />
  </component>
</template>
<script>

import { defineComponent, useCssModule } from 'vue';

export default defineComponent({
  name: 'CdrCard',
  props: {
    tag: {
      type: String,
      default: 'article',
    },
  },
  setup() {
    return {
      style: useCssModule(),
      baseClass: 'cdr-card',
    };
  },
});
</script>

<style lang="scss" module src="./styles/CdrCard.module.scss">
</style>

Refactor cdr-card to use <script setup> syntax

<script setup>
import { useCssModule, defineProps } from 'vue';

const props = defineProps({
    tag: {
      type: String,
      default: 'article',
    }
})

const baseClass = 'cdr-card';
const style = useCssModule();
</script>

<template>
  <component
    :is="tag"
    :class="style[baseClass]"
  >
    <slot />
  </component>
</template>

<style lang="scss" module src="./styles/CdrCard.module.scss">
</style>


<template>

</template>

Add the ability to not fill shapes


<template>

  <div class="activity-chip">
    <KLabeledIcon :label="coreString(kind)" class="activity-chip-label">
      <template #icon>
        <LearningActivityIcon :kind="kind" />
      </template>
    </KLabeledIcon>
  </div>

</template>

<script>

  import { LearningActivities } from 'kolibri.coreVue.vuex.constants';
  import commonCoreStrings from 'kolibri.coreVue.mixins.commonCoreStrings';
  import LearningActivityIcon from './LearningActivityIcon.vue';

  /**
   * Wraps LearningActivityIcon in KLabeledIcon
   */
  export default {
    name: 'LearningActivityChip',
    components: { LearningActivityIcon },
    mixins: [commonCoreStrings],
    props: {
      /**
       * Learning activity constant(s)
       * Can be one constant or an array of constants
       */
      kind: {
        type: String,
        required: true,
        validator(value) {
          return Object.values(LearningActivities).includes(value);
        },
      },
    },
  };

</script>

<style lang="scss" scoped>

  .activity-chip {
    display: inline-block;
    padding: 12px;
    font-weight: bold;
    background-color: #f5f5f5;
    border-radius: 4px;

    /deep/ svg {
      width: 24px;
      height: 24px;
      margin-top: -4px;
      margin-left: -2px;
    }
  }

  .activity-chip-label {
    width: auto;
  }

</style>

Use theme colors in the activity chip

<template>

  <div
    class="activity-chip"
    :style="{ backgroundColor: $themePalette.grey.v_100 }"
  >
    <KLabeledIcon :label="coreString(kind)" class="activity-chip-label">
      <template #icon>
        <LearningActivityIcon :kind="kind" />
      </template>
    </KLabeledIcon>
  </div>

</template>

<script>

  import { LearningActivities } from 'kolibri.coreVue.vuex.constants';
  import commonCoreStrings from 'kolibri.coreVue.mixins.commonCoreStrings';
  import LearningActivityIcon from './LearningActivityIcon.vue';

  /**
   * Wraps LearningActivityIcon in KLabeledIcon
   */
  export default {
    name: 'LearningActivityChip',
    components: { LearningActivityIcon },
    mixins: [commonCoreStrings],
    props: {
      /**
       * Learning activity constant(s)
       * Can be one constant or an array of constants
       */
      kind: {
        type: String,
        required: true,
        validator(value) {
          return Object.values(LearningActivities).includes(value);
        },
      },
    },
  };

</script>

<style lang="scss" scoped>

  .activity-chip {
    display: inline-block;
    padding: 12px;
    font-weight: bold;
    border-radius: 4px;

    /deep/ svg {
      width: 24px;
      height: 24px;
      margin-top: -4px;
      margin-left: -2px;
    }
  }

  .activity-chip-label {
    width: auto;
  }

</style>


<template>

{{ event.semester }} {{ event.year}}

    <scout-registration :event="event"
                        :preferences="registrationPreferences"
                        :purchases="registrationPurchases"
                        :assignments="registrationAssignments"></scout-registration>

</template>

Allow editing scouts from details page

<template>

      {{ event.semester }} {{ event.year}}
      <button class="edit-button button is-pulled-right"
              data-balloon="Edit Assignments"
              data-balloon-pos="left"
              @click.prevent="toggleEditing()"
              v-if="!editing">

      </button>

    <assignment-edit v-if="editing"
                     :scout="scout"
                     :event="event"
                     :preferences="registration.preferences"
                     :registration="registration"
                     @done="toggleEditing()"></assignment-edit>
    <scout-registration v-else
                        :event="event"
                        :preferences="registrationPreferences"
                        :purchases="registrationPurchases"
                        :assignments="registrationAssignments">
    </scout-registration>

</template>


<template lang="jade">
    li(v-bind:class='classObj')
        a(v-on:click='onClick()') {{ func.callingConvention == 'Stdcall' ? 'S' : '?' }} {{ func.name }}
</template>

<style lang="scss" scoped>
li {
    display: block;
}

.decompile-fail {
    background-color: darkred;
}
</style>

<script lang="ts">
module.exports = {
    props: ['func'],
    computed: {
        classObj() {
            return {
                'decompile-fail': this.func.status != 'BoundsResolved'
            }
        },
    },
    methods: {
        onClick() {
            this.$emit('functionClick', this.func)
        }
    },
}
</script>

Change function view font to monospace

<template lang="jade">
    li(v-bind:class='classObj')
        a(v-on:click='onClick()') {{ func.callingConvention == 'Stdcall' ? 'S' : '?' }} {{ func.name }}
</template>

<style lang="scss" scoped>
li {
    display: block;
    font-family: 'Courier New', Courier, monospace;
    font-size: 9pt;
}

.decompile-fail {
    background-color: darkred;
}
</style>

<script lang="ts">
module.exports = {
    props: ['func'],
    computed: {
        classObj() {
            return {
                'decompile-fail': this.func.status != 'BoundsResolved'
            }
        },
    },
    methods: {
        onClick() {
            this.$emit('functionClick', this.func)
        }
    },
}
</script>


<template>

                Allow new tags

Tags:{{ tags }}

</template>

Fix prop name in tag autocomplete example


<template>
    <nav class="mdl-navigation" ref="navigation">
        <slot></slot>
    </nav>
</template>

<script>
export default {
    name: "app-navigation",
    mounted() {
        for (let el of this.$refs.navigation.querySelectorAll("a")) {
            el.classList.add("mdl-navigation__link")
        }
    }
}
</script>

<style scoped>

</style>

Fix a bug in navigation component

<template>
    <nav class="mdl-navigation" ref="navigation">
        <slot></slot>
    </nav>
</template>

<script>
export default {
    name: "app-navigation",
    mounted() {
        this.update()
    },
    updated() {
        this.update()
    },
    methods: {
        update() {
            for (let el of this.$refs.navigation.querySelectorAll("a")) {
                el.classList.add("mdl-navigation__link")
            }
        }
    }
}
</script>

<style scoped>

</style>


<template>

{{ schedule.game_mode.name }}

        <Stage :stage="schedule.stage_a"></Stage>
        <Stage :stage="schedule.stage_b"></Stage>

</template>

Add rule name to each schedule box

<template>

            {{ schedule.game_mode.name }}

            {{ schedule.rule.name }}

        <Stage :stage="schedule.stage_a"></Stage>
        <Stage :stage="schedule.stage_b"></Stage>

</template>


<template>

  <CoreBase>

    <KPageContainer>

page 1

    </KPageContainer>

  </CoreBase>

</template>

Complete UI on immersive toolbar and bottom app bar

<template>

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

    <KPageContainer>
      <h1>{{ $tr('selectChannelQuizLabel') }}</h1>
      <ContentCardList
        :contentList="filteredContentList"
        :viewMoreButtonState="viewMoreButtonState"
        :contentCardMessage="selectionMetadata"
        :contentCardLink="contentLink"
        @moreresults="handleMoreResults"
      />

      <BottomAppBar>
        <KButtonGroup>
          <KRouterLink
            appearance="flat-button"
            :text="coreString('cancelAction')"
            :to="toolbarRoute"
          />
          <KButton
            :text="coreString('finishAction')"
            primary
            @click="finishProcess"
          />
        </KButtonGroup>
      </BottomAppBar>
    </KPageContainer>

  </CoreBase>

</template>

<script>

  import { mapState } from 'vuex';
  import responsiveWindowMixin from 'kolibri.coreVue.mixins.responsiveWindowMixin';
  import BottomAppBar from 'kolibri.coreVue.components.BottomAppBar';
  import commonCoreStrings from 'kolibri.coreVue.mixins.commonCoreStrings';
  import ContentCardList from '../../plan/LessonResourceSelectionPage/ContentCardList';
  import commonCoach from '../../common';

  export default {
    name: 'CreateChannelQuizPage',
    components: {
      BottomAppBar,
      ContentCardList,
    },
    mixins: [commonCoreStrings, commonCoach, responsiveWindowMixin],
    data() {
      return {
        // showError: false,
      };
    },
    computed: {
      ...mapState(['toolbarRoute']),
    },
    methods: {
      finishProcess() {
        return;

        // this.$router.push({ name: PageNames.EXAM_CREATION_QUESTION_SELECTION });
      },
    },
    $trs: {
      createNewExamLabel: 'Create new quiz',
      selectChannelQuizLabel: 'Select a channel quiz',
      // noneSelected: 'No exercises are selected',
    },
  };

</script>

<style lang="scss" scoped>

  .search-box {
    display: inline-block;
    vertical-align: middle;
  }

</style>


<template>
  <span>
    <v-icon v-if="elasticsearchNode.master" title="master node">mdi-star</v-icon>
    <v-icon v-else-if="elasticsearchNode.masterEligible" style="width: 24px"
            title="master eligible">mdi-star-bordered</v-icon>
    <v-icon v-if="elasticsearchNode.dataNode" title="data node">mdi-content-save</v-icon>
    <v-icon v-if="elasticsearchNode.ingestNode" title="ingest node">mdi-arrow-right-bold-box</v-icon>
    <v-icon v-if="elasticsearchNode.coordinatingNode"
            title="coordinating only node">mdi-transit-connection-variant</v-icon>
  </span>
</template>

<script>
  import ElasticsearchNode from '../../models/ElasticsearchNode'

  export default {
    name: 'node-icons',
    props: {
      elasticsearchNode: {
        type: ElasticsearchNode,
        default: () => {
          return new ElasticsearchNode()
        }
      }
    }
  }
</script>

Fix master eligible icon to star outline

<template>
  <span>
    <v-icon v-if="elasticsearchNode.master" title="master node">mdi-star</v-icon>
    <v-icon v-else-if="elasticsearchNode.masterEligible" style="width: 24px"
            title="master eligible">mdi-star-outline</v-icon>
    <v-icon v-if="elasticsearchNode.dataNode" title="data node">mdi-content-save</v-icon>
    <v-icon v-if="elasticsearchNode.ingestNode" title="ingest node">mdi-arrow-right-bold-box</v-icon>
    <v-icon v-if="elasticsearchNode.coordinatingNode"
            title="coordinating only node">mdi-transit-connection-variant</v-icon>
  </span>
</template>

<script>
  import ElasticsearchNode from '../../models/ElasticsearchNode'

  export default {
    name: 'node-icons',
    props: {
      elasticsearchNode: {
        type: ElasticsearchNode,
        default: () => {
          return new ElasticsearchNode()
        }
      }
    }
  }
</script>


<template>
    <div class="kiwi-typinguserslist">
        <span
            v-for="(user, idx) in typingUsers"
            :key="user.nick"
            :style="{ color: userColour(user) }"
        >
            {{ user.nick }}{{ typingUsers.length - 1 > idx ? ',' : '' }}
        </span> {{ typingUsers.length > 0 ? '…' : '' }}
    </div>
</template>
<script>
'kiwi public';

import TypingStatusIndicator from './TypingStatusIndicator';

export default {
    components: {
        TypingStatusIndicator,
    },
    props: ['buffer'],
    computed: {
        users() {
            if (this.buffer.isQuery()) {
                // if this is a query return the target as users
                let network = this.$state.getActiveNetwork();
                let user = this.$state.getUser(network.id, this.buffer.name);
                return { [user.nick]: user };
            }
            return this.buffer.users;
        },
        typingUsers() {
            let myNick = this.$state.getActiveNetwork().nick;
            return Object.values(this.users).filter(u => u.nick !== myNick
                && u.typingStatus(this.buffer.name).status);
        },
    },
    methods: {
        userColour(user) {
            return user && this.buffer.setting('colour_nicknames_in_messages') ? user.getColour() : '';
        },
    },
};
</script>
<style>

.kiwi-typinguserslist {
    font-size: 0.9em;
}

</style>

Fix error when opening query to user not on server

<template>
    <div class="kiwi-typinguserslist">
        <span
            v-for="(user, idx) in typingUsers"
            :key="user.nick"
            :style="{ color: userColour(user) }"
        >
            {{ user.nick }}{{ typingUsers.length - 1 > idx ? ',' : '' }}
        </span> {{ typingUsers.length > 0 ? '…' : '' }}
    </div>
</template>
<script>
'kiwi public';

import TypingStatusIndicator from './TypingStatusIndicator';

export default {
    components: {
        TypingStatusIndicator,
    },
    props: ['buffer'],
    computed: {
        users() {
            if (this.buffer.isQuery()) {
                // if this is a query return the target as users
                let network = this.$state.getActiveNetwork();
                let user = this.$state.getUser(network.id, this.buffer.name);
                return user ? { [user.nick]: user } : {};
            }
            return this.buffer.users;
        },
        typingUsers() {
            let myNick = this.$state.getActiveNetwork().nick;
            return Object.values(this.users).filter(u => u.nick !== myNick
                && u.typingStatus(this.buffer.name).status);
        },
    },
    methods: {
        userColour(user) {
            return user && this.buffer.setting('colour_nicknames_in_messages') ? user.getColour() : '';
        },
    },
};
</script>
<style>

.kiwi-typinguserslist {
    font-size: 0.9em;
}

</style>


<template>
  <v-chip
    v-if="!cat._meta.loading"
    :color="cat.color"
    :text-color="textColor"
    v-bind="$attrs"
    v-on="$listeners"
  >
    <slot>
      {{ cat.short }}
    </slot>
  </v-chip>
</template>

<script>
import { contrastColor } from '@/common/helpers/colors.js'
import Color from 'colorjs.io'

export default {
  name: 'CategoryChip',
  props: {
    scheduleEntry: {
      type: Object,
      required: false,
      default: null,
    },
    category: {
      type: Object,
      required: false,
      default: null,
    },
  },
  computed: {
    cat() {
      return this.category || this.scheduleEntry.activity().category()
    },
    textColor() {
      return contrastColor(new Color(this.cat.color)).toString({ format: 'hex' })
    },
  },
}
</script>

Create dense variant of category chip

<template>
  <v-chip
    v-if="!loading"
    :color="cat.color"
    :text-color="textColor"
    :class="{ 'v-chip--dense': dense }"
    v-bind="$attrs"
    v-on="$listeners"
  >
    <slot>

        {{ cat.name }}

        {{ cat.short }}

    </slot>
  </v-chip>
</template>


<template>
  <ui-textbox
      :label='vm.title'
      floating-label
      :multi-line='true'
      v-model='vm.value'></ui-textbox>
</template>
<script>
import { UiTextbox } from 'keen-ui';

export default {
  name: 'MultiLineText',
  props: ['vm'],
  components: {
    UiTextbox
  }
};
</script>

Use one row for multiline inputs

<template>
  <ui-textbox
      class='multi-line-goal'
      :label='vm.title'
      floating-label
      :rows='1'
      :multi-line='true'
      v-model='vm.value'></ui-textbox>
</template>
<script>
import { UiTextbox } from 'keen-ui';

export default {
  name: 'MultiLineText',
  props: ['vm'],
  components: {
    UiTextbox
  }
};
</script>
<style>
.multi-line-goal.ui-textbox.has-floating-label .ui-textbox__label-text.is-inline {
  transform: translateY(1.3rem) scale(1.1);
}
</style>


<template>

Fragments

      <input type="text" v-model="search" placeholder="Search...">

        {{ fragment['schema:name'] || 'Unnamed' }}
        {{ uri }}

</template>

Add dcterms:title fallback in recent fragments

<template>

Fragments

      <input type="text" v-model="search" placeholder="Search...">

        {{ fragment['schema:name'] || fragment['dcterms:title'] || 'Unnamed' }}
        {{ uri }}

</template>


<template>

    <core-servers-type-generic v-if="this.server" :server="server"></core-servers-type-generic>

</template>

Update to use new endpoint


<template>

      <Player v-for="player in players" :key="player['.key']" :player="player" />

</template>

Sort players by qualifying on frontend

<template>

      <Player v-for="player in sortedPlayers" :key="player['.key']" :player="player" />

</template>


<template>
  <div class='user'>
      <ui-button color='default' type='secondary' has-dropdown ref='profileButton' v-if='profile'>
          <ui-menu contain-focus has-icons has-secondary-text slot="dropdown" :options="profileOptions"
                  @select='onMenuClick'
                  @close="$refs.profileButton.closeDropdown()">
          </ui-menu>
        {{profile.name}}
      </ui-button>
  </div>
</template>

<script>
import { UiButton, UiMenu } from 'keen-ui';

export default {
  name: 'UserInfo',
  props: ['profile'],
  data() {
    return {
      profileOptions: [{
        id: 'sign-out',
        label: 'Sign out'
      }]
    };
  },
  methods: {
    onMenuClick(option) {
      if (option.id === 'sign-out') {
        this.$emit('signOut');
      }
    }
  },
  components: {
    UiButton,
    UiMenu
  }
};
</script>

<style scoped>
.user1 {
  -webkit-background-size: 32px 32px;
  background-size: 32px 32px;
  -webkit-border-radius: 50%;
  border-radius: 50%;
  display: block;
  margin: -1px;
  overflow: hidden;
  position: relative;
  height: 32px;
  width: 32px;
  z-index: 0;
}
</style>

Use icon instead of name on avatar

<template>
  <div class='user'>
      <ui-button color='default' type='secondary' has-dropdown ref='profileButton' v-if='profile' class='avatar-container' :disableRipple='true'>
          <ui-menu contain-focus has-icons has-secondary-text slot="dropdown" :options="profileOptions"
                  @select='onMenuClick'
                  @close="$refs.profileButton.closeDropdown()">
          </ui-menu>
          <img :src='profile.image' class='avatar'>
      </ui-button>
  </div>
</template>

<script>
import { UiButton, UiMenu } from 'keen-ui';

export default {
  name: 'UserInfo',
  props: ['profile'],
  methods: {
    onMenuClick(option) {
      if (option.id === 'sign-out') {
        this.$emit('signOut');
      }
    }
  },
  computed: {
    profileOptions() {
      if (!this.profile) return [];

      return [{
        label: 'Signed in as ' + this.profile.name,
        disabled: true
      }, {
        type: 'divider'
      }, {
        id: 'sign-out',
        label: 'Sign out'
      }];
    }
  },
  components: {
    UiButton,
    UiMenu
  }
};
</script>

<style lang='stylus' scoped>
.avatar-container {
  padding: 0;
  min-width: inherit;
  &:hover {
    background-color: transparent;
  }
}
.avatar-container.has-dropdown-open {
    background-color: transparent;
}
.avatar {
  width: 20px;
  border-radius: 5px;
}
</style>


<template>

</template>

Use new path to assets


<template>

    <template v-if="userStore.loggedIn">

Logout

    </template>

      Login

</template>

Add an alt for the user avatar image


<template>

Wreck Sam

            <router-link :to="{ name: 'PlayScreen' }" class="btn btn-danger btn-lg disabled">

                Play
            </router-link>

            <router-link :to="{ name: 'TutorialScreen' }" class="btn btn-dark btn-lg">

                Tutorial
            </router-link>

            <router-link :to="{ name: 'SettingsScreen' }" class="btn btn-dark btn-lg">

                Settings
            </router-link>

            <router-link :to="{ name: 'CreditsScreen' }" class="btn btn-dark btn-lg">

                Credits
            </router-link>

</template>

Remove disabled from main menu play

<template>

Wreck Sam

            <router-link :to="{ name: 'PlayScreen' }" class="btn btn-danger btn-lg">

                Play
            </router-link>

            <router-link :to="{ name: 'TutorialScreen' }" class="btn btn-dark btn-lg">

                Tutorial
            </router-link>

            <router-link :to="{ name: 'SettingsScreen' }" class="btn btn-dark btn-lg">

                Settings
            </router-link>

            <router-link :to="{ name: 'CreditsScreen' }" class="btn btn-dark btn-lg">

                Credits
            </router-link>

</template>


<template>
  <div class="offset-top container">
    <button type="button" class="btn btn-block btn-success" @click="updateStore()">
      Test Validating AnnotateAndSegment route
    </button>
    <nodule-list annotate="true"></nodule-list>
  </div>
</template>

<script>
import NoduleList from '../components/annotate-and-segment/NoduleList'

import Store from '../store'

export default {
  name: 'annotate-and-segment',
  components: {
    NoduleList
  },
  updateStore () {
    // Using a true boolean for now, but it can be anything
    Store.setRouteResult(this.$route.path, true)
  }
}
</script>

Put the vue method where it belong

<template>
  <div class="offset-top container">
    <button type="button" class="btn btn-block btn-success" @click="updateStore()">
      Test Validating AnnotateAndSegment route
    </button>
    <nodule-list annotate="true"></nodule-list>
  </div>
</template>

<script>
import NoduleList from '../components/annotate-and-segment/NoduleList'

import Store from '../store'

export default {
  name: 'annotate-and-segment',
  components: {
    NoduleList
  },
  methods: {
    updateStore () {
      // Using a true boolean for now, but it can be anything
      Store.setRouteResult(this.$route.path, true)
    }
  }
}
</script>


<template>

    <Navbar></Navbar>

      <Sidebar class="side"></Sidebar>
      <mainFooter class="side"></mainFooter>
      <router-view class="main-container"></router-view>

</template>

Check login will ignore at registe page.


<template>

            <input type='text' class="form-control" />

</template>

Remove the form-group wrapper div from the template so the consumer can decide whether or not to have spacing between the input


<template>
  <div>
    <userInputs v-model="action"></userInputs>
    <documents :action="action"></documents>
  </div>
</template>

<script>
import documents from './Documents';
import userInputs from './UserInputs';

export default {
  components: {
    documents,
    userInputs,
  },
  data() {
    return {
      action: '',
    };
  },
  watch: {
  }
};

</script>

<style scoped lang="scss">
@import 'src/assets/styles/variables.scss';

.documents {
  height: calc(100% - #{$navbar-height});
}
</style>

Remove unnecessary documents class css styling

<template>
  <div>
    <userInputs v-model="action"></userInputs>
    <documents :action="action"></documents>
  </div>
</template>

<script>
import documents from './Documents';
import userInputs from './UserInputs';

export default {
  components: {
    documents,
    userInputs,
  },
  data() {
    return {
      action: '',
    };
  },
  watch: {
  }
};

</script>

<style scoped lang="scss">
@import 'src/assets/styles/variables.scss';

</style>


<template>

    {{ linkitem.url }}

{{ linkitem.annotation }}

    <saw-delete-link :link-id="linkitem.id"></saw-delete-link>

</template>

Fix link opening in new tab instead of current tab


<template>
  <v-toolbar fixed class="cyan" dark>
    <v-toolbar-title class="mr-4">
      TabTracker
    </v-toolbar-title>

    <v-spacer></v-spacer>

    <v-toolbar-items>
      <v-btn flat dark to="login">
        Login
      </v-btn>
    </v-toolbar-items>

    <v-toolbar-items>
      <v-btn flat dark to="register">
        Sign Up
      </v-btn>
    </v-toolbar-items>
  </v-toolbar>
</template>

Hide sinup & login menu when user logged in

<template>
  <v-toolbar fixed class="cyan" dark>
    <v-toolbar-title class="mr-4">
      TabTracker
    </v-toolbar-title>

    <v-spacer></v-spacer>

    <v-toolbar-items>
      <v-btn flat dark to="login" v-if="!$store.state.isUserLoggedIn">
        Login
      </v-btn>
    </v-toolbar-items>

    <v-toolbar-items>
      <v-btn flat dark to="register" v-if="!$store.state.isUserLoggedIn">
        Sign Up
      </v-btn>
    </v-toolbar-items>
  </v-toolbar>
</template>


<template>

    <router-link :to="linkRoute">
      {{title}}

      {{year}} • {{publisher}} • {{runtime}} • {{pubTime}}
    </router-link>

</template>

Add type icon (video only)


<template>

    <game></game>

      <button v-on:click="clearAll">Clear All !</button>

</template>

Fix player vue on admin page

<template>

    <game :players="players"></game>

      <button v-on:click="clearAll">Clear All !</button>

</template>


<template>

  <button aria-label="Search" class='search-btn' :class="{ active: searchOpen }" @click='toggleSearch'>
    <svg src="search.svg"></svg>
  </button>

</template>

<script>

  const actions = require('../../actions');

  module.exports = {
    vuex: {
      getters: {
        searchOpen: state => state.searchOpen,
      },
      actions: {
        toggleSearch: actions.toggleSearch,
      },
    },
  };

</script>

<style lang="stylus" scoped>

  @require '~core-theme.styl'

  button
    border: none
    height: 36px
    width: 36px
    background-color: $core-bg-canvas

    svg
      fill: $core-text-annotation
    &.active
      svg
        fill: $core-action-normal

</style>

Update search button vue so the SVG icon is now a slot

<template>

  <button aria-label="Search" class='search-btn' :class="{ active: searchOpen }" @click='toggleSearch'>
    <slot></slot>
  </button>

</template>

<script>

  const actions = require('../../actions');

  module.exports = {
    vuex: {
      getters: {
        searchOpen: state => state.searchOpen,
      },
      actions: {
        toggleSearch: actions.toggleSearch,
      },
    },
  };

</script>

<style lang="stylus" scoped>

  @require '~core-theme.styl'

  button
    border: none
    height: 36px
    width: 36px
    background-color: $core-bg-canvas

    svg
      fill: $core-text-annotation
    &.active
      svg
        fill: $core-action-normal

</style>


<template>
<div class="start_over">
  <button @click="reset();$awa( { 'referrerUri': 'https://preview.pwabuilder.com/manifest/generator-startover-trigger' }); class="pwa-button pwa-button--simple">
    {{ $t('publish.start_over') }}
  </button>
</div>
</template>

<script lang="ts">
import Vue from 'vue';
import { Action, namespace } from 'vuex-class';
import Component from 'nuxt-class-component';

import * as publish from '~/store/modules/publish';

const PublishAction = namespace(publish.name, Action);

@Component()
export default class extends Vue {
  @PublishAction resetAppData;

  public reset(): void {
    this.resetAppData();
    this.$router.push({
        path: this.$i18n.path('')
    });
  }
}
</script>

<style lang="scss" scoped>
@import '~assets/scss/base/variables';

.start_over {
  background-color: $color-foreground-brighter;
  display: block;
  padding: 2rem;
  text-align: center;
}
</style>

Fix 'Start Over" button issue.

<template>
<div class="start_over">
  <button @click="reset();$awa( { 'referrerUri': 'https://preview.pwabuilder.com/manifest/generator-startover-trigger' });" class="pwa-button pwa-button--simple">
    {{ $t('publish.start_over') }}
  </button>
</div>
</template>

<script lang="ts">
import Vue from 'vue';
import { Action, namespace } from 'vuex-class';
import Component from 'nuxt-class-component';

import * as publish from '~/store/modules/publish';

const PublishAction = namespace(publish.name, Action);

@Component()
export default class extends Vue {
  @PublishAction resetAppData;

  public reset(): void {
    this.resetAppData();
    this.$router.push({
        path: this.$i18n.path('')
    });
  }
}
</script>

<style lang="scss" scoped>
@import '~assets/scss/base/variables';

.start_over {
  background-color: $color-foreground-brighter;
  display: block;
  padding: 2rem;
  text-align: center;
}
</style>


<template>
  <div v-if="user" class="user-popover">
    {{ user.displayedName }}
    <ly-button type="ghost" @click="logout">
      {{ $t('users.popover.logout') }}
    </ly-button>
  </div>
</template>

<script>
  import { mapGetters } from 'vuex'

  export default {
    computed: {
      ...mapGetters({
        user: 'users/current',
      }),
    },

    methods: {
      logout () {
        this.$store.dispatch('users/logout')
        window.location = '/'
      },
    },
  }
</script>

Prepare app header to accept link to profile

<template>
  <ly-popover v-if="user">
    <ly-button
      slot="toggle"
      type="ghost"
      icon="user"
    >
      {{ user.displayedName }}
    </ly-button>

    <template slot="menu">
      <ly-popover-item @click="logout">
        {{ $t('users.popover.logout') }}
        <ly-icon name="sign-out" class="float-right"></ly-icon>
      </ly-popover-item>
    </template>
  </ly-popover>
</template>

<script>
  import { mapGetters } from 'vuex'

  export default {
    computed: {
      ...mapGetters({
        user: 'users/current',
      }),
    },

    methods: {
      logout () {
        this.$store.dispatch('users/logout')
        window.location = '/'
      },
    },
  }
</script>

<style scoped>
  .ly-icon.float-right {
    float: right;
  }
</style>


<template>
  <loading-card :loading="loading" class="metric px-6 py-4 relative">

{{ card.name }}

{{ value }}

  </loading-card>
</template>

Support ranges on text metrics

<template>
  <loading-card :loading="loading" class="metric px-6 py-4 relative">

{{ card.name }}

      <select v-if="card.ranges.length > 0" @change="refresh" class="ml-auto min-w-24 h-6 text-xs no-appearance bg-40">
        <option v-for="range in card.ranges" :key="range.value" :value="range.value" :selected="range.value == selectedRange">{{ range.label }}</option>
      </select>

{{ value }}

  </loading-card>
</template>


<template>
  <div class="row no-span">
    <template v-for="action in actions">
      <q-separator v-if="action.name == 'separator'" :key="key(action)" vertical inset />
      <q-btn v-else
        :key="key(action)"
        :icon="action.icon"
        :color="action.warning ? 'red' : ''"
        flat
        round
        :dense="dense"
        @click="onActionTriggered(action)">
        <q-tooltip>
          {{action.warning ? action.warning : action.label}}
        </q-tooltip>
      </q-btn>
    </template>
  </div>
</template>

<script>
import { uid } from 'quasar'

export default {
  name: 'k-tool-bar',
  props: {
    actions: {
      type: Array,
      default: () => { return [] }
    },
    dense: {
      type: Boolean,
      default: false
    }
  },
  methods: {
    key (action) {
      return action.name + '-' + uid()
    },
    onActionTriggered (action) {
      // If a handler is given call it
      if (action.handler) action.handler()
      // If a route is given activate it
      else if (action.route) this.$router.push(action.route)
    }
  }
}
</script>

Set toolbar button color to secondary be default

<template>
  <div class="row no-span">
    <template v-for="action in actions">
      <q-separator v-if="action.name == 'separator'" :key="key(action)" vertical inset />
      <q-btn v-else
        :key="key(action)"
        :icon="action.icon"
        :color="action.warning ? 'red' : 'primary'"
        flat
        round
        :dense="dense"
        @click="onActionTriggered(action)">
        <q-tooltip>
          {{action.warning ? action.warning : action.label}}
        </q-tooltip>
      </q-btn>
    </template>
  </div>
</template>

<script>
import { uid } from 'quasar'

export default {
  name: 'k-tool-bar',
  props: {
    actions: {
      type: Array,
      default: () => { return [] }
    },
    dense: {
      type: Boolean,
      default: false
    }
  },
  methods: {
    key (action) {
      return action.name + '-' + uid()
    },
    onActionTriggered (action) {
      // If a handler is given call it
      if (action.handler) action.handler()
      // If a route is given activate it
      else if (action.route) this.$router.push(action.route)
    }
  }
}
</script>


<template>

            {{ $t(database.name) }}

    <transition name="fade">
      <selector
        v-if="show"
        :db_name="database"
        @close="closeModal()"/>
    </transition>

</template>

Fix translate for database selector


<template>

      <status-icon
        status="failed"
      />
      <button
        type="button"
        class="btn btn-success btn-sm"
        disabled="true"
      >
        {{ s__("mrWidget|Merge") }}
      </button>

        {{ s__("mrWidget|This project is archived, write access has been disabled") }}

</template>

Fix conflict with ee icon

<template>

      <status-icon
        status="warning"
      />
      <button
        type="button"
        class="btn btn-success btn-sm"
        disabled="true"
      >
        {{ s__("mrWidget|Merge") }}
      </button>

        {{ s__("mrWidget|This project is archived, write access has been disabled") }}

</template>


<template>
  <panel title="Search">
    <v-text-field
      v-model="search"
      label="Search by song title, artist, album or genre"
    ></v-text-field>
  </panel>
</template>

<script>
export default {
  data () {
    return {
      search: ''
    }
  },
  watch: {
    search (value) {
      const route = {
        name: 'songs'
      }
      if (this.search !== '') {
        route.query = {
          search: this.search
        }
      }
      this.$router.push(route)
    },
    '$route.query.search': {
      immediate: true,
      handler (value) {
        this.search = value
      }
    }
  }
}
</script>

<style scoped>
</style>

Use lodash debounce method for delay server request when user is typing too fast that might hang

<template>
  <panel title="Search">
    <v-text-field
      v-model="search"
      label="Search by song title, artist, album or genre"
    ></v-text-field>
  </panel>
</template>

<script>
import _ from 'lodash'
export default {
  data () {
    return {
      search: ''
    }
  },
  watch: {
    search: _.debounce(async function (value) {
      const route = {
        name: 'songs'
      }
      if (this.search !== '') {
        route.query = {
          search: this.search
        }
      }
      this.$router.push(route)
    }, 700),
    '$route.query.search': {
      immediate: true,
      handler (value) {
        this.search = value
      }
    }
  }
}
</script>

<style scoped>
</style>


<template>

    <navbar></navbar>

    <selectors></selectors>

            <panels></panels>

    <foot></foot>
</template>

Change accent + svg text selection


<script>
'use strict'

import moment from 'moment'

export default {
  name: 'date-from-now',
  props: {
    tag: {
      type: String,
      default: 'span'
    },
    value: {
      type: Number,
      default: 0
    },
    interval: {
      type: Number,
      default: 10000
    }
  },
  data () {
    const dt = moment(this.value)
    return {
      fromNow: dt.fromNow(),
      actual: dt.format('LLLL'),
      intervalId: null
    }
  },
  mounted () {
    this.intervalId = setInterval(this.updateFromNow, this.interval)
    this.$watch('value', this.updateFromNow)
  },
  beforeDestroy () {
    clearInterval(this.intervalId)
  },
  methods: {
    updateFromNow () {
      let newFromNow = moment(this.value).fromNow()
      if (newFromNow !== this.fromNow) this.fromNow = newFromNow
    }
  },
  render (createElement) {
    return createElement(this.tag, {
      domProps: {
        title: this.actual,
        innerText: this.fromNow
      }
    })
  }
}
</script>

Update tooltip on date changed

<script>
'use strict'

import moment from 'moment'

export default {
  name: 'date-from-now',
  props: {
    tag: {
      type: String,
      default: 'span'
    },
    value: {
      type: Number,
      default: 0
    },
    interval: {
      type: Number,
      default: 10000
    }
  },
  data () {
    return {
      fromNow: moment(this.value).fromNow(),
      intervalId: null
    }
  },
  computed: {
    actual () {
      return moment(this.value).format('LLLL')
    }
  },
  mounted () {
    this.intervalId = setInterval(this.updateFromNow, this.interval)
    this.$watch('value', this.updateFromNow)
  },
  beforeDestroy () {
    clearInterval(this.intervalId)
  },
  methods: {
    updateFromNow () {
      let newFromNow = moment(this.value).fromNow()
      if (newFromNow !== this.fromNow) this.fromNow = newFromNow
    }
  },
  render (createElement) {
    return createElement(this.tag, {
      domProps: {
        title: this.actual,
        innerText: this.fromNow
      }
    })
  }
}
</script>


<template>

        <router-link to="/">Index</router-link>

        <router-link to="/task-list">Task List</router-link>

    <router-view></router-view>

</template>

Remove redundant reference to shared Store in app component


<template>

{{ post_title }}

      <date-time-stamp :date="post_date" />

      <vue-disqus
        :shortname="disqus_shortname"
        :identifier="post_uid"
        :url="`${site_root_url}/${$nuxt.$route.path}`"
      ></vue-disqus>

</template>

Fix dependency issues and remove console statement 🧹


<template>

{{ receivedAtFormatted }}

</template>

Use .toLocaleDateString() to format date in notification page


<template>
  <div id="app">
    <navbar :home="true"></navbar>

    <disciplines/>
  </div>
</template>

<script>
import Disciplines from './components/summary/List'
import Navbar from './components/Navbar'

export default {
  name: 'app',
  components: {
    Navbar, Disciplines
  }
}
</script>

<style>
  *, *::before, *::after {
    box-sizing: inherit;
  }

  html, body, #app {
    height: 100%;
  }

  html {
    box-sizing: border-box;
  }

  body {
    margin: 0;
    padding: 0;
    font-family: PT Sans,sans-serif;
    background-color: rgba(231, 231, 231, 0.29);
  }

  #app {
    display: flex;
    flex-direction: column;
    align-items: stretch;
  }

  #app > * {
    flex-shrink: 0;
  }

  a {
    color: inherit;
    text-decoration: none;
  }
</style>

Improve layout for desktop screens

<template>
  <div id="app">
    <navbar :home="true"></navbar>

    <disciplines/>
  </div>
</template>

<script>
import Disciplines from './components/summary/List'
import Navbar from './components/Navbar'

export default {
  name: 'app',
  components: {
    Navbar, Disciplines
  }
}
</script>

<style>
  *, *::before, *::after {
    box-sizing: inherit;
  }

  html, body, #app {
    height: 100%;
  }

  @media screen and (min-device-width: 480px) {
    html, body {
      max-width: 480px;
      margin: auto;
    }
  }

  html {
    box-sizing: border-box;
  }

  body {
    margin: 0;
    padding: 0;
    font-family: PT Sans,sans-serif;
    background-color: rgba(231, 231, 231, 0.29);
  }

  #app {
    display: flex;
    flex-direction: column;
    align-items: stretch;
  }

  #app > * {
    flex-shrink: 0;
  }

  a {
    color: inherit;
    text-decoration: none;
  }
</style>


<template>

      <template v-if="username">
        {{ username }}
      </template>
      <template v-else-if="className">
        {{ className }}
      </template>
      <template v-else>
        {{ $tr('coachPageHeader') }}
      </template>

      {{ $tr('coachListLabel') }}

          {{ coachName }}

</template>

Hide class coaches on user-scoped pages


<template>
    <v-layout row justify-center>
        <v-dialog v-model="dialog" scrollable persistent max-width="650px">
            <create-question-dialog-content @close-dialog="$emit('close-dialog')"
                                            :init-question="$store.getters['createQuestion/getQuestionCopy']">
            </create-question-dialog-content>
        </v-dialog>
    </v-layout>
</template>

<script>
    import CreateQuestionDialogContent from './CreateQuestionDialogContent';

    export default {
        data() {
            return {dialog: true}
        },
        components: {CreateQuestionDialogContent}
    }
</script>

<style lang="scss">

</style>

Delete previous question when created.

<template>
    <v-layout row justify-center>
        <v-dialog v-model="dialog" scrollable persistent max-width="650px">
            <create-question-dialog-content @close-dialog="$emit('close-dialog')"
                                            :init-question="$store.getters['createQuestion/getQuestionCopy']">
            </create-question-dialog-content>
        </v-dialog>
    </v-layout>
</template>

<script>
    import CreateQuestionDialogContent from './CreateQuestionDialogContent';

    export default {
        data() {
            return {dialog: true}
        },
        created() {
            this.$store.commit('createQuestion/RESET');
        },
        components: {CreateQuestionDialogContent}
    }
</script>

<style lang="scss">

</style>


<template lang="html">

          <a @click="page <=1 ? 1 : page--" href="#" aria-label="Previous">
            «

{{ pageNumber }}

          = 5 ? 5 : page++" href="#" aria-label="Next">
            »

</template>

Fix previous and next pagination button

<template lang="html">

          <a @click.prevent="page <=1 ? 1 : page--" class="PageButton" aria-label="Previous">
            «

{{ pageNumber }}

          = 5 ? 5 : page++" class="PageButton" aria-label="Next">
            »

</template>


<template>

     0">.{{ crumb }}

</template>

Add icons in crumbs for translation keys

<template>

     0">.<i class="fa fa-fw fa-folder" style="margin-right: 3px" v-if="index < crumbs.length - 1">{{ crumb }}

</template>


<template>

                    Flash Cards

            <router-view/>

</template>

Remove blank line from app component.


<template>

      Explore »

      {{ crumb.title }} »

</template>

Add better breadcrumbs label and put separators in CSS.

<template>

      Explore

      {{ crumb.title }}

      Current:

</template>


<template>

      Registration opens at 6:00AM

        Est. Registration Date

          {{ estRegData.estRegDate }}

          at 6:00 AM

</template>

Convert to use card-status template

<template>

      Registration opens at 6:00AM

    <uw-card-status v-else>
      <template #status-label>Est. Registration Date</template>
      <template #status-value>{{ estRegData.estRegDate }}</template>
      <template #status-content>

at 6:00 AM

      </template>
    </uw-card-status>

</template>


<template>

</template>

Configure Viewer to display its component according to the viewMode


<template>

Wiki

      <ArticleCard v-for="article in featuredArticles"
                   :title="article.data.title"
                   :content="article.content"
                   :image="article.img"
                   :link="'/wiki/' + article.slug"
                   :key="article.slug"
      ></ArticleCard>

</template>

Remove unused frontpage features for now


<template>

      <icon name="comment" />

        <gl-button variant="default" @click="selectFilter(0)">
          {{ __('Show all activity') }}
        </gl-button>
        <gl-button variant="default" @click="selectFilter(1)">
          {{ __('Show comments only') }}
        </gl-button>

</template>

Remove icon in activity only message on smaller screen widths


<template>

      {{ name }}

      <router-link tag="li" :to="{ name: 'General' }">
         General
      </router-link>

</template>

Fix convention at slide item


<template>
<div class="dn" aria-hidden="true">
  <component :is="load"/>
</div>
</template>

<script>
import futurelink from 'futurelink';

export default {
  data: () => ({
    load: undefined,
    options: {
      links: [],
    },
  }),
  mounted() {
    // Don't run on the server
    if (typeof window === 'undefined') {
      return;
    }

    // Don't run on mobile
    if ('ontouchstart' in window) {
      return;
    }

    const loaded = [this.$route.path];

    this.options.future = (link) => {
      const href = link.getAttribute('href');

      if (loaded.includes(href)) {
        return;
      }

      loaded.push(href);

      // this.$emit('preload', href);

      const resolved = this.$router.resolve(href);
      const { matched } = resolved.resolved;
      this.load = matched[matched.length - 1].components.default;
    };

    futurelink(this.options);

    this.init();
    this.$router.afterEach(() => this.init());
  },
  methods: {
    init() {
      this.$nextTick(() => {
        const links = document.querySelectorAll('a[href^="/"');
        // Populate this.options.links {Array} from links {nodeList}
        for (let i = -1, l = links.length; ++i !== l; this.options.links[i] = links[i]); // eslint-disable-line no-plusplus
      });
    },
  },
};
</script>

Add comment to futurelink component

<!--
  Based on vue-futurelink with customisations for simplicity and performance
  REF: https://github.com/SamKnows/vue-futurelink
-->

<template>
<div class="dn" aria-hidden="true">
  <component :is="load"/>
</div>
</template>

<script>
import futurelink from 'futurelink';

export default {
  data: () => ({
    load: undefined,
    options: {
      links: [],
    },
  }),
  mounted() {
    // Don't run on the server
    if (typeof window === 'undefined') {
      return;
    }

    // Don't run on mobile
    if ('ontouchstart' in window) {
      return;
    }

    const loaded = [this.$route.path];

    this.options.future = (link) => {
      const href = link.getAttribute('href');

      if (loaded.includes(href)) {
        return;
      }

      loaded.push(href);

      // this.$emit('preload', href);

      const resolved = this.$router.resolve(href);
      const { matched } = resolved.resolved;
      this.load = matched[matched.length - 1].components.default;
    };

    futurelink(this.options);

    this.init();
    this.$router.afterEach(() => this.init());
  },
  methods: {
    init() {
      this.$nextTick(() => {
        const links = document.querySelectorAll('a[href^="/"');
        // Populate this.options.links {Array} from links {nodeList}
        for (let i = -1, l = links.length; ++i !== l; this.options.links[i] = links[i]); // eslint-disable-line no-plusplus
      });
    },
  },
};
</script>


<template>
	<button :class="classes" @click.prevent="subscribe">
		<svg :class="iconClasses">
			<use xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="#icon-star"></use>
		</svg>
	</button>
</template>

<script>
	export default {
		props: ['discussion'],

		data () {
			return {
				isSubscribed: false
			}
		},

		computed: {
			classes () {
				return ['btn', 'btn-subscribe'];
			},

			iconClass () {
				return ['icon', (this.isSubscribed) ? 'text-primary' : 'text-subtle'];
			}
		},

		methods: {
			subscribe () {
				if (this.isSubscribed) {
					axios.post(window.App.siteUrl + location.pathname + '/subscriptions');

					flash('Subscribed to discussion');
				} else {
					axios.delete(window.App.siteUrl + location.pathname + '/subscriptions');

					flash('Unsubscribed from discussion');
				}
			}
		},

		created () {
			this.isSubscribed = discussion.isSubscribedTo;
		}
	}
</script>

<style lang="scss">
	.btn-subscribe {
		background: transparent;
	}
</style>

Fix some errors with the subscribe control

<template>
	<button :class="classes" @click.prevent="subscribe">
		<svg :class="iconClasses">
			<use xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="#icon-star"></use>
		</svg>
	</button>
</template>

<script>
	export default {
		props: {
			discussion: {
				type: Object,
				required: true
			},
			size: {
				type: String,
				default: ''
			}
		},

		data () {
			return {
				isSubscribed: false
			}
		},

		computed: {
			classes () {
				return ['btn', 'btn-subscribe'];
			},

			iconClasses () {
				return ['icon', this.size, (this.isSubscribed) ? 'text-primary' : 'text-subtle'];
			}
		},

		methods: {
			subscribe () {
				if (this.isSubscribed) {
					axios.post(window.App.siteUrl + location.pathname + '/subscriptions');

					flash('Subscribed to discussion');
				} else {
					axios.delete(window.App.siteUrl + location.pathname + '/subscriptions');

					flash('Unsubscribed from discussion');
				}
			}
		},

		created () {
			this.isSubscribed = this.discussion.isSubscribedTo;
		}
	}
</script>

<style lang="scss">
	.btn-subscribe {
		background: transparent;
	}
</style>


<template>
    <td :class="{ 'has-text-right': numeric }" :data-label="label">
        <slot></slot>
    </td>
</template>

<script>
    export default {
        name: 'bTableColumn',
        props: {
            field: String,
            label: String,
            width: [Number, String],
            numeric: Boolean,
            sortable: Boolean,
            customSort: Function
        },
        created() {
            if (!this.$parent.isTableComponent) {
                this.$destroy()
                throw new Error('You should wrap bTableColumn on a bTable')
            }

            this.column = {
                field: this.field,
                label: this.label,
                width: this.width,
                isNumeric: this.numeric,
                isSortable: this.sortable,
                customSort: this.customSort
            }

            // Since we're using scoped prop the columns gonna be multiplied,
            // this finds when to stop based on the label prop.
            const repeated = this.$parent.columns.some((column) => {
                return column.label === this.label
            })
            !repeated && this.$parent.columns.push(this.column)
        }
    }
</script>

Add content prop Table Column

<template>

        <slot></slot>

</template>


<template>

     {{ post.title }}

</template>

Revert "Added async await for sync method"

<template>

     {{ post.title }}

</template>


<template>

{{ $t('Thank you for your interest in joining the CoderDojo community!') }}

{{ $t('Due to the coronavirus pandemic, we have paused the Dojo verification process.') }}

{{ $t('In the meantime, we’re providing exciting opportunities for young people, parents, volunteers, and educators to get creative with tech through') }}
        {{ $t('Digital Making at Home') }} {{ $t('from the Raspberry Pi Foundation.') }}

{{ $t('There are still many ways for you to get involved in the Raspberry Pi community!') }}
        {{ $t('Please register your details') }} {{ $t('with us so we can find out how best to support you.') }}

</template>

Change Raspberry Pi community to CoderDojo

<template>

{{ $t('Thank you for your interest in joining the CoderDojo community!') }}

{{ $t('Due to the coronavirus pandemic, we have paused the Dojo verification process.') }}

{{ $t('In the meantime, we’re providing exciting opportunities for young people, parents, volunteers, and educators to get creative with tech through') }}
        {{ $t('Digital Making at Home') }} {{ $t('from the Raspberry Pi Foundation.') }}

{{ $t('There are still many ways for you to get involved in the CoderDojo community!') }}
        {{ $t('Please register your details') }} {{ $t('with us so we can find out how best to support you.') }}

</template>


<template>

  {{ label }}

      <select
        ref="select"
        @change="updateValue"
      >
        <option
          v-for="option in options"
          :value="option.value || option.label"
          :selected="value === option.value"
        >
          {{ getOptionLabel(option) }}
        </option>
      </select>

</template>

Allow combo box to not have label

<template>

   0">
    {{ label }}

      <select
        ref="select"
        @change="updateValue"
      >
        <option
          v-for="option in options"
          :value="option.value || option.label"
          :selected="value === option.value"
        >
          {{ getOptionLabel(option) }}
        </option>
      </select>

</template>


<template>
  <a :href="href" @click="onClick" target="_blank" class="external-link">
    <slot></slot>
  </a>
</template>

<script>
export default {
  props: ['href'],
  methods: {
    onClick (e) {
      if (process.env.NODE_ENV === 'production') {
        import('electron').then(({ shell }) => {
          shell.openExternal(this.href)
          e.preventDefault()
        })
      }
    }
  }
}
</script>

<style scoped>
</style>

Use `requires` to dynamically import electron module instead of `import`

<template>
  <a :href="href" @click="onClick" target="_blank" class="external-link">
    <slot></slot>
  </a>
</template>

<script>
export default {
  props: ['href'],
  methods: {
    onClick (e) {
      if (process.env.NODE_ENV === 'production') {
        require('electron').shell.openExternal(this.href)
        e.preventDefault()
      }
    }
  }
}
</script>

<style scoped>
</style>


<template>

  <core-modal
    title="Please choose a source..."
    :enablebgclickcancel="false"
    @cancel="cancelImportExportWizard"
  >

        <icon-button text="Internet" @click="showImportNetworkWizard">
          <svg src="./world.svg"></svg>
        </icon-button>
        <icon-button text="Local Drives" @click="showImportLocalWizard">
          <svg src="./storage.svg"></svg>
        </icon-button>

        <button @click="cancelImportExportWizard">
          Cancel
        </button>

  </core-modal>

</template>

Add div around the 2 icon buttons


<template>
	<div>
		<app-editable-overlay
			class="-fill"
			v-if="isEditing && routeStore.canEditMedia"
			@click="showEditAvatar()"
		>
			<translate slot="overlay">Change</translate>
			<app-media-item-backdrop :media-item="community.thumbnail">
				<app-community-thumbnail-img :community="community" />
			</app-media-item-backdrop>
		</app-editable-overlay>
		<app-media-item-backdrop v-else :media-item="community.thumbnail">
			<app-community-thumbnail-img :community="community" />
		</app-media-item-backdrop>
	</div>
</template>

<script lang="ts" src="./editable-thumbnail"></script>

Fix the community card thumb not being linked.

<template>
	<div>
		<app-editable-overlay
			class="-fill"
			v-if="isEditing && routeStore.canEditMedia"
			@click="showEditAvatar()"
		>
			<translate slot="overlay">Change</translate>
			<app-media-item-backdrop :media-item="community.thumbnail">
				<app-community-thumbnail-img :community="community" />
			</app-media-item-backdrop>
		</app-editable-overlay>
		<router-link v-else :to="community.routeLocation">
			<app-media-item-backdrop :media-item="community.thumbnail">
				<app-community-thumbnail-img :community="community" />
			</app-media-item-backdrop>
		</router-link>
	</div>
</template>

<script lang="ts" src="./editable-thumbnail"></script>


<template>

    <side-menu></side-menu>

      <router-view></router-view>

</template>

Hide menu on login and register views always


<template>
  <figure>
    <img src="https://d21yo20tm8bmc2.cloudfront.net/2019/wedding-sign-700w.jpeg">
    <figcaption>
      The wedding was held out doors at a beautiful wooded venue in southern Tennessee.
    </figcaption>
  </figure>
</template>

<script>
export default {

};
</script>

Use Cloudinary API for images.

<template>
  <figure>
    <cld-image
      :public-id="publicId"
      :width="width"
      :height="height"
      :alt="alt"
      crop="scale"
      quality="auto"
    />
    <figcaption v-if="caption">
      {{ caption }}
    </figcaption>
  </figure>
</template>

<script>
export default {
  props: {
    publicId: {
      type: String,
      required: true,
    },
    width: {
      type: String,
      default: '',
    },
    height: {
      type: String,
      default: '',
    },
    alt: {
      type: String,
      required: true,
    },
    caption: {
      type: String,
      default: null,
    },
  },
};
</script>


<template>

Videos

        Getting started & creating pages
        Creating global components
        Communicating with the backend
        Customizing the build setup
        The basics of unit testing
        Continuous integration and code coverage
        Bundle splitting
        State management with Vuex

</template>

Add note about video series

<template>

Videos

Note: These videos have not been created yet.

        Getting started & creating pages
        Creating global components
        Communicating with the backend
        Customizing the build setup
        The basics of unit testing
        Continuous integration and code coverage
        Bundle splitting
        State management with Vuex

</template>


<template>

      <VueLogo class="icon" />
      Vue

      <SVGOIcon class="icon" />
      SVGO

      <WebpackIcon class="icon" />
      webpack

</template>

Add proper URLs in the docs example


<template>
  <div class="ads__container">
    <SteamLevels />
  </div>
</template>

<script>
import SteamLevels from './ads/SteamLevels'

export default {
  components: {
    SteamLevels,
  },

  computed: {
  },
}
</script>

<style>
</style>

Change steam levels block class

<template>
  <div class="infofriends__container">
    <SteamLevels />
  </div>
</template>

<script>
import SteamLevels from './ads/SteamLevels'

export default {
  components: {
    SteamLevels,
  },

  computed: {
  },
}
</script>

<style>
</style>


<template>
  <component :is="content"></component>
</template>
<script>
import Vue from 'vue'
import Modal from './Modal.vue'
import sbp from '../../../../shared/sbp.js'
import { OPEN_MODAL, LOAD_MODAL } from '../../../utils/events.js'

// @greg To test:
import LoginModal from '../../containers/LoginModal.vue'

export default {
  name: 'ModalDynamic',
  components: {
    Modal
  },
  data () {
    return {
      content: null
    }
  },
  created () {
    sbp('okTurtles.events/on', LOAD_MODAL, component => this.openModal(component))
  },
  methods: {
    openModal (component) {
      const componentName = component.name ? component.name : component
      const subFolder = component.subFolder ? `${component.subFolder}/` : ''
      const path = `../../containers/${subFolder}${componentName}.vue`
      console.log(`Trying to load ${componentName} (${path})`)
      Vue.component(componentName, () => import(path))
      // @greg: this is not working
      // this.content = componentName
      this.content = LoginModal
      sbp('okTurtles.events/emit', OPEN_MODAL)
    }
  }
}
</script>

Change modal to load lazy loaded components

<template>
  <component :is="content"></component>
</template>
<script>
// import Vue from 'vue'
import Modal from './Modal.vue'
import sbp from '../../../../shared/sbp.js'
import { OPEN_MODAL, LOAD_MODAL } from '../../../utils/events.js'

export default {
  name: 'ModalDynamic',
  components: {
    Modal
  },
  data () {
    return {
      content: null
    }
  },
  created () {
    sbp('okTurtles.events/on', LOAD_MODAL, component => this.openModal(component))
  },
  methods: {
    openModal (component) {
      const componentName = component.name ? component.name : component
      const subFolder = component.subFolder ? `${component.subFolder}/` : ''
      const path = `./${subFolder}${componentName}.js`
      this.content = componentName
      sbp('okTurtles.events/emit', OPEN_MODAL)
    }
  }
}
</script>


<template>

Steam Key Tumbler

        <key-input>Benson</key-input>

        <key-output></key-output>

        <counter></counter>

Share Link: /share?id=123

</template>

Add property unit to counter

<template>

Steam Key Tumbler

        <key-input>Benson</key-input>

        <key-output></key-output>

        <counter unit="eggs"></counter>

Share Link: /share?id=123

</template>


<template>

{{ $tr('errorHeader') }}

{{ $tr('errorPrefixText') }}

    <textarea>
      {{ error }}
    </textarea>

</template>

Add proper label for the error textarea box

<template>

{{ $tr('errorHeader') }}

    {{ $tr('errorPrefixText') }}

    <textarea id="error-textbox">
      {{ error }}
    </textarea>

</template>


<template>

        <button @click="$emit('previous')" class="btn btn-gray btn-sm" :class="{disabled: !canGoPrevious}">Prev</button>
        PAGE {{ page + 1 }}
        <button @click="$emit('next')" class="btn btn-gray btn-sm" :class="{disabled: !canGoNext}">Next</button>

</template>

Remove comments from old decorators

<template>

    <button
      @click="$emit('previous')"
      class="btn btn-gray btn-sm"
      :class="{disabled: !canGoPrevious}"
    >Prev</button>
    PAGE {{ page + 1 }}
    <button @click="$emit('next')" class="btn btn-gray btn-sm" :class="{disabled: !canGoNext}">Next</button>

</template>


<template>

    {{firstWho}}
    <template v-if="hasWhoElse">
      <i18n>and</i18n>
      <tooltip>
        <button type="button" class="gi-is-unstyled gi-is-link-inherit">{{who.length - 1}}<i18n>others</i18n></button>
        <template slot="tooltip">
          <p v-for="(name, index) in who.shift()" :key="`name-${index}`">{{name}}

        </template>
      </tooltip>
    </template>

</template>

Rewrite 'who' variable to avoid confusion

<template>

    {{firstWho}}
    <template v-if="hasWhoElse">
      <i18n>and</i18n>
      <tooltip>
        <button type="button" class="gi-is-unstyled gi-is-link-inherit">{{notFirstWho.length}}<i18n>others</i18n></button>
        <template slot="tooltip">
          <p v-for="(name, index) in notFirstWho" :key="`name-${index}`">{{name}}

        </template>
      </tooltip>
    </template>

</template>


<template>

    <input type="text" id="search" placeholder="Search" v-model="text" v-on:change="search">

</template>

Add basic styles to comics search


<template lang="pug">
  ul.resources
    li(v-for="resource in filteredResources")
      span {{resource.name}}, {{resource.website}}, {{resource.location}}
</template>

<script>
  import resourceIndex from 'components/pool/resource-index';

  export default {
    computed: {
      filteredResources() {
        if (!this.filter) return this.resources;

        const res = resourceIndex.search(this.filter);

        return res.map(r => this.resources.filter(resource => resource.key === r.ref)[0]);
      },
    },
    vuex: {
      getters: {
        filter: state => state.pool.filter,
        resources: state => state.pool.resources,
      },
    },
  };
</script>

Use component for rendering resources

<template lang="pug">
  ul.resources
    li(v-for="resource in filteredResources")
      resource(v-bind:resource="resource")
</template>

<script>
  import { index, booster } from 'components/pool/resource-index';
  import Resource from 'components/pool/resource';

  export default {
    computed: {
      filteredResources() {
        if (!this.filter) return this.resources;

        const res = index.search(this.filter, booster);

        return res.map(r => {
          for (let i = 0; i < this.resources.length; i++) {
            if (this.resources[i].key === r.ref) {
              return this.resources[i];
            }
          }
          return r;
        });
      },
    },
    vuex: {
      getters: {
        filter: state => state.pool.filter,
        resources: state => state.pool.resources,
      },
    },
    components: {
      Resource,
    },
  };
</script>


<template>

 0">
    <mat-svg
      category="maps"
      name="local_library"
      class="coach-icon"
    />

      {{ $tr('coachLabel') }}

      {{ value }}

</template>

Remove “coach” text from label

<template>

 0">
    <ui-icon icon="local_library" />

      {{ value }}

</template>


<template>
  <router-link :to="finalTo">
    <slot></slot>
  </router-link>
</template>

<script>
  export default {
    props: ['to'],
    computed: {
      finalTo() {
        let tempTo = this.to.startsWith('/')
          ? this.to.slice(1)
          : this.to;

        return `${this.$site.themeConfig.activeVersion.link}${tempTo}`;
      }
    },
  }
</script>

Improve docs-link component for docs versions

<template>
  <router-link :to="finalTo">
    <slot></slot>
  </router-link>
</template>

<script>
  export default {
    props: ['to'],
    computed: {
      finalTo() {
        const currentPageVersion = this.getCurrentPageVersion();
        const activeVersion = this.$site.themeConfig.activeVersion.link;
        const tempTo = this.to.startsWith('/')
          ? this.to.slice(1)
          : this.to;
        const versionLink = activeVersion == currentPageVersion
          ? this.$site.themeConfig.activeVersion.link
          : `/docs/${currentPageVersion}/`;

        return `${versionLink}${tempTo}`;
      }
    },
    methods: {
      getCurrentPageVersion() {
        return this.$page.path.match(/([0-9]*[.])?[0-9]+/)[0];
      }
    },
  }
</script>


<template>

     {{ generateAvatar(person) }}

</template>

Allow to set font size of person avatar


<template>
<router-link
  class="button"
  :to="path"
>
  {{ text }}
</router-link>
</template>

<script>
export default {
  name: 'button-link',
  props: {
    text: {
      default: 'button',
      type: String
    },
    path: {
      default: '',
      type: String
    }
  }
}
</script>

<style scoped>
</style>

Allow to set an icon on button links

<template>
<router-link
  class="button"
  :to="path"
>
   0">

  {{ text }}
</router-link>
</template>


<template>

          {{bundle.name}}

          {{bundle.reward}}

            <item-card :item="bundleitem.item"></item-card>

</template>

Add item completion and room completion.

<template>

            {{bundle.name}}
            {{bundle.items_required}}

            {{bundle.reward}}

            {{bundle.room.name}}
            {{bundle.room.items_required}}

            {{bundle.room.reward}}

            <item-card :item="bundleitem.item"></item-card>

</template>


<template>

    <hello></hello>

</template>

Add router-link and router-view. Components for view new components on navigation

<template>

    <hello></hello>

    <router-link to="/test">Go to Test</router-link>

    <router-view></router-view>

</template>


<template>

        <router-link to="/" exact>

        </router-link>

    <transition name="fade" mode="out-in">
      <router-view class="view"></router-view>
    </transition>

</template>

Add simple menu and outside border

<template>

          <router-link to="/" exact>

            vTaiwan
          </router-link>

Comment 留言不忘返

Live Draft 直播寫草案

Follow PROPOSAL 來追蹤定案

    <transition name="fade" mode="out-in">
      <router-view class="view"></router-view>
    </transition>

</template>


<template>
  <div>
    <the-navigation-drawer
      v-if="showSideIcon"
      :drawer="drawer"
      :game="game"
      app
      fixed
      right
      temporary
      @input="handleChangeDrawer"
      @resetList="handleResetList"
    />
    <v-toolbar
      color="primary"
      dark
      fixed
    >
      <v-toolbar-title>
        不思議のダンジョン
      </v-toolbar-title>
      <v-spacer v-if="showSideIcon"/>
      <v-toolbar-side-icon
        v-if="showSideIcon"
        @click.stop="drawer = !drawer"
      />
    </v-toolbar>
  </div>
</template>
<script>
import TheNavigationDrawer from "~/components/organisms/TheNavigationDrawer.vue"

export default {
  name: "TheHeader",
  components: { TheNavigationDrawer },
  props: {
    game: {
      type: Object,
      default: () => new Object()
    },
    showSideIcon: {
      type: Boolean,
      default: false
    }
  },
  data: function() {
    return {
      drawer: false
    }
  },
  methods: {
    handleChangeDrawer(val) {
      this.drawer = val
    },
    handleResetList() {
      this.drawer = false
      this.$emit("resetList")
    }
  }
}
</script>

Remove noisy attribution for TheNavigationDrawer

<template>
  <div>
    <the-navigation-drawer
      v-if="showSideIcon"
      :drawer="drawer"
      :game="game"
      @input="handleChangeDrawer"
      @resetList="handleResetList"
    />
    <v-toolbar
      color="primary"
      dark
      fixed
    >
      <v-toolbar-title>
        不思議のダンジョン
      </v-toolbar-title>
      <v-spacer v-if="showSideIcon"/>
      <v-toolbar-side-icon
        v-if="showSideIcon"
        @click.stop="drawer = !drawer"
      />
    </v-toolbar>
  </div>
</template>
<script>
import TheNavigationDrawer from "~/components/organisms/TheNavigationDrawer.vue"

export default {
  name: "TheHeader",
  components: { TheNavigationDrawer },
  props: {
    game: {
      type: Object,
      default: () => new Object()
    },
    showSideIcon: {
      type: Boolean,
      default: false
    }
  },
  data: function() {
    return {
      drawer: false
    }
  },
  methods: {
    handleChangeDrawer(val) {
      this.drawer = val
    },
    handleResetList() {
      this.drawer = false
      this.$emit("resetList")
    }
  }
}
</script>


<template>
  <div class="q-gutter-md">
    <q-card inline class="color" v-for="color in colors" :key="color">
      <q-card-section
        class="flex flex-center text-white"
        :class="'bg-' + color"
      >
        {{ color }}
      </q-card-section>

      <div
        class="detailed-color column flex-center"
        v-for="n in 14"
        :key="n"
        :class="[`bg-${color}-${n}`, n > 5 && n < 11 ? 'text-white': '']"
      >
        {{ color }}-{{ n }}
      </div>
    </q-card>
  </div>
</template>

<script>
export default {
  data () {
    return {
      colors: ['red', 'pink', 'purple', 'deep-purple', 'indigo', 'blue', 'light-blue', 'cyan', 'teal', 'green', 'light-green', 'lime', 'yellow', 'amber', 'orange', 'deep-orange', 'brown', 'grey', 'blue-grey']
    }
  }
}
</script>

<style lang="stylus" scoped>
.color
  width 135px
.detailed-color
  height 40px
</style>

Fix Color Palette example page

<template>
  <div class="q-gutter-md row items-start">
    <q-card class="color" v-for="color in colors" :key="color">
      <q-card-section
        class="flex flex-center text-white"
        :class="'bg-' + color"
      >
        {{ color }}
      </q-card-section>

      <div
        class="detailed-color column flex-center"
        v-for="n in 14"
        :key="n"
        :class="[`bg-${color}-${n}`, n > 5 && n < 11 ? 'text-white': '']"
      >
        {{ color }}-{{ n }}
      </div>
    </q-card>
  </div>
</template>

<script>
export default {
  data () {
    return {
      colors: ['red', 'pink', 'purple', 'deep-purple', 'indigo', 'blue', 'light-blue', 'cyan', 'teal', 'green', 'light-green', 'lime', 'yellow', 'amber', 'orange', 'deep-orange', 'brown', 'grey', 'blue-grey']
    }
  }
}
</script>

<style lang="stylus" scoped>
.color
  width 135px
.detailed-color
  height 40px
</style>


<template>

{{ config.title }}

        <partial :name="config.template"></partial>

</template>

Set new interval for twitter.


<template>

</template>

Load katex async to reduce bundle size


<template>
  <div>
    <img
      src="../../assets/img/dataviva-logo.png" >
  </div>
</template>

<script>
export default {
  name: "Loader",
};
</script>

<style scoped>
img {
  position: absolute;
  top: calc(50% - 50px);
  left: calc(50% - 50px);
  height: 100px;
  width: 100px;
}
</style>

Add CSS animation to dataviva logo

<template>
  <div>
    <img
      src="../../assets/img/dataviva-logo.png" >
  </div>
</template>

<script>
export default {
  name: "Loader",
};
</script>

<style scoped>
img {
  position: absolute;
  top: calc(50% - 50px);
  left: calc(50% - 50px);
  height: 100px;
  width: 100px;
  animation: fade-in-out 1s steps(12) infinite;
}

@keyframes fade-in-out {
  0% {
    opacity: 0;
  }
  100% {
    opacity: 1;
  }
}
</style>


<template>
  <div id="app">
    <hero></hero>
    <about></about>
    <do></do>
    <projects></projects>
    <skills></skills>
    <fork></fork>
    <contact></contact>
    <copyright></copyright>
  </div>
</template>

<script>
import Hero from './components/Hero';
import About from './components/About';
import Do from './components/Do';
import Projects from './components/Projects';
import Skills from './components/Skills';
import Contact from './components/Contact';
import Fork from './components/Fork';
import Copyright from './components/Copyright';

export default {
  name: 'app',
  components: {
    Hero,
    About,
    Do,
    Projects,
    Skills,
    Fork,
    Contact,
    Copyright,
  },
};
</script>

Use v-cloak to hide content untill load

<template>
  <div id="app" v-cloak>
    <hero></hero>
    <about></about>
    <do></do>
    <projects></projects>
    <skills></skills>
    <fork></fork>
    <contact></contact>
    <copyright></copyright>
  </div>
</template>

<script>
import Hero from './components/Hero';
import About from './components/About';
import Do from './components/Do';
import Projects from './components/Projects';
import Skills from './components/Skills';
import Contact from './components/Contact';
import Fork from './components/Fork';
import Copyright from './components/Copyright';

export default {
  name: 'app',
  components: {
    Hero,
    About,
    Do,
    Projects,
    Skills,
    Fork,
    Contact,
    Copyright,
  },
};
</script>


<template>

      <slot />

{{text}}

</template>

Add vhtml tag to empty state placeholder in dashboard


<template>

  {{ label }}

    <input
      :class="'input ' + inputClass"
      ref="input"
      :placeholder="placeholder"
      :type="type"
      :value="value"
      min="0"
      @input="updateValue()"
      @keyup.enter="emitEnter()"
    />

</template>

Allow textfield component to have a button at the right

<template>

  {{ label }}

    <input
      :class="'input flexrow-item' + inputClass"
      ref="input"
      :placeholder="placeholder"
      :type="type"
      :value="value"
      min="0"
      @input="updateValue()"
      @keyup.enter="emitEnter()"
    />
    <button
      class="button flexrow-item"
      v-if="buttonLabel"
    >
      {{ buttonLabel }}
    </button>

</template>


<template>
    <i
        class="ui-icon material-icons" :class="[icon]" v-text="removeText ? null : icon"
        aria-hidden="true"
    ></i>
</template>

<script>
export default {
    name: 'ui-icon',

    props: {
        icon: {
            type: String,
            required: true
        },
        removeText: {
            type: Boolean,
            default: false
        }
    }
};
</script>

<style lang="stylus">
@import './styles/imports';

.ui-icon {
    font-size: 24px;
    width: 1em;
    height: 1em;
    display: inline-block;
    cursor: inherit;
}
</style>

Align icon to middle (for inline use with text)

<template>
    <i
        class="ui-icon material-icons" :class="[icon]" v-text="removeText ? null : icon"
        aria-hidden="true"
    ></i>
</template>

<script>
export default {
    name: 'ui-icon',

    props: {
        icon: {
            type: String,
            required: true
        },
        removeText: {
            type: Boolean,
            default: false
        }
    }
};
</script>

<style lang="stylus">
@import './styles/imports';

.ui-icon {
    font-size: 24px;
    width: 1em;
    height: 1em;
    display: inline-block;
    cursor: inherit;
    vertical-align: middle;
}
</style>


<template>

                    {{ ('0' + (n - 1)).slice(-2) }}

</template>

Clean up template with filter.

<template>

                    {{ n - 1 | leadingZero }}

</template>


<template>

Logout

Logging you out...

Error logging out.

</template>

Fix a bug which caused the logout button to stay after logout


<template>

Your Cart

Please add some products to cart.

        {{ p.title }} - {{ p.price | currency }} x {{ p.quantity }}

Total: {{ total | currency }}

<button :disabled="!products.length" @click="checkout">Checkout</button>

Checkout {{ checkoutStatus }}.

</template>

Send the products data in the checkout action

<template>

Your Cart

Please add some products to cart.

        {{ p.title }} - {{ p.price | currency }} x {{ p.quantity }}

Total: {{ total | currency }}

<button :disabled="!products.length" @click="checkout(products)">Checkout</button>

Checkout {{ checkoutStatus }}.

</template>


<template lang="html">

          <a @click.prevent="page <=1 ? 1 : page--" class="PageButton" aria-label="Previous">
            «

{{ pageNumber }}

          = 5 ? 5 : page++" class="PageButton" aria-label="Next">
            »

</template>

Add the totalnumber of page

<template lang="html">

          <a @click.prevent="page <=1 ? 1 : page--" class="PageButton" aria-label="Previous">
            «

{{ pageNumber }}

          = totalPage ? totalPage : page++" class="PageButton" aria-label="Next">
            »

</template>


<script>
  export default {
    props: {
      label: {
        type: String,
        required: false,
        default: null,
      },
      helpPath: {
        type: String,
        required: false,
        default: null,
      },
      helpText: {
        type: String,
        required: false,
        default: null,
      },
    },
  };
</script>

<template>
  <div class="project-feature-row">
    <label
      v-if="label"
      class="label-light"
    >
      {{ label }}
      <a
        v-if="helpPath"
        :href="helpPath"
        target="_blank"
      >
        <i
          aria-hidden="true"
          data-hidden="true"
          class="fa fa-question-circle"
        >
        </i>
      </a>
    </label>
    <span
      v-if="helpText"
      class="help-block"
    >
      {{ helpText }}
    </span>
    <slot></slot>
  </div>
</template>

Fix help text for general settings permissions

<script>
  export default {
    props: {
      label: {
        type: String,
        required: false,
        default: null,
      },
      helpPath: {
        type: String,
        required: false,
        default: null,
      },
      helpText: {
        type: String,
        required: false,
        default: null,
      },
    },
  };
</script>

<template>
  <div class="project-feature-row">
    <label
      v-if="label"
      class="label-light"
    >
      {{ label }}
      <a
        v-if="helpPath"
        :href="helpPath"
        target="_blank"
      >
        <i
          aria-hidden="true"
          data-hidden="true"
          class="fa fa-question-circle"
        >
        </i>
      </a>
    </label>
    <span
      v-if="helpText"
      class="form-text text-muted"
    >
      {{ helpText }}
    </span>
    <slot></slot>
  </div>
</template>


<template>

  <div style="width: 100%;" @contextmenu.prevent="showMenu">
    <slot></slot>
    <VMenu v-model="show" :position-x="x" :position-y="y" absolute offset-y>
      <VCard>
        <slot name="menu"></slot>
      </VCard>
    </VMenu>
  </div>

</template>

<script>

  import { mapGetters, mapMutations } from 'vuex';

  export default {
    name: 'ContextMenu',
    data: () => {
      return {
        x: 0,
        y: 0,
        show: false,
      };
    },
    computed: {
      ...mapGetters(['currentContextMenu']),
    },
    watch: {
      currentContextMenu(menuID) {
        if (menuID !== this._uid) {
          this.show = false;
        }
      },
    },
    methods: {
      ...mapMutations({ setMenu: 'SET_CONTEXT_MENU' }),
      showMenu(e) {
        this.show = false;
        this.x = e.clientX;
        this.y = e.clientY;
        this.setMenu(this._uid);
        this.$nextTick(() => {
          this.show = true;
        });
      },
    },
  };

</script>

<style scoped>

</style>

Add an option to disable context menu

<template>

      <slot></slot>

      <slot></slot>
      <VMenu v-model="show" :position-x="x" :position-y="y" absolute offset-y>
        <VCard>
          <slot name="menu"></slot>
        </VCard>
      </VMenu>

</template>


<template>
    <div class="map-container">
        <map-area :place="mapData[locationIndex]"></map-area>
        <map-controls @locationChange="updateArea"
            :mapData="mapData"></map-controls>
    </div>
</template>

<script>
import MapArea from '@/components/MapArea';
import MapControls from '@/components/MapControls';

export default {
    name: 'el-map',
    props: {
        mapData: {
            type: Array,
            required: true,
        },
    },
    data() {
        return {
            locationIndex: 0,
        };
    },
    components: {
        MapArea,
        MapControls,
    },
    methods: {
        updateArea(label) {
            this.locationIndex = this.mapData.findIndex(place => place.city === label);
        },
    },
};
</script>

<style lang="less" scoped>
@import (reference) '~@/assets/variables/global';

.map-container {
    display: grid;
    grid-template-columns: 1.6fr 1fr;
    grid-column-gap: (@base-unit * 5);
}
</style>

Use grid-auto-rows intead of map-height for map and controls

<template>
    <div class="map-container">
        <map-area :place="mapData[locationIndex]"></map-area>
        <map-controls @locationChange="updateArea"
            :mapData="mapData"></map-controls>
    </div>
</template>

<script>
import MapArea from '@/components/MapArea';
import MapControls from '@/components/MapControls';

export default {
    name: 'el-map',
    props: {
        mapData: {
            type: Array,
            required: true,
        },
    },
    data() {
        return {
            locationIndex: 0,
        };
    },
    components: {
        MapArea,
        MapControls,
    },
    methods: {
        updateArea(label) {
            this.locationIndex = this.mapData.findIndex(place => place.city === label);
        },
    },
};
</script>

<style lang="less" scoped>
@import (reference) '~@/assets/variables/global';

.map-container {
    display: grid;
    grid-template-columns: 1.6fr 1fr;
    grid-column-gap: 2.4rem;
    grid-auto-rows: minmax(62vh, auto);
}
</style>


<template lang="jade">
	.App(v-bind:class="'is--animation-ready'")
		header.App-header
		router-view
		footer.App-footer
</template>

<script>
import { mixin as sizeClassHelper } from 'tools/size-class-helper';

export default {
	name: 'App',
	mixins: [sizeClassHelper],
	created: function () {}
};
</script>

<style lang="stylus">
	global-reset()

	html
		font-size (font__$mainFontSize px) !important
		overflow-x hidden

	a
		color color__$lightBlue

	.App
		width 100%
		background-color white
		overflow-x hidden
</style>

Add right padding to navbar logo

<template lang="jade">
	.App(v-bind:class="'is--animation-ready'")
		header.App-header
		router-view
		footer.App-footer
</template>

<script>
import { mixin as sizeClassHelper } from 'tools/size-class-helper';

export default {
	name: 'App',
	mixins: [sizeClassHelper],
	created: function () {}
};
</script>

<style lang="stylus">
	global-reset()

	html
		font-size (font__$mainFontSize px) !important
		overflow-x hidden

	a
		color color__$lightBlue

	.App
		width 100%
		background-color white
		overflow-x hidden

	.NavigationBar-logo
		padding-right 20px
</style>


<template>

Articles tagged with: {{ tag }}

{{ article.title }}

Published {{ article.date }}

{{ article.preview }}

      <nuxt-link :to="`/blog/${article.basename}`">
        Read
      </nuxt-link>

</template>

Use ArticlesList component to display tagged articles.

<template>

Articles tagged with: {{ tag }}

    <ArticlesList :all-articles="taggedArticles"
                  :start-page="1"
                  root-segment="tags"
    />

</template>


<template>

      <slot></slot>

</template>

Make pf-launcher-item accept href property for its link

<template>

      <slot></slot>

</template>


<template>

    {{group.id}} - Orphan {{group.fullName}}
    {{group.description}}

    <group-folder v-if="group.isOpen" :groups="group.subGroups" />

</template>

Stop event propagation to prevent multiple ajax calls


<template>

      Grid of Student Photos

      <li v-for="(reg, i) in registrations"
          :id="`student-photo-${reg.regid}`"
          :key="i"
          :class="getClass(reg)"
      >

          {{ reg.first_name }} {{ reg.surname }}

</template>

Fix style for show/hide joint course student

<template>

      Grid of Student Photos

      <li v-for="(reg, i) in registrations"
          :id="`student-photo-${reg.regid}`"
          :key="i"
          :style="getClass(reg)"
      >

          {{ reg.first_name }} {{ reg.surname }}

</template>


<template>

      {{ label }}
      Select File

    <input
      type="file"
      @change="handleFileChange"
    >

</template>

Add drag and drop file upload.

<template>

      {{ !fileName ? label : '' }}
      Select File

 File selected: {{ fileName }}

    <input
      ref="file"
      type="file"
      accept=".zip"
      @change="handleFileChange"
    >

</template>


<template lang="html">

      {{ currentGameImage }}

        {{ recommendation }}

</template>

Refactor display of current game.

<template lang="html">

        {{ recommendation }}

</template>