<template>
  <div
    :class="['card', { 'card--link': url }]"
  >
    <a
      v-if="url"
      class="card__link"
      :href="url"
      title=""
    >
      <p
        class="card__date"
        v-html="date"
      >
      <h3
        class="card__h3"
        v-html="title"
      />
      <p
        class="card__summary"
        v-html="summary"
      />
    </a>

    <template v-else>
      <p
        class="card__date"
        v-html="date"
      >
      <h3
        class="card__h3"
        v-html="title"
      />
      <p
        class="card__summary"
        v-html="summary"
      />
      <a
        v-if="fileUrl"
        class="button--download"
        href="fileUrl"
        target="_blank"
        title=""
      />
      <a
        v-if="linkUrl"
        class="button--link-external"
        href="linkUrl"
        target="_blank"
        title=""
        v-html="linkTitle"
      />
    </template>
  </div>
</template>

<script>
export default {
  name: 'listing-page-card-news',

  props: {
    date: String,
    fileUrl: String,
    linkTitle: String,
    linkUrl: String,
    summary: String,
    title: {
      type: String,
      required: true
    },
    url: String
  },

  computed: {
    class () {

    }
  }
}
</script>

Make sure urls are set correctly

<template>
  <div
    :class="['card', { 'card--link': url }]"
  >
    <a
      v-if="url"
      class="card__link"
      :href="url"
      title="title"
    >
      <p
        class="card__date"
        v-html="date"
      >
      <h3
        class="card__h3"
        v-html="title"
      />
      <p
        class="card__summary"
        v-html="summary"
      />
    </a>

    <template v-else>
      <p
        class="card__date"
        v-html="date"
      >
      <h3
        class="card__h3"
        v-html="title"
      />
      <p
        class="card__summary"
        v-html="summary"
      />
      <a
        v-if="fileUrl"
        class="button--download"
        :href="fileUrl"
        target="_blank"
        title="title"
      />
      <a
        v-if="linkUrl"
        class="button--link-external"
        :href="linkUrl"
        target="_blank"
        title="title"
        v-html="linkTitle"
      />
    </template>
  </div>
</template>

<script>
export default {
  name: 'listing-page-card-news',

  props: {
    date: String,
    fileUrl: String,
    linkTitle: String,
    linkUrl: String,
    summary: String,
    title: {
      type: String,
      required: true
    },
    url: String
  },

  computed: {
    class () {

    }
  }
}
</script>


<template>
    <div id="conversation-list">
        <conversation-item v-for="conversation in conversations" :key="conversation.device_id" :conversation_id="conversation.device_id" :timestamp="conversation.timestamp" :title="conversation.title" :snippet="conversation.snippet" :unread="conversation.read" :small="small"></conversation-item>
    </div>
</template>

<script>

import Querier from '@/query.js'
import ConversationItem from '@/components/ConversationItem.vue'

export default {
    name: 'conversations',

    mounted () {
        this.updateConversations();
    },

    methods: {
        updateConversations () {
            Querier.fetchConversations('index_unarchived')
                .then(response => {
                    this.conversations = response;
                }).catch(response => console.log(response));
        }
    },

    data () {
        return {
            conversations: [],
            small: true,
        }
    },

    components: {
        ConversationItem,
    }
}
</script>

<!-- Add "scoped" attribute to limit CSS to this component only -->
<style lang="scss" scoped>
    @import "../assets/scss/_vars.scss";

</style>

Add small prop to conversation

<template>
    <div id="conversation-list">
        <conversation-item v-for="conversation in conversations" :key="conversation.device_id" :conversation_id="conversation.device_id" :timestamp="conversation.timestamp" :title="conversation.title" :snippet="conversation.snippet" :unread="conversation.read" :small="small"></conversation-item>
    </div>
</template>

<script>

import Querier from '@/query.js'
import ConversationItem from '@/components/ConversationItem.vue'

export default {
    name: 'conversations',
    props: ['small'],

    mounted () {
        this.updateConversations();
    },

    methods: {
        updateConversations () {
            Querier.fetchConversations('index_unarchived')
                .then(response => {
                    this.conversations = response;
                });
        }
    },

    data () {
        return {
            conversations: [],
        }
    },

    components: {
        ConversationItem,
    }
}
</script>

<!-- Add "scoped" attribute to limit CSS to this component only -->
<style lang="scss" scoped>
    @import "../assets/scss/_vars.scss";

</style>


<template v-mdl>

    Categories

        <router-link tag="a" :to="'/products/' + item.id" >{{ item.title }}</router-link>

</template>

Add indication of active tab

<template v-mdl>

    Categories

        <router-link :to="'/products/' + item.id"
                      active-class="active"
                      class="mdl-navigation__link"
                      exact>
          {{ item.title }}
        </router-link>

</template>


<template>

    <button
      type="button"
      v-show="isCollapsed"
      class="text-expander btn-blank"
      :aria-label="ariaLabel"
      @click="onClick">
      ...
    </button>

      <slot name="expanded"></slot>

</template>

Use v-if in the expand button for performance


<template>
  <textarea></textarea>
</template>

<script>
  import CodeMirror from 'codemirror'
  require('codemirror/lib/codemirror.css')
  require('codemirror/mode/meta')
  require('codemirror/mode/markdown/markdown.js')
  require('codemirror/addon/selection/active-line.js')
  require('codemirror/theme/monokai.css')
  export default {
    data: function () {
      return {
        content: ''
      }
    },
    props: {
      options: {
        required: true
      }
    },
    mounted: function () {
      var _this = this
      this.editor = CodeMirror.fromTextArea(this.$el, this.options)
      this.editor.on('change', function (cm) {
        _this.content = cm.getValue()
        _this.$emit('change', _this.content)
      })
    }
  }
</script>

Add key map tab key to insert spaces

<template>
  <textarea></textarea>
</template>

<script>
  import CodeMirror from 'codemirror'
  require('codemirror/lib/codemirror.css')
  require('codemirror/mode/meta')
  require('codemirror/mode/markdown/markdown.js')
  require('codemirror/addon/selection/active-line.js')
  require('codemirror/theme/monokai.css')
  export default {
    data: function () {
      return {
        content: ''
      }
    },
    props: {
      options: {
        required: true
      }
    },
    mounted: function () {
      var _this = this
      this.editor = CodeMirror.fromTextArea(this.$el, this.options)
      this.editor.on('change', function (cm) {
        _this.content = cm.getValue()
        _this.$emit('change', _this.content)
      })
      this.editor.setOption('extraKeys', {
        Tab: function (cm) {
          var spaces = Array(cm.getOption('indentUnit') + 1).join(' ')
          cm.replaceSelection(spaces)
        }
      })
    }
  }
</script>


<template>

</template>

Fix mixed up tag key and value


<template>
  <textarea ref="code" v-model="code"></textarea>
</template>

<script>
import CodeMirror from 'codemirror';
import 'codemirror/mode/javascript/javascript';
import 'codemirror/lib/codemirror.css';
import 'codemirror/theme/material.css';

export default {
  name: 'CodeBlock',
  mounted () {
    this.initialize();
  },
  props: {
    code: {
      type: String
    }
  },
  methods: {
    initialize () {
      CodeMirror.fromTextArea(this.$refs.code, defaultOptions);
    }
  }
};

const defaultOptions = {
  mode: 'javascript',
  theme: 'material',
  readOnly: true,
  lineNumbers: true
};

</script>
<style scoped lang="scss">
@import 'new-dashboard/styles/variables';

</style>

Add watcher to code blocks

<template>
  <textarea ref="code" v-model="code"></textarea>
</template>

<script>
import CodeMirror from 'codemirror';
import 'codemirror/mode/javascript/javascript';
import 'codemirror/lib/codemirror.css';
import 'codemirror/theme/material.css';

export default {
  name: 'CodeBlock',
  data () {
    return {
      codemirror: null,
    }
  },
  mounted () {
    this.initialize();
  },
  beforeDestroy () {
    this.destroy();
  },
  props: {
    code: {
      type: String
    }
  },
  methods: {
    initialize () {
      this.codemirror = CodeMirror.fromTextArea(this.$refs.code, defaultOptions);
    },
    destroy () {
      const codemirrorElement = this.codemirror.doc.cm.getWrapperElement();
      codemirrorElement.remove && codemirrorElement.remove();
    }
  },
  watch: {
    code () {
      this.codemirror.setValue(this.code);
    }
  }
};

const defaultOptions = {
  mode: 'javascript',
  theme: 'material',
  readOnly: true,
  lineNumbers: true
};

</script>
<style scoped lang="scss">
@import 'new-dashboard/styles/variables';

</style>


<template>

    <navbar></navbar>

      <router-view></router-view>

</template>

Make sidebar shown false when first loaded.


<template>

    {{color | lowercase}}

    <transition name="fadeup">
      copied
    </transition>

</template>

Fix copy hex to clipboard


<template>
    <Card class="bezeless dark">

{{ title }}

            <slot></slot>

    </Card>
</template>

Add overflow scroll on upcoming games


<template>
    <div id="dungeons_and_dragons__character_sheet" class="pseudo-body">
        <app-navigation></app-navigation>

        <router-view :user="character"></router-view>

        <app-footer></app-footer>
    </div>
</template>

<script>
    import Vue from "vue"
    import Navigation from "./Navigation.vue"
    import Footer from "./Footer.vue"

    require("promise-polyfill")
    require("fetch-ie8")

    Vue.component("app-navigation", Navigation)
    Vue.component("app-footer", Footer)

    export default {
        name: "app",
        mounted() {
            this.fetch_user_data()
        },
        data() {
            return {
                character: [],
                loading: true
            }
        },
        methods: {
            fetch_user_data() {
                fetch(`./data/character_sheet__player_name.json`)
                    .then(response => response.json())
                    .then(data => {
                        this.character = data[0]

                        this.loading = false
                    })
            }
        },
        created() {
            // Filters
            Vue.filter("number", entry => entry.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ","))
            Vue.filter("sign", number => number >= 0 ? `+${number}` : `${number}`)
            Vue.filter("modifier", saving_throw => this.user.saving_throw_modifiers[saving_throw])

            // Directives
            Vue.directive("focus", {
                inserted: el => el.focus()
            })
        }
    }
</script>

Update name of global data ref from user to character

<template>
    <div id="dungeons_and_dragons__character_sheet" class="pseudo-body">
        <app-navigation></app-navigation>

        <router-view :user="character"></router-view>

        <app-footer></app-footer>
    </div>
</template>

<script>
    import Vue from "vue"
    import Navigation from "./Navigation.vue"
    import Footer from "./Footer.vue"

    require("promise-polyfill")
    require("fetch-ie8")

    Vue.component("app-navigation", Navigation)
    Vue.component("app-footer", Footer)

    export default {
        name: "app",
        mounted() {
            this.fetch_user_data()
        },
        data() {
            return {
                character: [],
                loading: true
            }
        },
        methods: {
            fetch_user_data() {
                fetch(`./data/character_sheet__player_name.json`)
                    .then(response => response.json())
                    .then(data => {
                        this.character = data[0]

                        this.loading = false
                    })
            }
        },
        created() {
            // Filters
            Vue.filter("number", entry => entry.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ","))
            Vue.filter("sign", number => number >= 0 ? `+${number}` : `${number}`)
            Vue.filter("modifier", saving_throw => this.character.saving_throw_modifiers[saving_throw])

            // Directives
            Vue.directive("focus", {
                inserted: el => el.focus()
            })
        }
    }
</script>


<template>

  <span class="channel-thumbnail">
    <!--
      we consider thumbnails decorative - empty `alt`
      attribute to hide it from screen readers
    -->
    <img
      v-if="thumbnailUrl"
      class="image"
      :src="thumbnailUrl"
      alt=""
    >
  </span>

</template>

<script>

  export default {
    name: 'ChannelThumbnail',
    props: {
      thumbnailUrl: {
        type: String,
        required: false,
        default: '',
      },
    },
  };

</script>

<style lang="scss" scoped>

  .channel-thumbnail {
    display: flex;
    flex-direction: column;
    justify-content: center;
    width: 100%;
  }

  .image {
    width: 100%;
    height: auto;
  }

</style>

Fix channel thumbnails images overflowing container in IE

<template>

  <span class="channel-thumbnail">
    <!--
      we consider thumbnails decorative - empty `alt`
      attribute to hide it from screen readers
    -->
    <img
      v-if="thumbnailUrl"
      class="image"
      :src="thumbnailUrl"
      alt=""
    >
  </span>

</template>

<script>

  export default {
    name: 'ChannelThumbnail',
    props: {
      thumbnailUrl: {
        type: String,
        required: false,
        default: '',
      },
    },
  };

</script>

<style lang="scss" scoped>

  .channel-thumbnail {
    display: flex;
    flex-direction: column;
    justify-content: center;
    width: 100%;
  }

  .image {
    flex-shrink: 0; // IE fix
    width: 100%;
    height: auto;
  }

</style>


<template>
    <div>
        <flash/>
        <login-form
                v-if="this.$route.name === 'login'"
                v-bind:user="user"
                v-on:update-email="onUpdateEmail"
        />
        <forgot-password
                v-if="this.$route.name === 'forgot-password'"
                v-bind:email="user.email"
                v-on:update-email="onUpdateEmail"
        />
        <two-factor-form v-if="this.$route.name === 'checkpoint'" />
    </div>
</template>

<script>
    import Flash from '../Flash';
    import ForgotPassword from "./ForgotPassword";
    import LoginForm from "./LoginForm";
    import TwoFactorForm from "./TwoFactorForm";

    export default {
        name: 'login',
        data: function () {
            return {
                user: {
                    email: ''
                },
            };
        },
        methods: {
            onUpdateEmail: function (value) {
                this.$data.user.email = value;
            },
        },
        components: {
            Flash,
            TwoFactorForm,
            ForgotPassword,
            LoginForm,
        },
    }
</script>

Fix flash margins on login page

<template>
    <div>
        <flash container="mb-2"/>
        <login-form
                v-if="this.$route.name === 'login'"
                v-bind:user="user"
                v-on:update-email="onUpdateEmail"
        />
        <forgot-password
                v-if="this.$route.name === 'forgot-password'"
                v-bind:email="user.email"
                v-on:update-email="onUpdateEmail"
        />
        <two-factor-form v-if="this.$route.name === 'checkpoint'" />
    </div>
</template>

<script>
    import Flash from '../Flash';
    import ForgotPassword from "./ForgotPassword";
    import LoginForm from "./LoginForm";
    import TwoFactorForm from "./TwoFactorForm";

    export default {
        name: 'login',
        data: function () {
            return {
                user: {
                    email: ''
                },
            };
        },
        methods: {
            onUpdateEmail: function (value) {
                this.$data.user.email = value;
            },
        },
        components: {
            Flash,
            TwoFactorForm,
            ForgotPassword,
            LoginForm,
        },
    }
</script>


<template>
  <div>
    <button @click="value = 0">0%</button>
    <button @click="value += 10">+10%</button>
    <button @click="value -= 10">-10%</button>
    <button @click="value = 100">100%</button>
    <label>
      <input type="checkbox" v-model="indeterminate">Indeterminate
    </label>
    Current value: {{ value }}
    Indeterminate: {{ indeterminate }}
    <progress-bar :value="value" :indeterminate="indeterminate"></progress-bar>
    <progress-bar :value="value" :indeterminate="indeterminate" :no-animation="true"></progress-bar>
    <progress-bar :value="value" :indeterminate="indeterminate" :no-stripes="true"></progress-bar>
  </div>
</template>

<script type="text/babel">
  import ProgressBar from './../components/progress-bar.vue'

  export default {
    data () {
      return {
        value: 10,
        indeterminate: false
      }
    },

    components: {
      ProgressBar
    }
  }
</script>

Update progress-bar demo and add docs

<template>

    <component-docs :docs="docs">

        <button @click="value = 0">0%</button>
        <button @click="value += 10">+10%</button>
        <button @click="value -= 10">-10%</button>
        <button @click="value = 100">100%</button>

          <input type="checkbox" v-model="indeterminate">Indeterminate

          <input type="checkbox" v-model="noAnimation">No animation

          <input type="checkbox" v-model="noStripes">No stripes

      <progress-bar
        :value="value"
        :indeterminate="indeterminate"
        :no-stripes="noStripes"
        :no-animation="noAnimation"
      ></progress-bar>

    </component-docs>

</template>


<template>
    <b-modal id="logs_modal" ref="modal" :title="langLogView" @hidden="clearContents">
        <streaming-log-view ref="logView" :log-url="logUrl"></streaming-log-view>

        <template #modal-footer>
            <b-button variant="default" type="button" @click="close">
                <translate key="lang_btn_close">Close</translate>
            </b-button>
            <b-button variant="primary" class="btn_copy" @click.prevent="doCopy" type="button">
                <translate key="lang_btn_copy">Copy to Clipboard</translate>
            </b-button>
        </template>
    </b-modal>
</template>

<script>
import '~/vendor/clipboard.js';
import StreamingLogView from "~/components/Common/StreamingLogView";

export default {
    name: 'StreamingLogModal',
    components: {StreamingLogView},
    data() {
        return {
            logUrl: null
        };
    },
    computed: {
        langLogView() {
            return this.$gettext('Log Viewer');
        }
    },
    methods: {
        show(logUrl) {
            this.logUrl = logUrl;
            this.$refs.modal.show();
        },
        doCopy() {
            this.$copyText(this.$refs.logView.getContents());
        },
        close() {
            this.$refs.modal.hide();
        },
        clearContents() {
            this.logUrl = null;
            this.log = null;
        }
    }
};
</script>

Make streaming log modal bigger.

<template>
    <b-modal id="logs_modal" size="lg" ref="modal" :title="langLogView" @hidden="clearContents">
        <streaming-log-view ref="logView" :log-url="logUrl"></streaming-log-view>

        <template #modal-footer>
            <b-button variant="default" type="button" @click="close">
                <translate key="lang_btn_close">Close</translate>
            </b-button>
            <b-button variant="primary" class="btn_copy" @click.prevent="doCopy" type="button">
                <translate key="lang_btn_copy">Copy to Clipboard</translate>
            </b-button>
        </template>
    </b-modal>
</template>

<script>
import '~/vendor/clipboard.js';
import StreamingLogView from "~/components/Common/StreamingLogView";

export default {
    name: 'StreamingLogModal',
    components: {StreamingLogView},
    data() {
        return {
            logUrl: null
        };
    },
    computed: {
        langLogView() {
            return this.$gettext('Log Viewer');
        }
    },
    methods: {
        show(logUrl) {
            this.logUrl = logUrl;
            this.$refs.modal.show();
        },
        doCopy() {
            this.$copyText(this.$refs.logView.getContents());
        },
        close() {
            this.$refs.modal.hide();
        },
        clearContents() {
            this.logUrl = null;
            this.log = null;
        }
    }
};
</script>


<template>
	<button
		v-if="link.type !== 'loading'"
		:class="['toggle-button', 'toggle-preview', { opened: link.shown }]"
		@click="onClick"
	/>
</template>

<script>
export default {
	name: "LinkPreviewToggle",
	props: {
		link: Object,
	},
	methods: {
		onClick() {
			this.link.shown = !this.link.shown;

			this.$parent.$emit("linkPreviewToggle", this.link, this.$parent.message);
		},
	},
};
</script>

Add aria-label on link preview toggle button

<template>
	<button
		v-if="link.type !== 'loading'"
		:class="['toggle-button', 'toggle-preview', { opened: link.shown }]"
		:aria-label="ariaLabel"
		@click="onClick"
	/>
</template>

<script>
export default {
	name: "LinkPreviewToggle",
	props: {
		link: Object,
	},
	computed: {
		ariaLabel() {
			return this.link.shown ? "Collapse preview" : "Expand preview";
		},
	},
	methods: {
		onClick() {
			this.link.shown = !this.link.shown;

			this.$parent.$emit("linkPreviewToggle", this.link, this.$parent.message);
		},
	},
};
</script>


<template>

      <router-link :to="{ name: 'Features' }">

      </router-link>

    <WalletCard />
    <BuyEthButton class="mt-2" />

</template>

Fix min height for feature pages


<template>
    <div class="page-open">
        <List
            type="Framework"
            :repo="repo"
            :click="frameworkClick" />
    </div>
</template>
<script>
import List from '@/lode/components/lode/List.vue';
export default {
    name: "Frameworks",
    props: {
        queryParams: Object
    },
    data: function() {
        return {
            repo: window.repo
        };
    },
    components: {List},
    methods: {
        frameworkClick: function(framework) {
            this.$router.push({name: "framework", params: {frameworkId: framework.id}});
        }
    }
};
</script>

<style lang="scss">

.page-open .e-name{
    label{
        display:none;
    }
    ul{
        margin-left:0px;
        display:inline;
        li{
            display:inline;
            margin-right:.5rem;
        }
    }
}

.page-open .e-description{
    label{
        display:none;
    }
    font-size:.8rem;
}

.page-open .e-Framework{
    a {display:none;}
    .icon {display:none;}
    .editable {display:block;float:right;}
}

</style>

Use slots to inject content into framework list

<template>
    <div class="page-open">
        <List
            type="Framework"
            :repo="repo"
            :click="frameworkClick">
            <template
                v-slot:frameworkTags="slotProps">
                {{ slotProps.item.competency.length }} competencies
            </template>
        </List>
    </div>
</template>
<script>
import List from '@/lode/components/lode/List.vue';
export default {
    name: "Frameworks",
    props: {
        queryParams: Object
    },
    data: function() {
        return {
            repo: window.repo
        };
    },
    components: {List},
    methods: {
        frameworkClick: function(framework) {
            this.$router.push({name: "framework", params: {frameworkId: framework.id}});
        }
    }
};
</script>

<style lang="scss">

.page-open .e-name{
    label{
        display:none;
    }
    ul{
        margin-left:0px;
        display:inline;
        li{
            display:inline;
            margin-right:.5rem;
        }
    }
}

.page-open .e-description{
    label{
        display:none;
    }
    font-size:.8rem;
}

.page-open .e-Framework{
    a {display:none;}
    .icon {display:none;}
    .editable {display:block;float:right;}
}

</style>


<template>

    {{ label|htmlDecode }}
    {{ value|htmlDecode }}

</template>

Fix for product attribute display


<template>
<router-link
  :to="'/tasks/' + task.id"
  class="tag"
  :style="{
    background: task.task_status_color,
    color: task.task_status_short_name !== 'todo' ? 'white' : '#333'
}">
  {{ task.task_status_short_name }}
</router-link>
</template>

<script>
export default {
  name: 'title',
  props: {
    task: {
      default: 'button',
      type: Object
    }
  }
}
</script>

<style scoped>
.tag {
  text-transform: uppercase;
}

.tag:hover {
  transform: scale(1.15);
  transition: all 0.1s ease-in-out
}
</style>

Convert colors coming from Shotgun to CGWire ones

<template>
<router-link
  :to="'/tasks/' + task.id"
  class="tag"
  :style="{
    background: this.backgroundColor,
    color: this.color
}">
  {{ task.task_status_short_name }}
</router-link>
</template>

<script>
export default {
  name: 'title',
  props: {
    task: {
      default: 'button',
      type: Object
    }
  },
  computed: {
    backgroundColor () {
      if (this.task.task_status_short_name === 'wtg') {
        return '#f5f5f5'
      } else if (this.task.task_status_short_name === 'ip') {
        return '#3273dc'
      } else if (this.task.task_status_short_name === 'pndng') {
        return '#ab26ff'
      } else if (this.task.task_status_short_name === 'fin') {
        return '#22d160'
      } else if (this.task.task_status_short_name === 'rtk') {
        return '#ff3860'
      } else if (this.task.task_status_short_name === 'cfrm') {
        return '#f1c40f'
      } else if (this.task.task_status_short_name === 'recd') {
        return '#1abc9c'
      }
      return this.task.task_status_color
    },
    color () {
      if (this.task.task_status_short_name !== 'todo' &&
          this.task.task_status_short_name !== 'wtg') {
        return 'white'
      } else {
        return '#333'
      }
    }
  }
}
</script>

<style scoped>
.tag {
  text-transform: uppercase;
}

.tag:hover {
  transform: scale(1.15);
  transition: all 0.1s ease-in-out
}
</style>


<template lang="pug">
  #learn.has-text-centered
    .heading
      h1.title {{ book.name }}
      h2.subtitle {{ book.count }} days / {{ book.totalWords }} words
    a.day.button.is-large.is-primary(v-for='n in book.count', @click='toggle(n)',
      :class='list.includes(n) || `is-outlined`') {{ '00'.concat(n).slice(-2) }}
    span.day.placeholder(v-for='n in 20')
</template>

<script>
  import axios from 'axios'

  export default {
    name: 'learn',
    data: () => ({ book: {}, list: [] }),

    async created () {
      const book = this.$route.params.book
      this.book = (await axios.get(`/learn/${book}`)).data
    },

    methods: {
      toggle (n) {
        const i = this.list.indexOf(n);
        (i < 0) ? this.list.push(n) : this.list.splice(i, 1)
      }
    }
  }
</script>

<style lang="sass" scoped>
  .heading
    margin-bottom: 1.5rem

  .day
    width: 4rem
    margin: 0.5rem
    display: inline-flex
    transition: all 0.25s ease

    &.button
      height: 3.7rem
</style>

Put comma for large numbers

<template lang="pug">
  #learn.has-text-centered
    .heading
      h1.title {{ book.name }}
      h2.subtitle {{ num(book.count) }} days / {{ num(book.totalWords) }} words
    a.day.button.is-large.is-primary(v-for='n in book.count', @click='toggle(n)',
      :class='list.includes(n) || `is-outlined`') {{ '00'.concat(n).slice(-2) }}
    span.day.placeholder(v-for='n in 20')
</template>

<script>
  import axios from 'axios'

  export default {
    name: 'learn',
    data: () => ({ book: {}, list: [] }),

    async created () {
      const book = this.$route.params.book
      this.book = (await axios.get(`/learn/${book}`)).data
    },

    methods: {
      num: (n) => Number(n).toLocaleString({ useGrouping: true }),
      toggle (n) {
        const i = this.list.indexOf(n);
        (i < 0) ? this.list.push(n) : this.list.splice(i, 1)
      }
    }
  }
</script>

<style lang="sass" scoped>
  .heading
    margin-bottom: 1.5rem

  .day
    width: 4rem
    margin: 0.5rem
    display: inline-flex
    transition: all 0.25s ease

    &.button
      height: 3.7rem
</style>


<template lang="html">

Clients

    <client-list></client-list>

Product Areas

    <product-area-list></product-area-list>

Feature Requests

    <feature-list></feature-list>

</template>

Remove product-list and client-list from main page

<template lang="html">

Feature Requests

    <feature-list></feature-list>

</template>


<template>

Props

          Name
          Type
          Default
          Description

          {{ name }}
          {{ prop.type ? prop.type.name : 'Any' }}
          {{ prop.default }}
          {{ prop.description }}

</template>

Fix props section for prop type as array

<template>

Props

          Name
          Type
          Default
          Description

          {{ name }}
          {{ getTypes(prop.type) }}
          {{ prop.default }}
          {{ prop.description }}

</template>


<template>
  <wall json-file="circle-members" :ordered-group-names="orderedGroupNames">
    <template #heading>

Our Circle Members

</template>
    <template #subheading="{ group }">
      <template v-if="group.name === 'Founders\' Circle'"
        >

          In honor of our three founders: Ross Ramsey, Evan Smith and John
            Thornton

</template
      >
    </template>
    <template #member="{ member }">
      {{ member }}
    </template>
  </wall>
</template>

Make template hack a little less brittle

<template>
  <wall json-file="circle-members" :ordered-group-names="orderedGroupNames">
    <template #heading>

Our Circle Members

</template>
    <template #subheading="{ group }">
      <template v-if="group.name === foundersLabel"
        >

          In honor of our three founders: Ross Ramsey, Evan Smith and John
            Thornton

</template
      >
    </template>
    <template #member="{ member }">
      {{ member }}
    </template>
  </wall>
</template>


<template>
  <button class="mdl-button mdl-js-button mdl-color--blue-grey-100 mdl-js-ripple-effect">
    {{text}}
  </div>
</template>

<script>
  export default {
    props: {
      text: {
        type: String,
        required: false
      }
    },

    mounted () {
      componentHandler.upgradeElement(this.$el)
    }
  }
</script>

Add slot to make button better.

<template>
  <button class="mdl-button mdl-js-button mdl-color--blue-grey-100 mdl-js-ripple-effect"
    v-on:click="clickHandler()">
    <slot></slot>
  </div>
</template>

<script>
  export default {
    mounted () {
      componentHandler.upgradeElement(this.$el)
    },

    methods: {
      clickHandler () {
        this.$emit('click')
      }
    }
  }
</script>


<template>

{{ title }}

        <button v-on:click="prevDay" class="btn btn-default">

        </button>
        <button v-on:click="nextDay" class="btn btn-default">

        </button>

        <button v-on:click="play" class="btn btn-default">

        </button>
        <button v-on:click="stop" class="btn btn-default">

        </button>
        <button v-on:click="pause" class="btn btn-default">

        </button>

      <router-link :to="{ name: 'setting' }" tag="button" class="btn btn-default">

      </router-link>
      <router-link :to="{ name: 'about' }" tag="button" class="btn btn-default pull-right">

      </router-link>

</template>

Use `@click` instead of v-on

<template>

{{ title }}

        <button @click="prevDay" class="btn btn-default">

        </button>
        <button @click="nextDay" class="btn btn-default">

        </button>

        <button @click="play" class="btn btn-default">

        </button>
        <button @click="stop" class="btn btn-default">

        </button>
        <button @click="pause" class="btn btn-default">

        </button>

      <router-link :to="{ name: 'setting' }" tag="button" class="btn btn-default">

      </router-link>
      <router-link :to="{ name: 'about' }" tag="button" class="btn btn-default pull-right">

      </router-link>

</template>


<template>

    <slot></slot>

      <UiIconButton
        type="flat"
        :title="coachString('printReportAction')"
        @click.prevent="$print()"
      >
        <mat-svg name="print" category="action" />
      </UiIconButton>
      <UiIconButton
        v-if="!disableExport"
        type="flat"
        :title="coachString('exportCSVAction')"
        @click.prevent="$emit('export')"
      >
        <mat-svg name="get_app" category="action" />
      </UiIconButton>

</template>

Add KTooltip to report controls

<template>

    <slot></slot>

      <UiIconButton
        ref="printButton"
        type="flat"
        :aria-label="coachString('printReportAction')"
        @click.prevent="$print()"
      >
        <mat-svg name="print" category="action" />
      </UiIconButton>
      <KTooltip
        reference="printButton"
        :refs="$refs"
      >
        {{ coachString('printReportAction') }}
      </KTooltip>

      <UiIconButton
        v-if="!disableExport"
        ref="exportButton"
        type="flat"
        :aria-label="coachString('exportCSVAction')"
        @click.prevent="$emit('export')"
      >
        <mat-svg name="get_app" category="action" />
      </UiIconButton>
      <KTooltip
        reference="exportButton"
        :refs="$refs"
      >
        {{ coachString('exportCSVAction') }}
      </KTooltip>

</template>


<script>
import { GlTooltipDirective, GlButton } from '@gitlab/ui';
import Icon from '~/vue_shared/components/icon.vue';

export default {
  name: 'ReplyButton',
  components: {
    Icon,
    GlButton,
  },
  directives: {
    GlTooltip: GlTooltipDirective,
  },
};
</script>

<template>
  <div class="note-actions-item">
    <gl-button
      ref="button"
      v-gl-tooltip.bottom
      class="note-action-button"
      variant="transparent"
      :title="__('Reply to comment')"
      @click="$emit('startReplying')"
    >
      <icon name="comment" css-classes="link-highlight" />
    </gl-button>
  </div>
</template>

Move reply to comment tooltip to the top

<script>
import { GlTooltipDirective, GlButton } from '@gitlab/ui';
import Icon from '~/vue_shared/components/icon.vue';

export default {
  name: 'ReplyButton',
  components: {
    Icon,
    GlButton,
  },
  directives: {
    GlTooltip: GlTooltipDirective,
  },
};
</script>

<template>
  <div class="note-actions-item">
    <gl-button
      ref="button"
      v-gl-tooltip
      class="note-action-button"
      variant="transparent"
      :title="__('Reply to comment')"
      @click="$emit('startReplying')"
    >
      <icon name="comment" css-classes="link-highlight" />
    </gl-button>
  </div>
</template>


<template>
  <component
    :is="tag"
    ref="element">
    <slot />
  </component>
</template>

<script>
import anime from 'animejs'

export default {
  name: 'AppReveal',
  className: 'AppReveal',
  props: {
    active: {
      type: Boolean,
      default: false
    },
    tag: {
      type: String,
      default: 'div'
    },
    delay: {
      type: Number,
      default: 0
    }
  },
  computed: {
    target() {
      return this.$refs.element
    }
  },
  watch: {
    active(val) {
      if (val) {
        this.animationPlay()
      } else {
        this.animationReverse()
      }
    }
  },
  mounted() {
    this.animationCreate()
  },
  methods: {
    animationCreate() {
      this.animation = anime.timeline({
        loop: false,
        autoplay: false
      })
        .add({
          targets: this.target,
          skew: ['-10deg', '0deg'],
          translateX: [10, 0],
          translateZ: 0,
          opacity: [0, 1],
          easing: 'easeInOutQuad',
          duration: 500,
          delay: 250 + this.delay
        })
    },
    animationPlay() {
      this.animation.play()
    },
    animationReverse() {
      this.animation.reverse()
    }
  }
}
</script>

<style lang="scss" scoped>
.AppReveal {
  opacity: 0;
}
</style>

Add class to attach the styles to.

<template>
  <component
    :is="tag"
    ref="element"
    :class="$options.className">
    <slot />
  </component>
</template>

<script>
import anime from 'animejs'

export default {
  name: 'AppReveal',
  className: 'AppReveal',
  props: {
    active: {
      type: Boolean,
      default: false
    },
    tag: {
      type: String,
      default: 'div'
    },
    delay: {
      type: Number,
      default: 0
    }
  },
  computed: {
    target() {
      return this.$refs.element
    }
  },
  watch: {
    active(val) {
      if (val) {
        this.animationPlay()
      } else {
        this.animationReverse()
      }
    }
  },
  mounted() {
    this.animationCreate()
  },
  methods: {
    animationCreate() {
      this.animation = anime.timeline({
        loop: false,
        autoplay: false
      })
        .add({
          targets: this.target,
          skew: ['-10deg', '0deg'],
          translateX: [10, 0],
          translateZ: 0,
          opacity: [0, 1],
          easing: 'easeInOutQuad',
          duration: 500,
          delay: 250 + this.delay
        })
    },
    animationPlay() {
      this.animation.play()
    },
    animationReverse() {
      this.animation.reverse()
    }
  }
}
</script>

<style lang="scss" scoped>
.AppReveal {
  opacity: 0;
}
</style>


<template lang="html">
  <div>
   <button @click="closeTabs">
    Tidy
   </button>
  </div>
</template>

<script>
export default {

  methods: {
    closeTabs () {
      chrome.storage.sync.set({'foo': { name: 'bar' }});
      chrome.storage.sync.set({'baz': { name: 'bar' }});
      chrome.storage.sync.get(null, function(items) {
        const allKeys = Object.keys(items);
      });

      chrome.windows.getCurrent(null, window => {
        chrome.tabs.query({}, tabs => {
          tabs.forEach(t => {
            if (t.windowId === window.id) {
              console.log(`Closing tab with url of ${t.url}`);
              chrome.tabs.remove(t.id)
            }
          })
        });
      });

      const dashboardURL = chrome.extension.getURL('pages/dashboard.html')
      chrome.tabs.create({ url: dashboardURL });
    }
  }
}
</script>

<style scoped="true" lang="sass">
@import '../styles/settings';
button {
  width: 100px;
  background: $color-primary;
  color: white;
}
</style>

Save Tabs to Chrome Storage

<template lang="html">
  <div>
   <button @click="closeTabs">
    Tidy
   </button>
  </div>
</template>

<script>
export default {

  methods: {
    saveTabData (tabs){
      chrome.windows.getCurrent(null, window => {
        chrome.tabs.query({}, tabs => {
          let data = {};

          tabs.forEach(t => {
            if (t.windowId === window.id) {
              data[t.url] = {
                'url': t.url,
                'icon': t.favIconUrl,
                'title': t.title,
              }
            }
          })

          chrome.storage.sync.get('tidy_storage', function(items) {
            let tabData = items['tidy_storage'] || {};

            const timeStamp = Date.now();

            tabData[timeStamp] = data;

            chrome.extension.getBackgroundPage().console.log(tabData);

            chrome.storage.sync.set({'tidy_storage': tabData});

          });
        });
      });
    },
    closeTabs () {
      this.saveTabData()

      chrome.windows.getCurrent(null, window => {
        chrome.tabs.query({}, tabs => {

          let windowTabs = tabs.filter(t => t.windowId === window.id);

          let tabsClosed = 0

          windowTabs.forEach(t => {
            tabsClosed++;
            if(tabsClosed == windowTabs.length){
              const dashboardURL = chrome.extension.getURL('pages/dashboard.html')
              chrome.tabs.create({ url: dashboardURL });
            }
            chrome.tabs.remove(t.id);
          })
        });
      });
    }
  }
}
</script>

<style scoped="true" lang="sass">
@import '../styles/settings';
button {
  width: 100px;
  background: $color-primary;
  color: white;
}
</style>


<template>
    <main id="app">
        <router-view/>
    </main>
</template>

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

<style>
#app {
    font-family: "Avenir", Helvetica, Arial, sans-serif;
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
    text-align: center;
    color: #2c3e50;
    margin-top: 60px;
}
</style>

Update type and introduce MARTA style elements

<template>
    <div id="app">
        <router-view/>
    </div>
</template>

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

<style lang="less">
html {
    font-family: BlinkMacSystemFont,-apple-system,"Segoe UI",Roboto,Oxygen-Sans,Ubuntu,Cantarell,"Helvetica Neue",sans-serif;
    font-size: 62.5%;
    box-sizing: border-box;
}
* {
    padding: 0;
    margin: 0;
    box-sizing: inherit;
    &:before,
    &:after {
        box-sizing: inherit;
    }
}
body {
    font-size: 1.6rem;
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
    box-shadow:
        inset 0 0 0 3px #fb7700,
        inset 0 0 0 6px #fbc00d;
    border: 3px solid #1e90d6;
    padding: 16px;
    color: #453e45;
    background: url("assets/images/1.png") #fafafa;

    @media only screen and (min-width: 501px)  {
        box-shadow:
            inset 0 0 0 5px #fb7700,
            inset 0 0 0 10px #fbc00d;
        border: 5px solid #1e90d6;
        padding: 28px;
    }
}
body,
html {
    min-height: 100vh;
    scroll-behavior: smooth;
    -webkit-overflow-scrolling: touch;
}
::selection {
    background-color: rgba(250, 179, 0, 0.3);
}
</style>


<template>

{{ paragraph }}

</template>

Use break-word for dapp detail paragraphs

<template>

{{ paragraph }}

</template>


<template>
  <figure class="my-10">
    <cld-image :public-id="publicId">
      <cld-transformation :width="width"
                          :height="height"
                          :alt="alt"
                          crop="scale"
                          fetchFormat="auto"
                          quality="auto"
      />
    </cld-image>
    <figcaption v-if="caption" class="mt-2 text-center font-semibold">
      {{ 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>

<style>
figure img {
  @apply .mx-auto;
}
</style>

Increase top and bottom margins.

<template>
  <figure class="my-10 md:my-16">
    <cld-image :public-id="publicId">
      <cld-transformation :width="width"
                          :height="height"
                          :alt="alt"
                          crop="scale"
                          fetchFormat="auto"
                          quality="auto"
      />
    </cld-image>
    <figcaption v-if="caption" class="mt-2 text-center font-semibold">
      {{ 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>

<style>
figure img {
  @apply .mx-auto;
}
</style>


<template>

 0">
      {{ $tr('shortTime', { time: 120 }) }}

 0">
      {{ $tr('suggestedTimeLabel', { time: 120 }) }}

    {{ $tr('suggestedTimeTooltip') }}

</template>

Make sure the right string is there

<template>

     0">
      {{ $tr('suggestedTimeLabel') }}
      <TimeDuration :seconds="seconds" :expand="true" />

</template>


<template functional>
  <aside>
    {{ $t('example.warning') }}
    <a
      href="https://panjiachen.github.io/vue-element-admin-site/guide/essentials/tags-view.html"
      target="_blank"
    >Document</a>
  </aside>
</template>

Revert "perf: add functional to template"

<template>
  <aside>
    {{ $t('example.warning') }}
    <a
      href="https://panjiachen.github.io/vue-element-admin-site/guide/essentials/tags-view.html"
      target="_blank"
    >Document</a>
  </aside>
</template>


<template>

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

</template>

Use jquery to select only the compoent's input element, instead of using generic class selector


<template>

    <button class="file">

Your first document

    </button>

</template>

Load filename from props passed down from Documents

<template>

    <button class="file">

{{fileName}}

    </button>

</template>


<template>
    <div>
        <Example :component="ExSimple" :code="ExSimpleCode" vertical/>

        <Example :component="ExMediaTemplate" :code="ExMediaTemplateCode" title="Media" vertical/>

        <Example :component="ExCardTemplate" :code="ExCardTemplateCode" title="Card" vertical/>

        <ApiView :data="api"/>
    </div>
</template>

<script>
    import api from './api/skeleton'

    import ExSimple from './examples/ExSimple'
    import ExSimpleCode from '!!raw-loader!./examples/ExSimple'

    import ExMediaTemplate from './examples/ExMediaTemplate'
    import ExMediaTemplateCode from '!!raw-loader!./examples/ExMediaTemplate'

    import ExCardTemplate from './examples/ExCardTemplate'
    import ExCardTemplateCode from '!!raw-loader!./examples/ExCardTemplate'

    export default {
        data() {
            return {
                api,
                ExSimple,
                ExSimpleCode,
                ExMediaTemplate,
                ExMediaTemplateCode,
                ExCardTemplate,
                ExCardTemplateCode
            }
        }
    }
</script>

Add missing variables docs to skeleton

<template>
    <div>
        <Example :component="ExSimple" :code="ExSimpleCode" vertical/>

        <Example :component="ExMediaTemplate" :code="ExMediaTemplateCode" title="Media" vertical/>

        <Example :component="ExCardTemplate" :code="ExCardTemplateCode" title="Card" vertical/>

        <ApiView :data="api"/>
        <VariablesView :data="variables"/>
    </div>
</template>

<script>
    import api from './api/skeleton'
    import variables from './variables/skeleton'

    import ExSimple from './examples/ExSimple'
    import ExSimpleCode from '!!raw-loader!./examples/ExSimple'

    import ExMediaTemplate from './examples/ExMediaTemplate'
    import ExMediaTemplateCode from '!!raw-loader!./examples/ExMediaTemplate'

    import ExCardTemplate from './examples/ExCardTemplate'
    import ExCardTemplateCode from '!!raw-loader!./examples/ExCardTemplate'

    export default {
        data() {
            return {
                api,
                ExSimple,
                ExSimpleCode,
                ExMediaTemplate,
                ExMediaTemplateCode,
                ExCardTemplate,
                ExCardTemplateCode
            }
        }
    }
</script>


<template src='./courses-list.html'></template>

<style scoped src='./courses-list.css'></style>

<script>
import courseCard from '../course-card/course-card'
import gql from 'graphql-tag'

// GraphQL query
// const courseList = gql`
//    query CourseList($test: String!) {
//        courses(title: $test)
//    }
// `

const courseList = gql`
    {
        courses {
            id
            title
            pitch
            member_limit
            member_count
        }
    }
`

export default {
    name: 'courses-list',
    data: function () {
        return {
            courses: ''
        }
    },
    props: {
        subject: {
            type: String
        }
    },
    components: {
        courseCard
    },
    watch: {
        subject: function (itemToSearch) {
            this.$apollo.query({
                query: courseList
                // variables: {
                //    courses: {
                //        test: 'casa'
                //    }
                // }
            })
            .then(res => {
                this.courses = res.data.courses
            })
        }
    }
}
</script>

Add query filter for course list

<template src='./courses-list.html'></template>

<style scoped src='./courses-list.css'></style>

<script>
import courseCard from '../course-card/course-card'
import gql from 'graphql-tag'

// GraphQL query
const courseFilter = gql`
    query CourseFilter($filter:CourseFilter){
        courses(filter:$filter){
            id
            title
            pitch
            member_limit
            member_count
        }
    }
`

export default {
    name: 'courses-list',
    data: function () {
        return {
            courses: ''
        }
    },
    props: {
        subject: {
            type: String
        }
    },
    components: {
        courseCard
    },
    watch: {
        subject: function (itemToSearch) {
            this.$apollo.query({
                query: courseFilter,
                variables: {
                    filter: {
                        byTopic: '',
                        byStatus: ''
                    }
                }
            })
            .then(res => {
                console.log(res)
                this.courses = res.data.courses
            })
        }
    }
}
</script>


<template lang="pug">
  transition-group(name='fade', mode='out-in')
    loader(v-if='!instanciate', key='loading')

    entries(v-else-if='instanciate && !current', key='entries')

    info-displayer(v-else-if='instanciate && current', key='current', :current='current', :return-cb='resetCurrent')
</template>

<script>
// Components
import Entries from 'components/feed/entries.vue'
import Loader from 'components/feed/loader.vue'
import InfoDisplayer from 'components/info/layout.vue'

export default {
  name: 'Feed',

  components: { Entries, Loader, InfoDisplayer },

  computed: {
    params: {
      get () {
        return this.$store.state.releases.params
      },
      set () {}
    },
    current: {
      get () {
        return this.$store.state.releases.current
      },
      set () {}
    },
    instanciate: {
      get () {
        return this.$store.state.releases.instanciate
      },
      set () {}
    }
  },

  methods: {
    resetCurrent () {
      this.$store.commit('releases/setCurrent', null)
    }
  }
}
</script>

<style lang="stylus" scoped>
  .fade-enter, .fade-leave-to
    opacity 0

  .fade-enter-active, .fade-leave
    transition all .5s
</style>

Fix release page transtion problem

<template lang="pug">
  div
    transition-group(name='fade', mode='out-in')
      loader(v-if='!instanciate', key='loading')

      entries(v-else-if='instanciate && !current', key='entries')

      info-displayer(v-else-if='instanciate && current', key='current', :current='current', :return-cb='resetCurrent')
</template>

<script>
// Components
import Entries from 'components/feed/entries.vue'
import Loader from 'components/feed/loader.vue'
import InfoDisplayer from 'components/info/layout.vue'

export default {
  name: 'Feed',

  components: { Entries, Loader, InfoDisplayer },

  computed: {
    params: {
      get () {
        return this.$store.state.releases.params
      },
      set () {}
    },
    current: {
      get () {
        return this.$store.state.releases.current
      },
      set () {}
    },
    instanciate: {
      get () {
        return this.$store.state.releases.instanciate
      },
      set () {}
    }
  },

  methods: {
    resetCurrent () {
      this.$store.commit('releases/setCurrent', null)
    }
  }
}
</script>

<style lang="stylus" scoped>
  .fade-enter, .fade-leave-to
    opacity 0

  .fade-enter-active, .fade-leave
    transition all .5s
</style>


<template>

        <Row>
            <Column :md="8">

Connections

            </Column>
        </Row>

        <Row>
            <Column :md="8">
                <ConnectToDiscord />
            </Column>

            <Column :md="8">
                <ConnectToTwitch />
            </Column>
        </Row>

</template>

Add spacing between connection buttons

<template>

Connections

            <ConnectToDiscord/>

            <ConnectToTwitch/>

</template>


<template>
    <a :class="classes"
       :href="href"
       :data-induration="induration"
       :data-outduration="outduration"
       :data-constrainwidth="constrainwidth"
       :data-hover="hover"
       :data-gutter="gutter"
       :data-beloworigin="beloworigin"
       :data-alignment="alignment"
    >
        <slot></slot>
    </a>
</template>

<script type="text/babel">
    import Buttons from '../mixins/buttons'
    import Waves from '../mixins/waves'
    import Dropdown from '../mixins/dropdown'

    export default {
        mixins: [
            Buttons,
            Waves,
            Dropdown
        ],

        props: {
            href: {
                type: String,
                default: '#!'
            }
        },

        computed: {
            classes () {
                return Object.assign(
                    this.buttonsMixin,
                    this.wavesMixing
                )
            }
        }
    }
</script>

Fix typo to enable waves effect on v-btn-link tag

<template>
    <a :class="classes"
       :href="href"
       :data-induration="induration"
       :data-outduration="outduration"
       :data-constrainwidth="constrainwidth"
       :data-hover="hover"
       :data-gutter="gutter"
       :data-beloworigin="beloworigin"
       :data-alignment="alignment"
    >
        <slot></slot>
    </a>
</template>

<script type="text/babel">
    import Buttons from '../mixins/buttons'
    import Waves from '../mixins/waves'
    import Dropdown from '../mixins/dropdown'

    export default {
        mixins: [
            Buttons,
            Waves,
            Dropdown
        ],

        props: {
            href: {
                type: String,
                default: '#!'
            }
        },

        computed: {
            classes () {
                return Object.assign(
                    this.buttonsMixin,
                    this.wavesMixin
                )
            }
        }
    }
</script>


<template>

Add a Comment

      <editor v-model="comment.body"></editor>

        You must be logged in to comment.

</template>

Make comment form take reload function

<template>

      <editor text="body" :submit="newComment"></editor>

        Add Comment

        You must be logged in to comment.

</template>


<template>
  <div>
    <login></login>
  </div>
</template>

<script>
import Login from 'components/auth/login';
import store from 'config/store';

export default {
  components: {
    Login,
  },
  store,
};
</script>

Switch to pug and add navigation

<template lang="pug">
  div
    login
    navigation
    router-view
</template>

<script>
import Login from 'components/auth/login';
import Navigation from 'components/shared/navigation';
import store from 'config/store';

export default {
  components: {
    Login,
    Navigation,
  },
  store,
};
</script>


<template>
  <q-layout>

      <button v-link="'/'">Logo</button>
      <q-toolbar-title>
        Unified Index Browser
      </q-toolbar-title>
      <q-search v-model="searchModel" class="primary titleSearch"></q-search>
      <button>Advanced</button>

    <router-view class="layout-view" :searchQuery="searchModel"></router-view>

      lilBrowser v{{lilBrowserVersion}}, Quasar v{{quasarVersion}}

  </q-layout>
</template>

Remove Advanced button until functional

<template>
  <q-layout>

      <button v-link="'/'">Logo</button>
      <q-toolbar-title>
        Unified Index Browser
      </q-toolbar-title>
      <q-search v-model="searchModel" class="primary titleSearch"></q-search>

    <router-view class="layout-view" :searchQuery="searchModel"></router-view>

      lilBrowser v{{lilBrowserVersion}}, Quasar v{{quasarVersion}}

  </q-layout>
</template>


<template>
  <li
    :title="helpText"
    :style="styleWidth"
  >
    <slot />
  </li>
</template>

<script>
export default {
    'name': 'CustomListItem',
    'props': {
        'width': {
            'type': String,
            'default': '50%',
        },
        'helpText': {
            'type': String,
            'default': '',
        },
    },
    'computed': {
        'styleWidth': function() {
            return 'width: ' + this.width + ';'
        }
    },
}
</script>

<style scoped>
li {
    box-sizing: border-box;
    display: block;
    white-space: nowrap;
    padding-top: 8px;
    padding-right: 5px;
    padding-bottom: 8px;
    padding-left: 5px;
    background: #e6e6e6;
    border-width: 2px;
    border-style: solid;
    border-color: #efefef;
}
</style>

Tidy up display of list items on narrow screens

<template>
  <li
    :title="helpText"
    :style="styleWidth"
  >
    <slot />
  </li>
</template>

<script>
export default {
    'name': 'CustomListItem',
    'props': {
        'width': {
            'type': String,
            'default': '50%',
        },
        'helpText': {
            'type': String,
            'default': '',
        },
    },
    'computed': {
        'styleWidth': function() {
            return 'width: ' + this.width + ';'
        }
    },
}
</script>

<style scoped>
li {
    box-sizing: border-box;
    display: block;
    padding-top: 8px;
    padding-right: 5px;
    padding-bottom: 8px;
    padding-left: 5px;
    background: #e6e6e6;
    border-width: 2px;
    border-style: solid;
    border-color: #efefef;
}
</style>


<script>
export default {
  props: {
    value: {
      required: true,
      note: "a model object the component should bind to"
    },
    disabled: {
      type: Boolean,
      default: false,
      note: "whether the element is disabled or not"
    },
    label: {
      type: String,
      note: "a label to be placed before the form field"
    },
    uid: {
      type: String,
      note: "a unique identifier for the component, used to bind labels to inputs"
    }
  },
  data() {
    return {
      focused: false,
      id: this.generateID()
    }
  },
  computed: {
    innerModel() {
      return this.value
    },
    hasLabel() {
      return this.label !== undefined
    }
  },
  methods: {
    generateID() {
      if (this.uid) return this.uid
      if (this.label === undefined) return ""
      return Math.random().toString(16).slice(2) + Date.now()
    },
    onChange(e) { this.$emit('input', this.stateFromEvent(e) ) }
  }
}
</script>

Fix invalid IDs in label-input relationship

<script>
export default {
  props: {
    value: {
      required: true,
      note: "a model object the component should bind to"
    },
    disabled: {
      type: Boolean,
      default: false,
      note: "whether the element is disabled or not"
    },
    label: {
      type: String,
      note: "a label to be placed before the form field"
    },
    uid: {
      type: String,
      note: "a unique identifier for the component, used to bind labels to inputs"
    }
  },
  data() {
    return {
      focused: false,
      id: this.generateID()
    }
  },
  computed: {
    innerModel() {
      return this.value
    },
    hasLabel() {
      return this.label !== undefined
    }
  },
  methods: {
    generateID() {
      if (this.uid) return this.uid
      if (this.label === undefined) return ""
      return "p" + Math.random().toString(16).slice(2) + Date.now()
    },
    onChange(e) { this.$emit('input', this.stateFromEvent(e) ) }
  }
}
</script>


<template>

    — Proudly using vue.js and made with oss by nbo | Submit new tiles with <acronym title="Pull Request" lang="en">PR</acronym> on href="https://gitlab.epfl.ch/###">gitlab —

</template>

Fix link + updated info

<template>

    — Proudly using vue.js based on an idea of nbo with help of EPFLDojo —

    Submit new tiles with <acronym title="Pull Request" lang="en">PR</acronym> on GitHub

</template>


<template>

 3" class="header">
      <button class="button" @click.prevent="addChamp">
        <template v-if="state.isAnnotation">
          Ajouter une annotation
        </template>
        <template v-else>
          Ajouter un champ
        </template>
      </button>

      <button class="button primary" @click.prevent="save">Enregistrer</button>

    <Draggable :list="state.typesDeChamp" :options="{handle:'.handle'}">
      <DraggableItem
        v-for="(item, index) in state.typesDeChamp"
        :state="state"
        :index="index"
        :item="item"
        :key="item.id" />
    </Draggable>

      <button class="button" @click.prevent="addChamp">
        <template v-if="state.isAnnotation">
          Ajouter une annotation
        </template>
        <template v-else>
          Ajouter un champ
        </template>
      </button>

      <button class="button primary" @click.prevent="save">Enregistrer</button>

</template>

Add scroll-to to add champ buttons

<template>

 3" class="header">
      <button class="button" v-scroll-to="'.footer'" @click.prevent="addChamp">
        <template v-if="state.isAnnotation">
          Ajouter une annotation
        </template>
        <template v-else>
          Ajouter un champ
        </template>
      </button>

      <button class="button primary" @click.prevent="save">Enregistrer</button>

    <Draggable :list="state.typesDeChamp" :options="{handle:'.handle'}">
      <DraggableItem
        v-for="(item, index) in state.typesDeChamp"
        :state="state"
        :index="index"
        :item="item"
        :key="item.id" />
    </Draggable>

      <button class="button" v-scroll-to="'.footer'" @click.prevent="addChamp">
        <template v-if="state.isAnnotation">
          Ajouter une annotation
        </template>
        <template v-else>
          Ajouter un champ
        </template>
      </button>

      <button class="button primary" @click.prevent="save">Enregistrer</button>

</template>


<template>

  {{ label }}

    <input
      class="input"
      ref="input"
      :placeholder="placeholder"
      :type="type"
      :value="value"
      @input="updateValue()"
    />

</template>

Allow textfield to emit enter key up event

<template>

  {{ label }}

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

</template>


<template>

      <slot name="panel-body" />

</template>

Remove b-container as incorrect component to use.


<template>

          <input
            type="radio"
            name="screensaver"
            :data-name="saver.name"
            :value="saver.key"

            :checked="checked"
          >
          {{ saver.name }}

            {{ saver.description }}

</template>

Add a little tag for local/custom screensavers

<template>

          <input
            type="radio"
            name="screensaver"
            :data-name="saver.name"
            :value="saver.key"
            :checked="checked"
          >
          {{ saver.name }}
          <template v-if="saver.editable === true">
             (custom)
          </template>

            {{ saver.description }}

</template>


<template>
    <transition
        :enter-active-class="transition.enter"
        :leave-active-class="transition.leave">

{{ message }}

                <button class="button is-dark">{{ actionText }}</button>

    </transition>
</template>

Remove is-dark class in snackbar button

<template>
    <transition
        :enter-active-class="transition.enter"
        :leave-active-class="transition.leave">

{{ message }}

                <button class="button">{{ actionText }}</button>

    </transition>
</template>


<template>
<section class="page page--welcome">
  <Welcome />
  <MapsSection class="section" />
  <DatasetsSection class="section" />
</section>
</template>

<script>
import Welcome from './WelcomeSection/Welcome.vue';
import MapsSection from './MapsSection/MapsSection.vue';
import DatasetsSection from './DatasetsSection/DatasetsSection.vue';

export default {
  name: 'Home',
  components: {
    Welcome,
    MapsSection,
    DatasetsSection
  },
  beforeRouteLeave (to, from, next) {
    this.$store.dispatch('datasets/resetFilters');
    this.$store.dispatch('maps/resetFilters');
    next();
  }
};
</script>

<style scoped lang="scss">
@import 'stylesheets/new-dashboard/variables';

.page--welcome {
  padding: 0;
}
</style>

Remove unnecessary border bottom in Datasets Section

<template>
<section class="page page--welcome">
  <Welcome />
  <MapsSection class="section" />
  <DatasetsSection class="section section--noBorder" />
</section>
</template>

<script>
import Welcome from './WelcomeSection/Welcome.vue';
import MapsSection from './MapsSection/MapsSection.vue';
import DatasetsSection from './DatasetsSection/DatasetsSection.vue';

export default {
  name: 'Home',
  components: {
    Welcome,
    MapsSection,
    DatasetsSection
  },
  beforeRouteLeave (to, from, next) {
    this.$store.dispatch('datasets/resetFilters');
    this.$store.dispatch('maps/resetFilters');
    next();
  }
};
</script>

<style scoped lang="scss">
@import 'stylesheets/new-dashboard/variables';

.page--welcome {
  padding: 0;
}
</style>


<template>
    <!-- App -->
    <f7-app :params="f7params">
        <f7-statusbar></f7-statusbar>
        <f7-panel left cover>
            <f7-view url="/panel-left/" links-view=".view-main" />
        </f7-panel>
        <f7-view url="/" :main="true" class="ios-edges"></f7-view>
    </f7-app>
</template>
<script>
// Import Routes...
import routes from './routes.js'

let theme = 'auto';
if (document.location.search.indexOf('theme=') >= 0) {
  theme = document.location.search.split('theme=')[1].split('&')[0];
}

export default {
    data() {
        return {
            f7params: {
                theme,
                routes,
                id: 'io.framework7.testapp',
            }
        }
    }
}
</script>

Update for native/hardware back button

<template>
    <!-- App -->
    <f7-app :params="f7params">
        <f7-statusbar></f7-statusbar>
        <f7-panel left cover>
            <f7-view url="/panel-left/" links-view=".view-main" />
        </f7-panel>
        <f7-view url="/" :main="true" class="ios-edges"></f7-view>
    </f7-app>
</template>
<script>
// Import Routes...
import routes from './routes.js'

let theme = 'auto';
if (document.location.search.indexOf('theme=') >= 0) {
  theme = document.location.search.split('theme=')[1].split('&')[0];
}

export default {
    data() {
        return {
            f7params: {
                theme,
                routes,
                id: 'io.framework7.testapp',
            }
        }
    },
    mounted() {
      let vm = this;
      document.addEventListener('backbutton', () => {
        vm.$f7.views.current.router.back()
      }, false)
    }
}
</script>


<template>

      Fields

{{ field.name }}:

{{ getValue(field) }}

</template>

Support fields with no value


<template>

</template>

<script>
 export default {
   data() { return { }; }
 }
</script>

<style lang="scss" scoped>
  @import '~styles/vars.scss';
</style>

Add sms list and dummy data

<template>

      <button class="button" :class="action.class">

        {{action.label}}
      </button>

  <sms-list :list="list" :checked-rows="checkedRows"></sms-list>

</template>


<template>

      <Collection :collection="collection" />

</template>

Allow each column to read its own collections and render


<template>
    <div>
        <router-view></router-view>
    </div>
</template>

<script>
    export default {

        // Auth stuff
        ready() {
            this.$on('userHasLoggedOut', function () {
                this.destroyLogin()
            })

            this.$on('userHasLoggedIn', function (user) {
                console.log('user has logged in')
                this.setLogin(user)
            })

            var token = localStorage.getItem('jwt-token')
            if (token !== null && token !== 'undefined') {
                var that = this
                client({path: '/users/me'}).then(
                        function (response) {
                            // User has successfully logged in using the token from storage
                            that.setLogin(response.entity.user)
                            // broadcast an event telling our children that the data is ready and views can be rendered
                            that.$broadcast('data-loaded')
                        },
                        function (response) {
                            // Login with our token failed, do some cleanup and redirect if we're on an authenticated route
                            that.destroyLogin()
                        }
                )
            }
        },

        data() {
            return {
                user: null,
                token: null,
                authenticated: false
            }
        },
        methods: {
            setLogin(user) {
                this.user = user;
                this.authenticated = true;
                this.token = localStorage.getItem('jwt-token')
            },
            destroyLogin(user) {
                this.user = null;
                this.token = null;
                this.authenticated = false;
                localStorage.removeItem('jwt-token')
                if(this.$route.auth) this.$route.router.go('/auth/login')
            }
        }
    }
</script>

Remove stored lang-data on logout

<template>
    <div>
        <router-view></router-view>
    </div>
</template>

<script>
    export default {

        // Auth stuff
        ready() {
            this.$on('userHasLoggedOut', function () {
                this.destroyLogin()
            })

            this.$on('userHasLoggedIn', function (user) {
                console.log('user has logged in')
                this.setLogin(user)
            })

            var token = localStorage.getItem('jwt-token')
            if (token !== null && token !== 'undefined') {
                var that = this
                client({path: '/users/me'}).then(
                        function (response) {
                            // User has successfully logged in using the token from storage
                            that.setLogin(response.entity.user)
                            // broadcast an event telling our children that the data is ready and views can be rendered
                            that.$broadcast('data-loaded')
                        },
                        function (response) {
                            // Login with our token failed, do some cleanup and redirect if we're on an authenticated route
                            that.destroyLogin()
                        }
                )
            }
        },

        data() {
            return {
                user: null,
                token: null,
                authenticated: false
            }
        },
        methods: {
            setLogin(user) {
                this.user = user;
                this.authenticated = true;
                this.token = localStorage.getItem('jwt-token')
            },
            destroyLogin(user) {
                this.user = null;
                this.token = null;
                this.authenticated = false;
                localStorage.removeItem('jwt-token');
                localStorage.removeItem('lang');
                if(this.$route.auth) this.$route.router.go('/auth/login')
            }
        }
    }
</script>


<template>
    <span
        :data-label="label"
        :class="[newType, position, size, {
            'tooltip': active,
            'is-square': square,
            'is-animated': newAnimated,
            'is-always': always,
            'is-multiline': multilined,
            'is-dashed': dashed
        }]">
        <slot></slot>
    </span>
</template>

<script>
    import config from '../../utils/config'

    export default {
        name: 'bTooltip',
        props: {
            active: {
                type: Boolean,
                default: true
            },
            type: String,
            label: String,
            position: {
                type: String,
                default: 'is-top',
                validator(value) {
                    return [
                        'is-top',
                        'is-bottom',
                        'is-left',
                        'is-right'
                    ].indexOf(value) > -1
                }
            },
            always: Boolean,
            animated: Boolean,
            square: Boolean,
            dashed: Boolean,
            multilined: Boolean,
            size: {
                type: String,
                default: 'is-medium'
            }
        },
        data() {
            return {
                newType: this.type || config.defaultTooltipType,
                newAnimated: this.animated || config.defaultTooltipAnimated
            }
        }
    }
</script>

Fix tooltip type not being reactive

<template>
    <span
        :data-label="label"
        :class="[newType, position, size, {
            'tooltip': active,
            'is-square': square,
            'is-animated': newAnimated,
            'is-always': always,
            'is-multiline': multilined,
            'is-dashed': dashed
        }]">
        <slot></slot>
    </span>
</template>

<script>
    import config from '../../utils/config'

    export default {
        name: 'bTooltip',
        props: {
            active: {
                type: Boolean,
                default: true
            },
            type: String,
            label: String,
            position: {
                type: String,
                default: 'is-top',
                validator(value) {
                    return [
                        'is-top',
                        'is-bottom',
                        'is-left',
                        'is-right'
                    ].indexOf(value) > -1
                }
            },
            always: Boolean,
            animated: Boolean,
            square: Boolean,
            dashed: Boolean,
            multilined: Boolean,
            size: {
                type: String,
                default: 'is-medium'
            }
        },
        computed: {
            newType() {
                return this.type || config.defaultTooltipType
            },
            newAnimated() {
                return this.animated || config.defaultTooltipAnimated
            }
        }
    }
</script>


<template>
  <div id="overview">
    <canvas width="400" height="400" id="overview-canvas">
      Canvas unsupported, use a newer browser.
    </canvas>
  </div>
</template>

<script>
import DisplayMap from "../game/utils/DisplayMap";

export default {
  mounted() {
    DisplayMap();
  }
};
</script>

<style>
#overview-canvas {
  background-color: var(--ui-darker);
}

#overview {
  padding: 5px;
  margin-bottom: 2px;
  color: var(--text-blur);
  background-color: var(--ui-darker);
  border-color: var(--ui-border);
  font-family: "Ubuntu Mono", monospace;
  font-size: 0.9em;
  white-space: pre-wrap;
  text-align: center;
  overflow-y: auto;
}

#overview::-webkit-scrollbar {
  width: 8px;
}

#overview::-webkit-scrollbar-track {
  background-color: var(--ui-darker);
}

#overview::-webkit-scrollbar-thumb {
  background-color: var(--ui-border);
}
</style>

Use rot.js for displaying overview

<template>
  <div id="overview" @click="resizeOverview">
    <!-- <canvas width="400" height="400" id="overview-canvas">
      Canvas unsupported, use a newer browser.
    </canvas> -->
  </div>
</template>

<script>
import DisplayMap from "../game/utils/DisplayMap";
import * as ROT from "rot-js";
import _ from "lodash";

export default {
  data() {
    return {
      display: new ROT.Display({
        fontSize: 10,
        fg: "#daddd8",
        bg: "#1e1e1e",
        forceSquareRatio: true
      })
    }
  },
  mounted() {
    // DisplayMap();

    document.querySelector("#overview").appendChild(this.display.getContainer());

    this.resizeOverview();

    window.addEventListener("resize", _.debounce(this.resizeOverview, 100));
  },
  methods: {
    resizeOverview() {
      let overviewDiv = document.querySelector("#overview");

      this.display.setOptions({
        width: ~~(overviewDiv.offsetWidth / 10),
        height: ~~(overviewDiv.offsetHeight / 10)
      });
    }
  }
};
</script>

<style>
#overview-canvas {
  background-color: var(--ui-darker);
}

#overview {
  color: var(--text-blur);
  background-color: #1e1e1e;
  border-color: var(--ui-border);
  font-family: "Ubuntu Mono", monospace;
  font-size: 0.9em;
  white-space: pre-wrap;
  text-align: center;
  overflow-y: auto;

  display: flex;
  justify-content: center;
  align-items: center;
}

#overview::-webkit-scrollbar {
  width: 8px;
}

#overview::-webkit-scrollbar-track {
  background-color: var(--ui-darker);
}

#overview::-webkit-scrollbar-thumb {
  background-color: var(--ui-border);
}
</style>


<template>

Overview

</template>

Use canvas for overview display

<template>
  <div id="overview">
    <canvas width="400" height="400" id="overview-canvas">
      Canvas unsupported, use a newer browser.
    </canvas>
  </div>
</template>

<script>
export default {};
</script>

<style>
#overview-canvas {
  background-color: var(--ui-darker);
}

#overview {
  padding: 5px;
  margin-bottom: 2px;
  color: var(--text-blur);
  background-color: var(--ui-darker);
  border-color: var(--ui-border);
  font-family: "Ubuntu Mono", monospace;
  font-size: 0.9em;
  white-space: pre-wrap;
  text-align: center;
  overflow-y: auto;
}

#overview::-webkit-scrollbar {
  width: 8px;
}

#overview::-webkit-scrollbar-track {
  background-color: var(--ui-darker);
}

#overview::-webkit-scrollbar-thumb {
  background-color: var(--ui-border);
}
</style>


<template>
  <div class="notification" v-html="renderedNotification">
  </div>
</template>

<script>
 import UserStub from '@/components/Users/Stub'

 export default {
   name: 'notification',

   components: {
     UserStub
   },

   computed: {
     renderedNotification: function () {
       let s = this.notification.body.split(' ')
       let username = '<router-link :to=\'/users/' + s[0] + '\'>' +
                      s[0] + '</router-link>'
       let rest = s.slice(1)
       return username + ' ' + rest.join(' ')
     }
   },

   props: {
     notification: {
       type: Object,
       required: true
     }
   }
 }
</script>

Create vue component for notifications

<template>

      <user-stub :username="notification.actioningUser" />
      {{ actionText }}
      <router-link :to="'/tickets/' + notification.actionedTicket">
        {{ notification.actionedTicket }}
      </router-link>

</template>


<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>

Attach 'changeViewMode' and 'execute' event listener to userInput element

<template>
  <div>
    <userInputs
      v-on:changeViewMode="changeViewMode"
      v-on:execute="executeAction"
    ></userInputs>
    <documents
      :viewMode="viewMode"
    ></documents>
  </div>
</template>

<script>
// import fileManager from 'src/logic/filemanager';
import documents from './Documents';
import userInputs from './UserInputs';

// const mockFolder = {
//   id: 0,
//   name: 'root',
//   path: '/',
//   parentFolder: null,
//   childFolderList: [], // list of folders in current folder
//   childFileList: [], // list of files in current folder
// };

export default {
  components: {
    documents,
    userInputs,
  },
  data() {
    return {
      viewMode: 'listView',
    };
  },
  watch: {
  },
  methods: {
    changeViewMode(viewMode) {
      this.viewMode = viewMode;
    },
    executeAction(action) {
      this.$emit('executeAction', action);
    }
  }
  // mounted() {
  //   const startPromise = fileManager.start();

  //   startPromise
  //   .then((rootFolder) => {
  //     console.log(rootFolder, 'rootFolder');
  //   })
  //   .catch((error) => {
  //     console.log(error);
  //   });
  // }
};

</script>

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

</style>


<template lang="pug">
.form-group(:class="{ 'label-floating' : labelFloating }")
  label.control-label(v-if='label')
    | {{label}}
  textarea.form-control(v-bind='$props', @input="$emit('input',$event.target.value)", v-if="type === 'textarea'")
  input.form-control(v-bind='$props', :value='value', @input="$emit('input',$event.target.value)", v-else)
</template>
<script>
export default {
  name: 'md-fg-input',
  props: {
    type: {
      type: String,
      default: 'text'
    },
    label: String,
    name: String,
    disabled: Boolean,
    placeholder: String,
    value: [String, Number],
    labelFloating: Boolean,
    rows: Number,
    cols: Number
  }
}

</script>
<style lang="scss"></style>

Fix v-model directive in textarea component

<template lang="pug">
.form-group(:class="{ 'label-floating' : labelFloating }")
  label.control-label(v-if='label')
    | {{label}}
  textarea.form-control(v-bind='$props', @input="$emit('input',$event.target.value)" , v-if="type === 'textarea'")
  input.form-control(v-bind='$props', :value='value', @input="$emit('input',$event.target.value)", v-else)
</template>
<script>
export default {
  name: 'md-fg-input',
  props: {
    type: {
      type: String,
      default: 'text'
    },
    label: String,
    name: String,
    disabled: Boolean,
    placeholder: String,
    value: [String, Number],
    labelFloating: Boolean,
    rows: Number,
    cols: Number
  }
}

</script>
<style lang="scss"></style>


<template>

{{ $store.state.count }}

    <button @click="$store.commit('decrement');">-</button>
    <button @click="$store.commit('increment');">+</button>
    <button @click="$store.commit('increment2');">+2</button>

      Open this example in
      multiple
      tabs and play with
      +,
      -, and
      +2 buttons.

      + and
      - actions are shared using
      vuex-shared-mutations, so the value will be in sync while you're using these buttons

      +2 action, however, is not shared, so you can put your tabs
      out-of-sync. This is intentional, we're sharing
      actions, not entire
      system state

      Demo source

</template>

Fix wording in nuxt example

<template>

{{ $store.state.count }}

    <button @click="$store.commit('decrement');">-</button>
    <button @click="$store.commit('increment');">+</button>
    <button @click="$store.commit('increment2');">+2</button>

      Open this example in
      multiple
      tabs and play with
      +,
      -, and
      +2 buttons.

      + and
      - mutations are shared using
      vuex-shared-mutations, so the value will be in sync while you're using these buttons

      +2 mutation, however, is not shared, so you can put your tabs
      out-of-sync. This is intentional, we're sharing
      mutations, not entire
      system state

      Demo source

</template>


<template>

    <status-icon status="failed" />

        <template v-if="mr.mergeError">{{ mr.mergeError }}.</template>
        {{ s__("mrWidget|This merge request failed to be merged automatically") }}

      <button
        @click="refreshWidget"
        :disabled="isRefreshing"
        type="button"
        class="btn btn-xs btn-default"
      >
        <loading-icon v-if="isRefreshing" />
        {{ s__("mrWidget|Refresh") }}
      </button>

</template>

Update icon to match EE

<template>

    <status-icon status="warning" />

        <template v-if="mr.mergeError">{{ mr.mergeError }}.</template>
        {{ s__("mrWidget|This merge request failed to be merged automatically") }}

      <button
        @click="refreshWidget"
        :disabled="isRefreshing"
        type="button"
        class="btn btn-xs btn-default"
      >
        <loading-icon v-if="isRefreshing" />
        {{ s__("mrWidget|Refresh") }}
      </button>

</template>


<template lang="html">
  <v-btn
    id='save-button'
    icon
    @click.native="saveAnnotation()">
    <v-icon>
      save
    </v-icon>
  </v-btn>
</template>

<script>
import { mapActions } from 'vuex'

export default {
  mounted () {
    window.addEventListener('keydown', this.keyDown)
  },

  methods: {
    ...mapActions({
      saveAnnotation: 'annotation/saveAnnotation'
    }),

    // Attach event listeners for the keyboard shortcuts
    keyDown (e) {
      if (e.key === 's' && (e.metaKey === true || e.ctrlKey === true)) {
        e.preventDefault()
        document.getElementById('save-button').click()
      }
    }

  }
}
</script>

<style lang='css'>
</style>

Refactor save to 'backend' vuex module

<template lang="html">
  <v-btn
    id='save-button'
    icon
    @click.native="saveAnnotation()">
    <v-icon>
      save
    </v-icon>
  </v-btn>
</template>

<script>
import { mapActions } from 'vuex'

export default {
  mounted () {
    window.addEventListener('keydown', this.keyDown)
  },

  methods: {
    ...mapActions({
      saveAnnotation: 'backend/saveAnnotation'
    }),

    // Attach event listeners for the keyboard shortcuts
    keyDown (e) {
      if (e.key === 's' && (e.metaKey === true || e.ctrlKey === true)) {
        e.preventDefault()
        document.getElementById('save-button').click()
      }
    }

  }
}
</script>

<style lang='css'>
</style>


<template>

{{ $t('server_down.title') }}

      {{ $t('server_down.text') }}

</template>

Improve server down page behavior


<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.') }}

</template>

Add utm tags to DMAH link in covid banner


<template>

  <div>
    <UiAlert
      v-if="notification === notificationTypes.PAGELOAD_FAILURE"
      type="error"
      @dismiss="dismiss()"
    >
      {{ $tr('pageloadFailure') }}
    </UiAlert>

    <UiAlert
      v-if="notification === notificationTypes.SAVE_SUCCESS"
      type="success"
      @dismiss="dismiss()"
    >
      {{ $tr('saveSuccess') }}
    </UiAlert>

    <UiAlert
      v-if="notification === notificationTypes.SAVE_FAILURE"
      type="success"
      @dismiss="dismiss()"
    >
      {{ $tr('saveFailure') }}
    </UiAlert>
  </div>

</template>

<script>

  import UiAlert from 'kolibri.coreVue.components.UiAlert';
  import { notificationTypes } from '../../constants';

  export default {
    name: 'ConfigPageNotifications',
    components: { UiAlert },
    props: {
      notification: {
        type: String,
        required: false,
      },
    },
    computed: { notificationTypes: () => notificationTypes },
    methods: {
      dismiss() {
        this.$emit('dismiss');
      },
    },
    $trs: {
      saveFailure: 'There was a problem saving your settings',
      saveSuccess: 'Facility settings updated',
      pageloadFailure: 'There was a problem loading your settings',
    },
  };

</script>

Fix error notification ui for save settings

<template>

  <div>
    <UiAlert
      v-if="notification === notificationTypes.PAGELOAD_FAILURE"
      type="error"
      @dismiss="dismiss()"
    >
      {{ $tr('pageloadFailure') }}
    </UiAlert>

    <UiAlert
      v-if="notification === notificationTypes.SAVE_SUCCESS"
      type="success"
      @dismiss="dismiss()"
    >
      {{ $tr('saveSuccess') }}
    </UiAlert>

    <UiAlert
      v-if="notification === notificationTypes.SAVE_FAILURE"
      type="error"
      @dismiss="dismiss()"
    >
      {{ $tr('saveFailure') }}
    </UiAlert>
  </div>

</template>

<script>

  import UiAlert from 'kolibri.coreVue.components.UiAlert';
  import { notificationTypes } from '../../constants';

  export default {
    name: 'ConfigPageNotifications',
    components: { UiAlert },
    props: {
      notification: {
        type: String,
        required: false,
      },
    },
    computed: { notificationTypes: () => notificationTypes },
    methods: {
      dismiss() {
        this.$emit('dismiss');
      },
    },
    $trs: {
      saveFailure: 'There was a problem saving your settings',
      saveSuccess: 'Facility settings updated',
      pageloadFailure: 'There was a problem loading your settings',
    },
  };

</script>


<template>
    <select class="ui fluid dropdown" v-model="data" @change="onChange($event)">
        <slot></slot>
    </select>
</template>

<script>
    export default {
        name: "dropdown",
        props: {
            value: {
                required: false
            }
        },
        mounted() {
            $(document).ready(() => {
                this.emit = false;
                $(this.$el).dropdown('set selected', this.value);
                this.emit = true;
            });
            this.data = this.value;
        },
        data() {
            return {
                data: "",
                emit: true,
            }
        },
        methods: {
            onChange(event) {
                if (this.emit) {
                    this.$emit('input', this.data);
                    this.$emit('change', event);
                }
            }
        }
    }
</script>

<style scoped>

</style>

Fix error when data is undefined

<template>
    <select class="ui fluid dropdown" v-model="data" @change="onChange($event)">
        <slot></slot>
    </select>
</template>

<script>
    export default {
        name: "dropdown",
        props: {
            value: {
                required: false
            }
        },
        mounted() {
            $(document).ready(() => {
                this.emit = false;
                $(this.$el).dropdown('set selected', this.value);
                this.emit = true;
            });
            this.data = this.value;
        },
        data() {
            return {
                data: [],
                emit: true,
            }
        },
        methods: {
            onChange(event) {
                if (this.emit) {
                    this.$emit('input', this.data);
                    this.$emit('change', event);
                }
            }
        }
    }
</script>

<style scoped>

</style>


<template>

      <KLabeledIcon>
        <KIcon v-if="contentType" slot="icon" :[iconType]="true" />
        {{ title }}
      </KLabeledIcon>
      <ProgressIcon class="progress-icon" :progress="progress" />

</template>

Fix RTL behavior of ContentPage ProgressIcon


<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>

Adjust dashboard tree width on xs viewports

<template>
  <div>
    <v-toolbar
      fixed
      dark
      flat
      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 xs12 sm10 offset-sm1>
          <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>

  <span>
    <KLabeledIcon :style="active ? {} : { color: $themeTokens.textDisabled }">
      <KIcon
        slot="icon"
        icon="dot"
        :color="active ? $themeTokens.success : $themeTokens.textDisabled"
      />
      {{ label }}
    </KLabeledIcon>
  </span>

</template>

<script>

  import KLabeledIcon from 'kolibri.coreVue.components.KLabeledIcon';
  import KIcon from 'kolibri.coreVue.components.KIcon';
  import themeMixin from 'kolibri.coreVue.mixins.themeMixin';
  import { coachStringsMixin } from './commonCoachStrings';

  export default {
    name: 'QuizActive',
    components: {
      KLabeledIcon,
      KIcon,
    },
    mixins: [themeMixin, coachStringsMixin],
    props: {
      active: {
        type: Boolean,
        required: true,
      },
    },
    computed: {
      label() {
        return this.active ? this.coachString('activeLabel') : this.coachString('inactiveLabel');
      },
    },
  };

</script>

<style lang="scss" scoped></style>

Update the quiz label active

<template>

  <span>
    <KLabeledIcon :style="active ? {} : { color: $themeTokens.textDisabled }">
      <KIcon
        slot="icon"
        icon="dot"
        :color="active ? $themeTokens.success : $themeTokens.textDisabled"
      />
      {{ label }}
    </KLabeledIcon>
  </span>

</template>

<script>

  import KLabeledIcon from 'kolibri.coreVue.components.KLabeledIcon';
  import KIcon from 'kolibri.coreVue.components.KIcon';
  import themeMixin from 'kolibri.coreVue.mixins.themeMixin';
  import { coachStringsMixin } from './commonCoachStrings';

  export default {
    name: 'QuizActive',
    components: {
      KLabeledIcon,
      KIcon,
    },
    mixins: [themeMixin, coachStringsMixin],
    props: {
      active: {
        type: Boolean,
        required: true,
      },
    },
    computed: {
      label() {
        return this.active ? this.$tr('activeQuizLabel') : this.$tr('inactiveQuizLabel');
      },
    },
    $trs: {
      activeQuizLabel: "Active",
      inactiveQuizLabel: "Inactive",
    }
  };

</script>

<style lang="scss" scoped></style>


<!--
  - @copyright 2021 Passwords App
  -
  - @author Marius David Wieschollek
  - @license AGPL-3.0
  -
  - This file is part of the Passwords App
  - created by Marius David Wieschollek.
  -->

<template>
    <select v-model="model">
        <translate tag="option" value="" say="Type" selected v-if="empty"/>
        <translate tag="option" value="text" say="Text"/>
        <translate tag="option" value="secret" say="Secret"/>
        <translate tag="option" value="email" say="Email"/>
        <translate tag="option" value="url" say="Link"/>
        <translate tag="option" value="file" say="File"/>
        <translate tag="option" value="data" say="Data" v-if="showData"/>
    </select>
</template>

<script>
    import Translate from "@vc/Translate";
    import SettingsService from "@js/Services/SettingsService";

    export default {
        components: {Translate},
        props     : {
            value: String,
            empty: {
                type   : Boolean,
                default: false
            }
        },

        data() {
            return {
                model   : this.value,
                showData: SettingsService.get('client.ui.custom.fields.show.hidden', false)
            };
        },

        watch: {
            value(value) {
                this.model = value;
            },
            model(value) {
                if(this.value !== value) {
                    this.$emit('input', value);
                }
            }
        }
    };
</script>

<style scoped>

</style>

Rename type link to type website

<!--
  - @copyright 2021 Passwords App
  -
  - @author Marius David Wieschollek
  - @license AGPL-3.0
  -
  - This file is part of the Passwords App
  - created by Marius David Wieschollek.
  -->

<template>
    <select v-model="model">
        <translate tag="option" value="" say="Type" selected v-if="empty"/>
        <translate tag="option" value="text" say="Text"/>
        <translate tag="option" value="secret" say="Secret"/>
        <translate tag="option" value="email" say="Email"/>
        <translate tag="option" value="url" say="Website"/>
        <translate tag="option" value="file" say="File"/>
        <translate tag="option" value="data" say="Data" v-if="showData"/>
    </select>
</template>

<script>
    import Translate from "@vc/Translate";
    import SettingsService from "@js/Services/SettingsService";

    export default {
        components: {Translate},
        props     : {
            value: String,
            empty: {
                type   : Boolean,
                default: false
            }
        },

        data() {
            return {
                model   : this.value,
                showData: SettingsService.get('client.ui.custom.fields.show.hidden', false)
            };
        },

        watch: {
            value(value) {
                this.model = value;
            },
            model(value) {
                if(this.value !== value) {
                    this.$emit('input', value);
                }
            }
        }
    };
</script>

<style scoped>

</style>


<template>

        <button type="button" class="close" aria-hidden="true" @click="close">×</button>

            {{alert.title}}

        {{alert.details}}

</template>

Handle line returns in notification details

<template>

        <button type="button" class="close" aria-hidden="true" @click="close">×</button>

            {{alert.title}}

        {{{ details }}}

</template>