Fix inability to fetch feed data right after logging in

<template>

        <lazy-image
          :width="428"
          :height="321"
          :background-color="'#28342c'"
          :lazy-src="'/img/nature.jpg'"
        />

          mode_edit

        Здравствуйте, {{ user }}!

Нажмите на ручку, чтобы просмотреть важную информацию.

        <template v-if="isLoggedIn">

            Инфо
            close

          <template v-if="!feedLoad">

Оценки за сегодня

                <template v-for="(card, indexCard) in feedData.Feed.Days[0].MarkCards">
                  <template v-for="(value, index) in card.Values">
                    <li
                      class="collection-item avatar z-depth-1"
                      :key="`item-mark-${indexCard}-${index}`"
                    >
                      subject

                      {{ value.Value }}

{{ card.Subject.Name }} - {{ card.WorkType.Kind }}

                    <div
                      :style="{display: 'block', clear: 'both', height: '2px'}"
                      :key="`delimiter-mark-${indexCard}-${index}`"
                    >

                  </template>
                </template>

Важные работы

                <template v-for="(work, index) in feedData.Feed.Days[0].ImportantWorks">
                  <li class="collection-item avatar z-depth-1" :key="`item-warn-${index}`">
                    warning

                    {{ work.Subject.Name }}

{{ work.WorkType.Kind }}

                  <div
                    :style="{display: 'block', clear: 'both', height: '2px'}"
                    :key="`delimiter-warn-${index}`"
                  >

                </template>

Объявления

                <template
                  v-for="(notification, index) in feedData.Feed.Days[0].Schedule.Notifications"
                >
                  <li class="collection-item avatar z-depth-1" :key="`item-notif-${index}`">
                    message

                    {{ notification.Title }}

                  <div
                    :style="{display: 'block', clear: 'both', height: '2px'}"
                    :key="`delimiter-notif-${index}`"
                  >

                </template>

                Спасибо, что решили протестировать beta-версию DnevnikClient. Я очень это ценю.

Обо всех ошибках просьба сообщать, открывая Issue в
                репозитории на GitHub.

Надеюсь, вам нравится клиент, и вы довольны его функционалом и проделанной мной работой.

Напоминаю, что проект - Open Source, так что вы в любой момент можете помочь разработке.

By
                @limitedeternity

          </template>
          <template v-else>

          </template>
        </template>
        <template v-else>

            Инфо
            close

            Спасибо, что решили протестировать beta-версию DnevnikClient. Я очень это ценю.

Обо всех ошибках просьба сообщать, открывая Issue в
            репозитории на GitHub.

Надеюсь, вам нравится клиент, и вы довольны его функционалом и проделанной мной работой.

Напоминаю, что проект - Open Source, так что вы в любой момент можете помочь разработке.

By
            @limitedeternity

        </template>

</template>


modify user phone verify

modify user phone verify

<template>
  <mdl-snackbar display-on="mailSent"></mdl-snackbar>

              用户名：
              <input disabled id="username-input" @change="infoChange" value="{{userInfo.username}}">

              是否验证：
              <input disabled id="verify-input" @change="infoChange" value="{{userInfo.is_verified}}">

              姓名：
              <input id="name-input" @change="infoChange" value="{{userInfo.name}}">

              邮箱：
              <input id="email-input" @change="infoChange" value="{{userInfo.email}}">

              固定电话：
              <input id="phone-input" @change="infoChange" value="{{userInfo.telephone}}">

              手机号码：
              <input id="mobile-input" @change="infoChange" value="{{userInfo.mobile}}">

              位置：
              <input disabled id="location-input" @change="infoChange" value="{{userInfo.location}}">

              单位：
              <input id="organization-input" @change="infoChange" value="{{userInfo.organization}}">

              职务/职称：
              <input id="position-input" @change="infoChange" value="{{userInfo.position}}">

              注册时间：
              <input disabled id="createdAt-input" value="{{userInfo.createdAt}}">

</template>


Removed the columns ID, registered by and registered date from the table as they are not useful to the normal user

<template>

Personregister

                        <input v-model="filterKey"
                               class="input"
                               placeholder="Søk"/>

                                <input type="checkbox"
                                       :value="currentYear"
                                       v-model="membershipYearsFilter">
                                Medlem {{currentYear}}

                                <input type="checkbox"
                                       :value="nextYear"
                                       v-model="membershipYearsFilter">
                                Medlem {{nextYear}}

                                <input type="checkbox"
                                       v-model="noMembershipFilter">
                                Uten medlemskap

            <router-link :to="'/personaddoredit'">
                Legg til person
            </router-link>

                Viser {{filteredPersons.length}} av {{persons.length}}

                    Navn
                    E-post
                    Telefon
                    Adresse
                    Endre
                    Medlem
                    Administrer medlemskap

                    {{person.fullName}}
                    {{person.email}}
                    {{person.phone}}
                    {{person.address}}

                        <router-link :to="`/personaddoredit/${person.id}`">
                            Endre
                        </router-link>

                    {{hasMembership(person.memberships) ? 'Ja' : 'Nei'}}

                        <router-link :to="`/membership/${person.id}`">
                            Medlemskap
                        </router-link>

        <not-authenticated :auth="auth"
                           :authenticated="authenticated">
        </not-authenticated>

</template>

<template>

Personregister

                        <input v-model="filterKey"
                               class="input"
                               placeholder="Søk"/>

                                <input type="checkbox"
                                       :value="currentYear"
                                       v-model="membershipYearsFilter">
                                Medlem {{currentYear}}

                                <input type="checkbox"
                                       :value="nextYear"
                                       v-model="membershipYearsFilter">
                                Medlem {{nextYear}}

                                <input type="checkbox"
                                       v-model="noMembershipFilter">
                                Uten medlemskap

            <router-link :to="'/personaddoredit'">
                Legg til person
            </router-link>

                Viser {{filteredPersons.length}} av {{persons.length}}

                    ID
                    Navn
                    E-post
                    Telefon
                    Adresse
                    Endre
                    Registrert av
                    Registrert dato
                    Medlem
                    Administrer medlemskap

                    {{person.id}}
                    {{person.fullName}}
                    {{person.email}}
                    {{person.phone}}
                    {{person.address}}

                        <router-link :to="`/personaddoredit/${person.id}`">
                            Endre
                        </router-link>

                    {{person.createdBy}}
                    {{person.createdDate | formatDate}}
                    {{hasMembership(person.memberships) ? 'Ja' : 'Nei'}}

                        <router-link :to="`/membership/${person.id}`">
                            Medlemskap
                        </router-link>

        <not-authenticated :auth="auth"
                           :authenticated="authenticated">
        </not-authenticated>

</template>


Update messaging-room.vue

Update messaging-room.vue


Add line before rule

Add line before rule

Signed-off-by: marco <3397c7e210a9d1588196cfebb12b2c6b133f6934@pm.me>

<template>

		<Button v-if="showStartCallButton"
			id="call_button"
			v-tooltip="{
				placement: 'auto',
				trigger: 'hover',
				content: startCallToolTip,
				autoHide: false,
				html: true
			}"
			:disabled="startCallButtonDisabled || loading || blockCalls"
			:type="startCallButtonType"
			@click="handleClick">
			<template #icon>

			</template>
			{{ startCallLabel }}
		</Button>
		<Button v-else-if="showLeaveCallButton && !canEndForAll"
			id="call_button"
			type="error"
			:disabled="loading"
			@click="leaveCall(false)">
			<template #icon>

			</template>
			{{ leaveCallLabel }}
		</Button>
		<Actions v-else-if="showLeaveCallButton && canEndForAll"
			:disabled="loading">
			<template slot="icon">
				<VideoOff :size="16"
					decorative />
				{{ leaveCallLabel }}
				<MenuDown :size="16"
					decorative />
			</template>
			<ActionButton @click="leaveCall(false)">
				<VideoOff slot="icon"
					:size="20"
					decorative />
				{{ leaveCallLabel }}
			</ActionButton>
			<ActionButton @click="leaveCall(true)">
				<VideoOff slot="icon"
					:size="20"
					decorative />
				{{ t('spreed', 'End meeting for all') }}
			</ActionButton>
		</Actions>

</template>


Disable the "Start call" button when talk hash is dirty

Signed-off-by: Joas Schilling <ab43a7c9cb5b2380afc4ddf8b3e2583169b39a02@schilljs.com>

<!--
  - @copyright Copyright (c) 2019 Marco Ambrosini <marcoambrosini@pm.me>
  -
  - @author Marco Ambrosini <marcoambrosini@pm.me>
  -
  - @license GNU AGPL version 3 or any later version
  -
  - This program is free software: you can redistribute it and/or modify
  - it under the terms of the GNU Affero General Public License as
  - published by the Free Software Foundation, either version 3 of the
  - License, or (at your option) any later version.
  -
  - This program is distributed in the hope that it will be useful,
  - but WITHOUT ANY WARRANTY; without even the implied warranty of
  - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  - GNU Affero General Public License for more details.
  -
  - You should have received a copy of the GNU Affero General Public License
  - along with this program. If not, see <http://www.gnu.org/licenses/>.
-->

<template>
	<button v-if="showStartCallButton"
		v-tooltip="{
			placement: 'auto',
			trigger: 'hover',
			content: startCallToolTip,
			autoHide: false,
			html: true
		}"
		:disabled="startCallButtonDisabled || loading || blockCalls"
		class="top-bar__button"
		:class="startCallButtonClasses"
		@click="joinCall">
		<span
			class="icon"
			:class="startCallIcon" />
		{{ startCallLabel }}
	</button>
	<button v-else-if="showLeaveCallButton"
		class="top-bar__button error"
		:disabled="loading"
		@click="leaveCall">
		<span
			class="icon"
			:class="leaveCallIcon" />
		{{ leaveCallLabel }}
	</button>
</template>

<script>
import { CONVERSATION, PARTICIPANT, WEBINAR } from '../../constants'
import browserCheck from '../../mixins/browserCheck'
import Tooltip from '@nextcloud/vue/dist/Directives/Tooltip'
import { emit } from '@nextcloud/event-bus'

export default {
	name: 'CallButton',

	directives: {
		Tooltip,
	},

	mixins: [browserCheck],

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

	computed: {
		token() {
			return this.$store.getters.getToken()
		},
		isNextcloudTalkHashDirty() {
			return this.$store.getters.isNextcloudTalkHashDirty
		},

		conversation() {
			if (this.$store.getters.conversation(this.token)) {
				return this.$store.getters.conversation(this.token)
			}
			return {
				participantFlags: PARTICIPANT.CALL_FLAG.DISCONNECTED,
				participantType: PARTICIPANT.TYPE.USER,
				readOnly: CONVERSATION.STATE.READ_ONLY,
				hasCall: false,
				canStartCall: false,
				lobbyState: WEBINAR.LOBBY.NONE,
			}
		},

		participant() {
			const participantIndex = this.$store.getters.getParticipantIndex(this.token, this.$store.getters.getParticipantIdentifier())
			if (participantIndex !== -1) {
				console.debug('Current participant found')
				return this.$store.getters.getParticipant(this.token, participantIndex)
			}

			console.debug('Current participant not found')
			return {
				inCall: PARTICIPANT.CALL_FLAG.DISCONNECTED,
			}
		},

		isBlockedByLobby() {
			return this.conversation.lobbyState === WEBINAR.LOBBY.NON_MODERATORS
				&& !this.isParticipantTypeModerator(this.conversation.participantType)
		},

		startCallButtonDisabled() {
			return (!this.conversation.canStartCall
					&& !this.conversation.hasCall)
				|| this.isBlockedByLobby
				|| this.isNextcloudTalkHashDirty
		},

		leaveCallLabel() {
			return t('spreed', 'Leave call')
		},

		leaveCallIcon() {
			if (this.loading) {
				return 'icon-loading-small'
			}

			return 'icon-leave-call'
		},

		startCallLabel() {
			if (this.conversation.hasCall && !this.isBlockedByLobby) {
				return t('spreed', 'Join call')
			}

			return t('spreed', 'Start call')
		},

		startCallToolTip() {
			if (this.isNextcloudTalkHashDirty) {
				return t('spreed', 'Nextcloud Talk was updated, you need to reload the page before you can start or join a call')
			}

			if (this.callButtonTooltipText) {
				return this.callButtonTooltipText
			}

			if (!this.conversation.canStartCall && !this.conversation.hasCall) {
				return t('spreed', 'You will be able to join the call only after a moderator starts it.')
			}

			return ''
		},

		startCallIcon() {
			if (this.loading) {
				return 'icon-loading-small'
			}

			if (this.conversation.hasCall && !this.isBlockedByLobby) {
				return 'icon-incoming-call'
			}

			return 'icon-start-call'
		},

		startCallButtonClasses() {
			return {
				primary: !this.conversation.hasCall && !this.isBlockedByLobby,
				success: this.conversation.hasCall && !this.isBlockedByLobby,
			}
		},

		showStartCallButton() {
			return this.conversation.readOnly === CONVERSATION.STATE.READ_WRITE
				&& this.participant.inCall === PARTICIPANT.CALL_FLAG.DISCONNECTED
		},

		showLeaveCallButton() {
			return this.conversation.readOnly === CONVERSATION.STATE.READ_WRITE
				&& this.participant.inCall !== PARTICIPANT.CALL_FLAG.DISCONNECTED
		},
	},

	methods: {
		isParticipantTypeModerator(participantType) {
			return [PARTICIPANT.TYPE.OWNER, PARTICIPANT.TYPE.MODERATOR, PARTICIPANT.TYPE.GUEST_MODERATOR].indexOf(participantType) !== -1
		},

		async joinCall() {
			console.info('Joining call')
			this.loading = true
			// Close navigation
			emit('toggle-navigation', {
				open: false,
			})
			await this.$store.dispatch('joinCall', {
				token: this.token,
				participantIdentifier: this.$store.getters.getParticipantIdentifier(),
				flags: PARTICIPANT.CALL_FLAG.IN_CALL, // FIXME add audio+video as per setting
			})
			this.loading = false
		},

		async leaveCall() {
			console.info('Leaving call')
			// Remove selected participant
			this.$store.dispatch('selectedVideoPeerId', null)
			this.loading = true
			// Open navigarion
			emit('toggle-navigation', {
				open: true,
			})
			await this.$store.dispatch('leaveCall', {
				token: this.token,
				participantIdentifier: this.$store.getters.getParticipantIdentifier(),
			})
			this.loading = false
			// Go back to promoted view upon leaving a call
			this.$store.dispatch('isGrid', false)
		},
	},
}
</script>

<style lang="scss" scoped>
.top-bar__button .icon {
	opacity: 1;

	&.icon-incoming-call {
		animation: pulse 2s infinite;
	}
}

.success {
	color: white;
	background-color: var(--color-success);
	border: 1px solid var(--color-success);

	&:hover,
	&:focus,
	&:active {
		border: 1px solid var(--color-success) !important;
	}
}

@keyframes pulse {
	0% {
		opacity: .5;
	}
	50% {
		opacity: 1;
	}
	100% {
		opacity: .5;
	}
}
</style>

<!--
  - @copyright Copyright (c) 2019 Marco Ambrosini <marcoambrosini@pm.me>
  -
  - @author Marco Ambrosini <marcoambrosini@pm.me>
  -
  - @license GNU AGPL version 3 or any later version
  -
  - This program is free software: you can redistribute it and/or modify
  - it under the terms of the GNU Affero General Public License as
  - published by the Free Software Foundation, either version 3 of the
  - License, or (at your option) any later version.
  -
  - This program is distributed in the hope that it will be useful,
  - but WITHOUT ANY WARRANTY; without even the implied warranty of
  - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  - GNU Affero General Public License for more details.
  -
  - You should have received a copy of the GNU Affero General Public License
  - along with this program. If not, see <http://www.gnu.org/licenses/>.
-->

<template>
	<button v-if="showStartCallButton"
		v-tooltip="{
			placement: 'auto',
			trigger: 'hover',
			content: startCallToolTip,
			autoHide: false,
			html: true
		}"
		:disabled="startCallButtonDisabled || loading || blockCalls"
		class="top-bar__button"
		:class="startCallButtonClasses"
		@click="joinCall">
		<span
			class="icon"
			:class="startCallIcon" />
		{{ startCallLabel }}
	</button>
	<button v-else-if="showLeaveCallButton"
		class="top-bar__button error"
		:disabled="loading"
		@click="leaveCall">
		<span
			class="icon"
			:class="leaveCallIcon" />
		{{ leaveCallLabel }}
	</button>
</template>

<script>
import { CONVERSATION, PARTICIPANT, WEBINAR } from '../../constants'
import browserCheck from '../../mixins/browserCheck'
import Tooltip from '@nextcloud/vue/dist/Directives/Tooltip'
import { emit } from '@nextcloud/event-bus'

export default {
	name: 'CallButton',

	directives: {
		Tooltip,
	},

	mixins: [browserCheck],

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

	computed: {
		token() {
			return this.$store.getters.getToken()
		},

		conversation() {
			if (this.$store.getters.conversation(this.token)) {
				return this.$store.getters.conversation(this.token)
			}
			return {
				participantFlags: PARTICIPANT.CALL_FLAG.DISCONNECTED,
				participantType: PARTICIPANT.TYPE.USER,
				readOnly: CONVERSATION.STATE.READ_ONLY,
				hasCall: false,
				canStartCall: false,
				lobbyState: WEBINAR.LOBBY.NONE,
			}
		},

		participant() {
			const participantIndex = this.$store.getters.getParticipantIndex(this.token, this.$store.getters.getParticipantIdentifier())
			if (participantIndex !== -1) {
				console.debug('Current participant found')
				return this.$store.getters.getParticipant(this.token, participantIndex)
			}

			console.debug('Current participant not found')
			return {
				inCall: PARTICIPANT.CALL_FLAG.DISCONNECTED,
			}
		},

		isBlockedByLobby() {
			return this.conversation.lobbyState === WEBINAR.LOBBY.NON_MODERATORS
				&& !this.isParticipantTypeModerator(this.conversation.participantType)
		},

		startCallButtonDisabled() {
			return (!this.conversation.canStartCall
					&& !this.conversation.hasCall)
				|| this.isBlockedByLobby
		},

		leaveCallLabel() {
			return t('spreed', 'Leave call')
		},

		leaveCallIcon() {
			if (this.loading) {
				return 'icon-loading-small'
			}

			return 'icon-leave-call'
		},

		startCallLabel() {
			if (this.conversation.hasCall && !this.isBlockedByLobby) {
				return t('spreed', 'Join call')
			}

			return t('spreed', 'Start call')
		},

		startCallToolTip() {
			if (this.callButtonTooltipText) {
				return this.callButtonTooltipText
			}

			if (!this.conversation.canStartCall && !this.conversation.hasCall) {
				return t('spreed', 'You will be able to join the call only after a moderator starts it.')
			}

			return ''
		},

		startCallIcon() {
			if (this.loading) {
				return 'icon-loading-small'
			}

			if (this.conversation.hasCall && !this.isBlockedByLobby) {
				return 'icon-incoming-call'
			}

			return 'icon-start-call'
		},

		startCallButtonClasses() {
			return {
				primary: !this.conversation.hasCall && !this.isBlockedByLobby,
				success: this.conversation.hasCall && !this.isBlockedByLobby,
			}
		},

		showStartCallButton() {
			return this.conversation.readOnly === CONVERSATION.STATE.READ_WRITE
				&& this.participant.inCall === PARTICIPANT.CALL_FLAG.DISCONNECTED
		},

		showLeaveCallButton() {
			return this.conversation.readOnly === CONVERSATION.STATE.READ_WRITE
				&& this.participant.inCall !== PARTICIPANT.CALL_FLAG.DISCONNECTED
		},
	},

	methods: {
		isParticipantTypeModerator(participantType) {
			return [PARTICIPANT.TYPE.OWNER, PARTICIPANT.TYPE.MODERATOR, PARTICIPANT.TYPE.GUEST_MODERATOR].indexOf(participantType) !== -1
		},

		async joinCall() {
			console.info('Joining call')
			this.loading = true
			// Close navigation
			emit('toggle-navigation', {
				open: false,
			})
			await this.$store.dispatch('joinCall', {
				token: this.token,
				participantIdentifier: this.$store.getters.getParticipantIdentifier(),
				flags: PARTICIPANT.CALL_FLAG.IN_CALL, // FIXME add audio+video as per setting
			})
			this.loading = false
		},

		async leaveCall() {
			console.info('Leaving call')
			// Remove selected participant
			this.$store.dispatch('selectedVideoPeerId', null)
			this.loading = true
			// Open navigarion
			emit('toggle-navigation', {
				open: true,
			})
			await this.$store.dispatch('leaveCall', {
				token: this.token,
				participantIdentifier: this.$store.getters.getParticipantIdentifier(),
			})
			this.loading = false
			// Go back to promoted view upon leaving a call
			this.$store.dispatch('isGrid', false)
		},
	},
}
</script>

<style lang="scss" scoped>
.top-bar__button .icon {
	opacity: 1;

	&.icon-incoming-call {
		animation: pulse 2s infinite;
	}
}

.success {
	color: white;
	background-color: var(--color-success);
	border: 1px solid var(--color-success);

	&:hover,
	&:focus,
	&:active {
		border: 1px solid var(--color-success) !important;
	}
}

@keyframes pulse {
	0% {
		opacity: .5;
	}
	50% {
		opacity: 1;
	}
	100% {
		opacity: .5;
	}
}
</style>


minor change to circle progress

minor change to circle progress

<template lang="jade">
.component
  .ui.container.basic.segment(:id="name")

    h2.ui.center.aligned.header.m-title.thin-only
      | {{ label }}

    .desc.ui.basic.center.aligned.segment.fat-only
      sup
        i.quote.left.icon
      | {{ desc }}
      sub
        i.quote.right.icon

    .ui.info.message(v-if="!list.length")
        i.archive.icon
        | 目前還沒有正在進行的議案…

    .ui.four.stackable.doubling.cards
      router-link.card(:to="'/topic/' + item.routeName", v-for="item in list")
        .image(:style="'background-image:url(' + item.cover + ')'")
          template(v-if="name === 'discuss'")
            .progressbar(v-if="item.status === '意見徵集'")
              | 剩
              .active-border(:data-degrees='Math.floor(item.progress / item.total * 360)', data-color='#39B4CC', data-bgcolor='gray', style='background-color:gray')
                .circle
                  span.percent
                    | {{Math.floor(item.total - item.progress)}}
              | 天
            .progressbar(v-else) 討論已結束
        .content
          h3.ui.header {{ item.title }}
        .extra.content
          p {{ item.slogan }}

</template>

<script>
  export default {
    name: 'box',
    props: ['list', 'desc', 'label', 'name'],
    data() {
      return {
        isSent: false
      }
    },
    mounted:function(){
      /* bind event scroll to window */
      window.addEventListener('scroll', this.mTitleHitEvent)
      // $(window).scroll(this.mTitleHitEvent)
      setTimeout( () => this.drawProgress(), 1000)
    },
    beforeDestroy: function(){
      window.removeEventListener('scroll', this.mTitleHitEvent)
    },
    methods:{
      mTitleHitEvent: function() {
        let btn_name = this.name
        let el = $("#"+btn_name)
        if (!el.length) /* check if m-title exist */
          return 0
        let mTop = el.offset().top /* m-title's position */
        let mHeight = el.height() /* m-title's height */
        let wScroll = $(window).scrollTop() /* length window has scrolled */
        let isSent = this.isSent
        /* minus 1 to be safer */
        let isScollingOn = (wScroll > (mTop-1)) && (wScroll < (mTop+mHeight))
        if ( isScollingOn && !isSent ){ /* do not double sent */
          this.$emit('stickTo', btn_name) /* sent event trigger to parent comp */
          this.isSent = true
        }
        else if ( !isScollingOn ) {
          this.isSent = false
        }
      },
      drawProgress: function () {
        $(".active-border").map( function () {
          let degrees = $(this).data("degrees");
          let color = $(this).data("color");
          let bgcolor = $(this).data("bgcolor");
          if (degrees <= 180) {
            $(this).css('background-image','linear-gradient(' + (90+degrees) + 'deg, transparent 50%, ' + color + ' 50%),linear-gradient(90deg, ' + color + ' 50%, transparent 50%)');
          }
          else {
            $(this).css('background-image','linear-gradient(' + (degrees-90) + 'deg, transparent 50%, ' + bgcolor + ' 50%),linear-gradient(90deg, ' + color + ' 50%, transparent 50%)');
          }
        })
      }
    }
  }
</script>

<style lang="scss" scoped>
@import "../sass/global.scss";

.desc {
  width: 100%;
  margin-top: -1em;
  font-size: 120%;
}
.info.message {
  text-align: center;
}
.cards .card {
    margin: 0 0 1em 0;
    overflow: hidden;
    .content h3.ui.header {
      font-family: $main-font;
      font-size: 150%;
    }
    .image{
      position: relative;
      height: 12em;
      background-size: cover;
      background-position: center;
      .progressbar {
        color: white;
        position: absolute;
        display: flex;
        align-items: center;
        justify-content: center;
        width: 100%;
        height: 100%;
        z-index: 100;
        /* below is for circular progbar */
        .active-border{
          font-size: 2rem;
          margin: 0 1ch;
          position: relative;
          text-align: center;
          width: calc( 2em + 10px );
          height: calc( 2em + 10px );
          border-radius: 50%;
          // background-color: #39B4CC;
          // background-image:
          //   linear-gradient(91deg, transparent 50%, #A2ECFB 50%),
          //   linear-gradient(90deg, #A2ECFB 50%, transparent 50%);
          .circle {
            position: relative;
            top: 5px;
            left: 5px;
            width: 2em;
            height: 2em;
            border-radius: 50%;
            // background-color: black;
            .percent{
              position: relative;
              line-height: 1;
              top: .4em;
            }
          }
        }
      }
    }
}

/********************************* mobile view */

.m-title {
  // text-align: left;
  background-color: #E6E6E6;
  // margin-right: -1rem;
  margin: 0 0 1em 0;
  padding: .5em;
}

</style>

<template lang="jade">
.component
  .ui.container.basic.segment(:id="name")

    h2.ui.center.aligned.header.m-title.thin-only
      | {{ label }}

    .desc.ui.basic.center.aligned.segment.fat-only
      sup
        i.quote.left.icon
      | {{ desc }}
      sub
        i.quote.right.icon

    .ui.info.message(v-if="!list.length")
        i.archive.icon
        | 目前還沒有正在進行的議案…

    .ui.four.stackable.doubling.cards
      router-link.card(:to="'/topic/' + item.routeName", v-for="item in list")
        .image(:style="'background-image:url(' + item.cover + ')'")
          template(v-if="name === 'discuss'")
            .progressbar(v-if="item.status === '意見徵集'")
              | 剩
              .active-border(:data-degrees='Math.floor(item.progress / item.total * 360)')
                .circle
                  span.percent
                    | {{Math.floor(item.total - item.progress)}}
              | 天
            .progressbar(v-else) 討論已結束
        .content
          h3.ui.header {{ item.title }}
        .extra.content
          p {{ item.slogan }}

</template>

<script>
  export default {
    name: 'box',
    props: ['list', 'desc', 'label', 'name'],
    data() {
      return {
        isSent: false
      }
    },
    mounted:function(){
      /* bind event scroll to window */
      window.addEventListener('scroll', this.mTitleHitEvent)
      // $(window).scroll(this.mTitleHitEvent)
      setTimeout( () => this.drawProgress(), 1000)
    },
    beforeDestroy: function(){
      window.removeEventListener('scroll', this.mTitleHitEvent)
    },
    methods:{
      mTitleHitEvent: function() {
        let btn_name = this.name
        let el = $("#"+btn_name)
        if (!el.length) /* check if m-title exist */
          return 0
        let mTop = el.offset().top /* m-title's position */
        let mHeight = el.height() /* m-title's height */
        let wScroll = $(window).scrollTop() /* length window has scrolled */
        let isSent = this.isSent
        /* minus 1 to be safer */
        let isScollingOn = (wScroll > (mTop-1)) && (wScroll < (mTop+mHeight))
        if ( isScollingOn && !isSent ){ /* do not double sent */
          this.$emit('stickTo', btn_name) /* sent event trigger to parent comp */
          this.isSent = true
        }
        else if ( !isScollingOn ) {
          this.isSent = false
        }
      },
      drawProgress: function () {
        $(".active-border").map( function () {
          let degrees = $(this).data("degrees");
          if (degrees <= 180) {
            $(this).css('background-image','linear-gradient(' + (90+degrees) + 'deg, transparent 50%, #A2ECFB 50%),linear-gradient(90deg, #A2ECFB 50%, transparent 50%)');
          }
          else {
            $(this).css('background-image','linear-gradient(' + (degrees-90) + 'deg, transparent 50%, #39B4CC 50%),linear-gradient(90deg, #A2ECFB 50%, transparent 50%)');
          }
        })
      }
    }
  }
</script>

<style lang="scss" scoped>
@import "../sass/global.scss";

.desc {
  width: 100%;
  margin-top: -1em;
  font-size: 120%;
}
.info.message {
  text-align: center;
}
.cards .card {
    margin: 0 0 1em 0;
    overflow: hidden;
    .content h3.ui.header {
      font-family: $main-font;
      font-size: 150%;
    }
    .image{
      position: relative;
      height: 12em;
      background-size: cover;
      background-position: center;
      .progressbar {
        color: white;
        position: absolute;
        display: flex;
        align-items: center;
        justify-content: center;
        width: 100%;
        height: 100%;
        z-index: 100;
        /* below is for circular progbar */
        .active-border{
          font-size: 2rem;
          margin: 0 1ch;
          position: relative;
          text-align: center;
          width: calc( 2em + 10px );
          height: calc( 2em + 10px );
          border-radius: 50%;
          background-color: #39B4CC;
          // background-image:
          //   linear-gradient(91deg, transparent 50%, #A2ECFB 50%),
          //   linear-gradient(90deg, #A2ECFB 50%, transparent 50%);
          .circle {
            position: relative;
            top: 5px;
            left: 5px;
            width: 2em;
            height: 2em;
            border-radius: 50%;
            background-color: black;
            .percent{
              position: relative;
              top: .4em;
            }
          }
        }
      }
    }
}

/********************************* mobile view */

.m-title {
  // text-align: left;
  background-color: #E6E6E6;
  // margin-right: -1rem;
  margin: 0 0 1em 0;
  padding: .5em;
}

</style>


Also set currentTab if it becomes invalid

<template>
  <AposModal
    class="apos-doc-editor" :modal="modal"
    :modal-title="modalTitle"
    @inactive="modal.active = false" @show-modal="modal.showModal = true"
    @esc="confirmAndCancel" @no-modal="$emit('safe-close')"
  >
    <template #secondaryControls>
      <AposButton
        type="default" label="Cancel"
        @click="confirmAndCancel"
      />
    </template>
    <template #primaryControls>
      <AposButton
        type="primary" label="Save"
        :disabled="docOtherFields.hasErrors || docUtilityFields.hasErrors"
        @click="submit"
      />
    </template>
    <template #leftRail>
      <AposModalRail>
        <AposModalTabs
          v-if="tabs.length > 0"
          :current="currentTab" :tabs="tabs"
          @select-tab="switchPane"
        />
      </AposModalRail>
    </template>
    <template #main>
      <AposModalBody>
        <template #bodyMain>
          <AposModalTabsBody>

              <AposSchema
                v-if="docReady"
                :schema="schemaOtherFields"
                :current-fields="currentFields"
                :trigger-validation="triggerValidation"
                :utility-rail="false"
                :following-values="followingValues('other')"
                :doc-id="docId"
                :value="docOtherFields"
                @input="updateDocOtherFields"
                :server-errors="serverErrors"
                ref="otherSchema"
              />

          </AposModalTabsBody>
        </template>
      </AposModalBody>
    </template>
    <template #rightRail>
      <AposModalRail type="right">

          <AposSchema
            v-if="docReady"
            :schema="schemaUtilityFields"
            :current-fields="utilityFields"
            :trigger-validation="triggerValidation"
            :utility-rail="true"
            :following-values="followingValues('utility')"
            :doc-id="docId"
            :value="docUtilityFields"
            @input="updateDocUtilityFields"
            :modifiers="['small', 'inverted']"
            ref="utilitySchema"
            :server-errors="serverErrors"
          />

      </AposModalRail>
    </template>
  </AposModal>
</template>


the logic to select the first choice when required was failing to actually emit that choice as a change, now it is compatible with conditional fields

<template>
  <AposInputWrapper
    :field="field"
    :error="effectiveError"
    :uid="uid"
    :modifiers="modifiers"
    :display-options="displayOptions"
  >
    <template #body>
      <div class="apos-input-wrapper">
        <select
          class="apos-input apos-input--select" :id="uid"
          @change="change($event.target.value)"
        >
          <option
            v-for="choice in choices" :key="JSON.stringify(choice.value)"
            :value="JSON.stringify(choice.value)"
            :selected="choice.value === value.data"
          >
            {{ choice.label }}
          </option>
        </select>
        <AposIndicator
          icon="menu-down-icon"
          class="apos-input-icon"
          :icon-size="20"
        />
      </div>
    </template>
  </AposInputWrapper>
</template>

<script>
import AposInputMixin from 'Modules/@apostrophecms/schema/mixins/AposInputMixin';

export default {
  name: 'AposInputSelect',
  mixins: [ AposInputMixin ],
  props: {
    icon: {
      type: String,
      default: 'menu-down-icon'
    }
  },
  data() {
    return {
      next: (this.value.data == null) ? null : this.value.data,
      choices: []
    };
  },
  mounted() {
    // Add an null option if there isn't one already
    if (!this.field.required && !this.field.choices.find(choice => {
      return choice.value === null;
    })) {
      this.choices.push({
        label: '',
        value: null
      });
    }
    this.choices = this.choices.concat(this.field.choices);
    this.$nextTick(() => {
      // this has to happen on nextTick to avoid emitting before schemaReady is
      // set in AposSchema
      if (this.field.required && (this.next == null) && (this.field.choices[0] != null)) {
        this.next = this.field.choices[0].value;
      }
    });
  },
  methods: {
    validate(value) {
      if (this.field.required && !value.length) {
        return 'required';
      }

      if (value && !this.field.choices.find(choice => choice.value === value)) {
        return 'invalid';
      }

      return false;
    },
    change(value) {
      // Allows expression of non-string values
      this.next = this.choices.find(choice => choice.value === JSON.parse(value)).value;
    }
  }
};
</script>

<style lang="scss" scoped>
.apos-input-icon {
  @include apos-transition();
}
</style>

<template>
  <AposInputWrapper
    :field="field"
    :error="effectiveError"
    :uid="uid"
    :modifiers="modifiers"
    :display-options="displayOptions"
  >
    <template #body>
      <div class="apos-input-wrapper">
        <select
          class="apos-input apos-input--select" :id="uid"
          @change="change($event.target.value)"
        >
          <option
            v-for="choice in choices" :key="JSON.stringify(choice.value)"
            :value="JSON.stringify(choice.value)"
            :selected="choice.value === value.data"
          >
            {{ choice.label }}
          </option>
        </select>
        <AposIndicator
          icon="menu-down-icon"
          class="apos-input-icon"
          :icon-size="20"
        />
      </div>
    </template>
  </AposInputWrapper>
</template>

<script>
import AposInputMixin from 'Modules/@apostrophecms/schema/mixins/AposInputMixin';

export default {
  name: 'AposInputSelect',
  mixins: [ AposInputMixin ],
  props: {
    icon: {
      type: String,
      default: 'menu-down-icon'
    }
  },
  data() {
    return {
      next: (this.value && this.value.data != null)
        ? this.value.data : (this.field.required ? (this.field.choices[0] && this.field.choices[0].value) : ''),
      choices: []
    };
  },
  mounted() {
    // Add an null option if there isn't one already
    if (!this.field.required && !this.field.choices.find(choice => {
      return choice.value === null;
    })) {
      this.choices.push({
        label: '',
        value: null
      });
    }
    this.choices = this.choices.concat(this.field.choices);
  },
  methods: {
    validate(value) {
      if (this.field.required && !value.length) {
        return 'required';
      }

      if (value && !this.field.choices.find(choice => choice.value === value)) {
        return 'invalid';
      }

      return false;
    },
    change(value) {
      // Allows expression of non-string values
      this.next = this.choices.find(choice => choice.value === JSON.parse(value)).value;
    }
  }
};
</script>

<style lang="scss" scoped>
.apos-input-icon {
  @include apos-transition();
}
</style>


Fixed regression with not resolving proxy settings.

The SettingsComponent was still trying to resolve proxySettings. These have been moved into requestSettings.


Make sure the "max_downloads" setting has the correct type when saving.

<template>

                Settings
                <button class="mod-btn btn-green pull-right" @click="save">Save</button>

Click the arrows to reveal each group's options. Click SAVE when done.

            <kn-drawer label="Knossos">

Basic Knossos settings for downloads, errors, and data

                    Data Path:

                        {{ knossos.base_path }}
                        <button @click.prevent="changeBasePath">Browse</button>

                    Max Downloads:

                        <input type="number" style="width: 50px" v-model="knossos.max_downloads">

                    Send Error Reports:

                        <input type="checkbox" v-model="knossos.use_raven">

            </kn-drawer>

            <kn-drawer label="Video">

Set your default video settings and resolution

                    Resolution:

                        <select v-model="fso.active_mode">
                            <option v-for="res in fso.modes">{{ res }}</option>
                        </select>

                    Bit Depth:

                        <select v-model="fso.depth">
                            <option value="16">16-bit</option>
                            <option value="32">32-bit</option>
                        </select>

                    Texture Filter:

                        <select v-model="fso.texfilter">
                            <option value="0">Bilinear</option>
                            <option value="1">Trilinear</option>
                        </select>

            </kn-drawer>

            <kn-drawer label="Audio">

Set your default playback and capture devices

                    Playback Device:

                        <select v-model="fso.active_audio_dev">
                            <option v-for="dev in fso.audio_devs">{{ dev }}</option>
                        </select>

                    Capture Device:

                        <select v-model="fso.active_cap_dev">
                            <option v-for="dev in fso.cap_devs">{{ dev }}</option>
                        </select>

                    Enable EFX:

<input type="checkbox" v-model="fso.enable_efx">

                    Sample Rate:

                        <input type="number" v-model="fso.sample_rate">

            </kn-drawer>

            <kn-drawer label="Nebula">

Login and manage your Nebula credentials

                    Username:

                        <input type="text" class="neb-input" v-model="neb_user">

                    Password:

                        <input type="password" class="neb-input" v-model="neb_password">

                    E-Mail:

                        <input type="email" class="neb-input" v-model="neb_email" placeholder="only required for registration">

                        <button class="mod-btn btn-link-blue" @click="login">Login</button>
                        <button class="mod-btn btn-link-blue" @click="register">Register</button>
                        <button class="mod-btn btn-link-red" @click="resetPassword">Reset Pass</button>

            </kn-drawer>

            <kn-drawer label="Speech" v-if="fso.has_voice">

Manage settings related to Text-To-Speech

                    Voice:

                        <select disabled></select>

                    Volume:

                        <input type="range" min="0" max="100" style="width: calc(100% - 80px); display: inline-block;" disabled>
                        <button disabled>Test</button>

                    Use Speech In:

                        Tech Room:
                        <input type="checkbox" disabled>

                        In-Game:
                        <input type="checkbox" disabled>

                        Briefings:
                        <input type="checkbox" disabled>

                        Multiplayer:
                        <input type="checkbox" disabled>

            </kn-drawer>

            <kn-drawer label="Joystick">

Setup and calibrate your joystick

                    Joystick:

                        <select v-model="fso.joystick_id">
                            <option>No Joystick</option>
                            <option v-for="(joy, i) in fso.joysticks" :value="i">{{ joy }}</option>
                        </select>

                        <button disabled>Detect</button>
                        <button disabled>Calibrate</button>

                        Force Feedback:
                        <input type="checkbox" disabled>

                        Directional Hit:
                        <input type="checkbox" disabled>

            </kn-drawer>

            <kn-drawer label="Network">

Manage your network settings for multiplayer

                    Connection Type:

                        <select>
                            <option>None</option>
                            <option>Dialup</option>
                            <option>Broadband/LAN</option>
                        </select>

                    Force Local Port:

                        <input type="number" disabled>

                    Connection Speed:

                        <select disabled>
                            <option>None</option>
                            <option>28k modem</option>
                            <option>56k modem</option>
                            <option>ISDN</option>
                            <option>DSL</option>
                            <option>Cable/LAN</option>
                        </select>

                    Force IP Address:

                        <input type="text" disabled>

            </kn-drawer>

</template>


padding

padding


Slider: triggers validator when value changed, fixed #3968

<template>

    <el-input-number
      v-model="firstValue"
      v-if="showInput && !range"
      class="el-slider__input"
      ref="input"
      :step="step"
      :disabled="disabled"
      :controls="showInputControls"
      :min="min"
      :max="max"
      size="small">
    </el-input-number>

      <slider-button
        v-model="firstValue"
        ref="button1">
      </slider-button>
      <slider-button
        v-model="secondValue"
        ref="button2"
        v-if="range">
      </slider-button>

</template>


feat: use the theme colours for the wallet filters instead of white (#1270)

<template>
  <div
    v-click-outside.capture="emitClose"
    :class="isSidebarExpanded ? 'WalletSidebarFilters--expanded' : 'WalletSidebarFilters--collapsed'"
    class="WalletSidebarFilters absolute z-20 rounded-lg"
  >
    <div
      class="bg-theme-settings p-10 rounded-lg shadow font-bold"
    >
      <WalletSidebarFiltersSearchInput
        v-model="filters.searchQuery"
        :placeholder="hasContacts
          ? $t('WALLET_SIDEBAR.SEARCH.PLACEHOLDER_CONTACTS')
          : $t('WALLET_SIDEBAR.SEARCH.PLACEHOLDER_WALLETS')
        "
        @input="setSearchQuery"
      />
    </div>

    <MenuOptions class="WalletSidebarFilters__sorting mt-2 rounded-lg shadow font-bold">
      <div class="mx-10 py-5 mb-2 text-theme-settings-heading select-none">
        {{ $t('WALLET_SIDEBAR.SORT.BY') }}
      </div>
      <MenuOptionsItem
        v-for="option in $options.sortOptions"
        :key="option"
        :title="`${$t('WALLET_SIDEBAR.SORT.' + option.toUpperCase().replace('-', '_'))}`"
        :class="stringifiedSortOrder === option ? 'WalletSidebarFilters__sorting__order--selected' : ''"
        @click="setSort(option)"
      />
    </MenuOptions>

    <MenuOptions class="WalletSidebarFilters__settings mt-2 rounded-lg shadow font-medium">
      <MenuOptionsItem
        :title="hasContacts
          ? $t('WALLET_SIDEBAR.FILTERS.HIDE_EMPTY_CONTACTS')
          : $t('WALLET_SIDEBAR.FILTERS.HIDE_EMPTY_WALLETS')
        "
        @click="toggleSelect('hide-empty')"
      >
        <div
          slot="controls"
          class="pointer-events-none"
        >
          <ButtonSwitch
            ref="hide-empty"
            :is-active="currentFilters.hideEmpty"
            background-color="var(--theme-settings-switch)"
            @change="setHideEmpty"
          />
        </div>
      </MenuOptionsItem>
      <MenuOptionsItem
        v-if="hasLedger && !hasContacts"
        :title="$t('WALLET_SIDEBAR.FILTERS.HIDE_LEDGER')"
        @click="toggleSelect('hide-ledger')"
      >
        <div
          slot="controls"
          class="pointer-events-none"
        >
          <ButtonSwitch
            ref="hide-ledger"
            :is-active="currentFilters.hideLedger"
            background-color="var(--theme-settings-switch)"
            @change="setHideLedger"
          />
        </div>
      </MenuOptionsItem>
    </MenuOptions>
  </div>
</template>

<script>
import { ButtonSwitch } from '@/components/Button'
import { MenuOptions, MenuOptionsItem } from '@/components/Menu'
import WalletSidebarFiltersSearchInput from './WalletSidebarFiltersSearchInput'

export default {
  name: 'WalletSidebarFilters',

  components: {
    ButtonSwitch,
    MenuOptions,
    MenuOptionsItem,
    WalletSidebarFiltersSearchInput
  },

  sortOptions: [
    'name-asc',
    'name-desc',
    'balance-asc',
    'balance-desc'
  ],

  props: {
    hasContacts: {
      type: Boolean,
      required: false,
      default: false
    },
    // Show the Ledger options or not
    hasLedger: {
      type: Boolean,
      required: false,
      default: false
    },
    isSidebarExpanded: {
      type: Boolean,
      required: false,
      default: false
    },
    outsideClick: {
      type: Boolean,
      required: false,
      default: false
    },
    filters: {
      type: Object,
      required: true
    },
    searchQuery: {
      type: String,
      required: false,
      default: ''
    },
    sortOrder: {
      type: Object,
      required: false,
      default: () => ({ field: 'name', type: 'asc' })
    }
  },

  data () {
    return {
      currentSearchQuery: this.searchQuery,
      currentFilters: this.filters,
      currentSortOrder: this.sortOrder
    }
  },

  computed: {
    stringifiedSortOrder () {
      return Object.values(this.currentSortOrder).join('-')
    }
  },

  watch: {
    filters (filters) {
      this.currentFilters = filters
    },

    searchQuery (query) {
      this.currentSearchQuery = query
    }
  },

  methods: {
    setSearchQuery (query) {
      this.currentSearchQuery = query
      this.emitSearch()
    },

    setHideEmpty (isHidden) {
      this.filters.hideEmpty = isHidden
      this.emitFilter()
    },

    setHideLedger (isHidden) {
      this.filters.hideEmpty = isHidden
      this.emitFilter()
    },

    setFilter (filter, value) {
      this.currentFilters[filter] = value
      this.emitFilter()
    },

    setSort (value) {
      const [field, type] = value.split('-')
      this.currentSortOrder = { field, type }
      this.emitSort()
    },

    toggleSelect (name) {
      this.$refs[name].toggle()
    },

    emitFilter () {
      this.$emit('filter', this.currentFilters)
    },

    emitSearch () {
      this.$emit('search', this.currentSearchQuery)
    },

    emitSort () {
      this.$emit('sort', this.currentSortOrder)
    },

    emitClose (context) {
      this.$emit('close', context)
    }
  }
}
</script>

<style lang="postcss" scoped>
.WalletSidebarFilters {
  position: fixed;
  width: 380px;
  /* The expanded sidebar uses `.w-1/7` */
  right: calc(12.5% - 0.5rem);
  top: 0.75rem;
  transition: right 0.4s;
}

.WalletSidebarFilters--expanded {
  /* The expanded sidebar uses `.w-1/3` */
  right: calc(33.33333% - 2.5rem);
}

.WalletSidebarFilters__sorting .MenuOptionsItem {
  transition: color 0.2s;
}
.WalletSidebarFilters__sorting__order--selected {
  color: var(--theme-settings-heading) !important;
}

.WalletSidebarFilters .MenuOptions {
  @apply .rounded-lg
}
/* To not render the bubble arrow */
.WalletSidebarFilters .MenuOptions:after {
  border-width: 0px
}
</style>

<template>
  <div
    v-click-outside.capture="emitClose"
    :class="isSidebarExpanded ? 'WalletSidebarFilters--expanded' : 'WalletSidebarFilters--collapsed'"
    class="WalletSidebarFilters absolute z-20 rounded-lg theme-light"
  >
    <div
      class="bg-theme-feature p-10 rounded-lg shadow font-bold"
    >
      <WalletSidebarFiltersSearchInput
        v-model="filters.searchQuery"
        :placeholder="hasContacts
          ? $t('WALLET_SIDEBAR.SEARCH.PLACEHOLDER_CONTACTS')
          : $t('WALLET_SIDEBAR.SEARCH.PLACEHOLDER_WALLETS')
        "
        @input="setSearchQuery"
      />
    </div>

    <MenuOptions class="WalletSidebarFilters__sorting mt-2 theme-light rounded-lg shadow font-bold">
      <div class="mx-10 py-5 mb-2 text-theme-settings-heading select-none">
        {{ $t('WALLET_SIDEBAR.SORT.BY') }}
      </div>
      <MenuOptionsItem
        v-for="option in $options.sortOptions"
        :key="option"
        :title="`${$t('WALLET_SIDEBAR.SORT.' + option.toUpperCase().replace('-', '_'))}`"
        :class="stringifiedSortOrder === option ? 'WalletSidebarFilters__sorting__order--selected' : ''"
        @click="setSort(option)"
      />
    </MenuOptions>

    <MenuOptions class="WalletSidebarFilters__settings mt-2 rounded-lg shadow font-medium">
      <MenuOptionsItem
        :title="hasContacts
          ? $t('WALLET_SIDEBAR.FILTERS.HIDE_EMPTY_CONTACTS')
          : $t('WALLET_SIDEBAR.FILTERS.HIDE_EMPTY_WALLETS')
        "
        @click="toggleSelect('hide-empty')"
      >
        <div
          slot="controls"
          class="pointer-events-none"
        >
          <ButtonSwitch
            ref="hide-empty"
            :is-active="currentFilters.hideEmpty"
            class="theme-light"
            background-color="var(--theme-settings-switch)"
            @change="setHideEmpty"
          />
        </div>
      </MenuOptionsItem>
      <MenuOptionsItem
        v-if="hasLedger && !hasContacts"
        :title="$t('WALLET_SIDEBAR.FILTERS.HIDE_LEDGER')"
        @click="toggleSelect('hide-ledger')"
      >
        <div
          slot="controls"
          class="pointer-events-none"
        >
          <ButtonSwitch
            ref="hide-ledger"
            :is-active="currentFilters.hideLedger"
            class="theme-light"
            background-color="var(--theme-settings-switch)"
            @change="setHideLedger"
          />
        </div>
      </MenuOptionsItem>
    </MenuOptions>
  </div>
</template>

<script>
import { ButtonSwitch } from '@/components/Button'
import { MenuOptions, MenuOptionsItem } from '@/components/Menu'
import WalletSidebarFiltersSearchInput from './WalletSidebarFiltersSearchInput'

export default {
  name: 'WalletSidebarFilters',

  components: {
    ButtonSwitch,
    MenuOptions,
    MenuOptionsItem,
    WalletSidebarFiltersSearchInput
  },

  sortOptions: [
    'name-asc',
    'name-desc',
    'balance-asc',
    'balance-desc'
  ],

  props: {
    hasContacts: {
      type: Boolean,
      required: false,
      default: false
    },
    // Show the Ledger options or not
    hasLedger: {
      type: Boolean,
      required: false,
      default: false
    },
    isSidebarExpanded: {
      type: Boolean,
      required: false,
      default: false
    },
    outsideClick: {
      type: Boolean,
      required: false,
      default: false
    },
    filters: {
      type: Object,
      required: true
    },
    searchQuery: {
      type: String,
      required: false,
      default: ''
    },
    sortOrder: {
      type: Object,
      required: false,
      default: () => ({ field: 'name', type: 'asc' })
    }
  },

  data () {
    return {
      currentSearchQuery: this.searchQuery,
      currentFilters: this.filters,
      currentSortOrder: this.sortOrder
    }
  },

  computed: {
    stringifiedSortOrder () {
      return Object.values(this.currentSortOrder).join('-')
    }
  },

  watch: {
    filters (filters) {
      this.currentFilters = filters
    },

    searchQuery (query) {
      this.currentSearchQuery = query
    }
  },

  methods: {
    setSearchQuery (query) {
      this.currentSearchQuery = query
      this.emitSearch()
    },

    setHideEmpty (isHidden) {
      this.filters.hideEmpty = isHidden
      this.emitFilter()
    },

    setHideLedger (isHidden) {
      this.filters.hideEmpty = isHidden
      this.emitFilter()
    },

    setFilter (filter, value) {
      this.currentFilters[filter] = value
      this.emitFilter()
    },

    setSort (value) {
      const [field, type] = value.split('-')
      this.currentSortOrder = { field, type }
      this.emitSort()
    },

    toggleSelect (name) {
      this.$refs[name].toggle()
    },

    emitFilter () {
      this.$emit('filter', this.currentFilters)
    },

    emitSearch () {
      this.$emit('search', this.currentSearchQuery)
    },

    emitSort () {
      this.$emit('sort', this.currentSortOrder)
    },

    emitClose (context) {
      this.$emit('close', context)
    }
  }
}
</script>

<style lang="postcss" scoped>
.WalletSidebarFilters {
  position: fixed;
  width: 380px;
  /* The expanded sidebar uses `.w-1/7` */
  right: calc(12.5% - 0.5rem);
  top: 0.75rem;
  transition: right 0.4s;
}

.WalletSidebarFilters--expanded {
  /* The expanded sidebar uses `.w-1/3` */
  right: calc(33.33333% - 2.5rem);
}

.WalletSidebarFilters__sorting .MenuOptionsItem {
  transition: color 0.2s;
}
.WalletSidebarFilters__sorting__order--selected {
  color: var(--theme-settings-heading) !important;
}

.WalletSidebarFilters .MenuOptions {
  @apply .rounded-lg
}
/* To not render the bubble arrow */
.WalletSidebarFilters .MenuOptions:after {
  border-width: 0px
}
</style>


ScreenCard: remove using ID, not relative list-index

<template>

            <img :src="'/' + screen.photo.thumb_path"
                class="screencard_img {{(screen.screengroups.length<1 || screen.category==1)?'inactive':''}}">

                <template v-if="from=='screen' || from=='screengroup'">

                        <template v-if="from=='screengroup'">

                        </template>
                        <template v-else>

                        </template>

                </template>
                <template v-else>

                </template>

</template>


Add transition to image name

Add transition to image name

<template lang="pug">
.annotation-tool-container.container-fluid
  .isic-page.row
    .isic-pane.col-sm-3
      .header
        .info-group
          span.title ISIC Annotation Tool
          a.pull-right(href='/') Return home.
        .info-group #[b Image:]
          transition(name='image-name')
            span(v-if='image') {{ image.name }}
        .info-group(v-if='user')
          span #[b User:] {{ user.name() }}
        .info-group.dropdown
          button.btn.btn-warning.dropdown-toggle(
            type='button',
            data-toggle='dropdown'
          )
            span Flag Image
            span.caret
          ul.dropdown-menu
            li
              a(@click='setFlagStatus("ok")') Unflagged
            li
              a(@click='setFlagStatus("phi")') Contains PHI
            li
              a(@click='setFlagStatus("quality")') Low optical quality
            li
              a(@click='setFlagStatus("zoom")') Inadequate zoom factor
            li
              a(@click='setFlagStatus("inappropriate")') Clinically inappropriate
            li.divider
            li
              a(@click='flag("other")') Other reason
          span.flag-status
            span #[b Flag Status:] {{ flagStatus }}
      .annotation(v-if='questions.length > 0')
        h3.annotation-header Questions
        annotation-tool-questions
      .annotation(v-if='features.length > 0')
        h3.annotation-header Features
        .annotation-section
          annotation-tool-features(
            :activeFeatureId='activeFeatureId',
            @featureActivated='onFeatureActivated',
            @featureDeactivated='onFeatureDeactivated',
            @displayFeature='onDisplayFeature',
            @deleteFeature='onDeleteFeature'
          )
      .annotation-section
        .btn-group
          template(v-if='showReview')
            button.btn.btn-info(
              type='button',
              @click='setShowReview(false)'
            ) Continue editing
          template(v-else)
            button.btn.btn-info(
              type='button',
              @click='reset'
            ) Reset current
        .btn-group.pull-right
          template(v-if='showReview')
            button.btn.btn-success(
              type='button',
              @click='submitAnnotation'
            ) Submit
          template(v-else)
            button.btn.btn-success(
              type='button',
              @click='setShowReview(true)'
            ) Review
    .isic-map.col-sm-9
      annotation-tool-viewer.isic-map(
        ref='viewer',
        :image='image'
      )
</template>

<template lang="pug">
.annotation-tool-container.container-fluid
  .isic-page.row
    .isic-pane.col-sm-3
      .header
        .info-group
          span.title ISIC Annotation Tool
          a.pull-right(href='/') Return home.
        .info-group(v-if='image')
            span #[b Image:] {{ image.name }}
        .info-group(v-if='user')
          span #[b User:] {{ user.name() }}
        .info-group.dropdown
          button.btn.btn-warning.dropdown-toggle(
            type='button',
            data-toggle='dropdown'
          )
            span Flag Image
            span.caret
          ul.dropdown-menu
            li
              a(@click='setFlagStatus("ok")') Unflagged
            li
              a(@click='setFlagStatus("phi")') Contains PHI
            li
              a(@click='setFlagStatus("quality")') Low optical quality
            li
              a(@click='setFlagStatus("zoom")') Inadequate zoom factor
            li
              a(@click='setFlagStatus("inappropriate")') Clinically inappropriate
            li.divider
            li
              a(@click='flag("other")') Other reason
          span.flag-status
            span #[b Flag Status:] {{ flagStatus }}
      .annotation(v-if='questions.length > 0')
        h3.annotation-header Questions
        annotation-tool-questions
      .annotation(v-if='features.length > 0')
        h3.annotation-header Features
        .annotation-section
          annotation-tool-features(
            :activeFeatureId='activeFeatureId',
            @featureActivated='onFeatureActivated',
            @featureDeactivated='onFeatureDeactivated',
            @displayFeature='onDisplayFeature',
            @deleteFeature='onDeleteFeature'
          )
      .annotation-section
        .btn-group
          template(v-if='showReview')
            button.btn.btn-info(
              type='button',
              @click='setShowReview(false)'
            ) Continue editing
          template(v-else)
            button.btn.btn-info(
              type='button',
              @click='reset'
            ) Reset current
        .btn-group.pull-right
          template(v-if='showReview')
            button.btn.btn-success(
              type='button',
              @click='submitAnnotation'
            ) Submit
          template(v-else)
            button.btn.btn-success(
              type='button',
              @click='setShowReview(true)'
            ) Review
    .isic-map.col-sm-9
      annotation-tool-viewer.isic-map(
        ref='viewer',
        :image='image'
      )
</template>


Global filters continue loading until page changed

<template>
    <div class="SharpGlobalFilters">
        <template v-for="filter in filters">
            <SharpFilterSelect
                :name="filter.label"
                :values="filter.values"
                :value="filterValue(filter.key)"
                :multiple="filter.multiple"
                :required="filter.required"
                :template="filter.template"
                :search-keys="filter.searchKeys"
                :searchable="filter.searchable"
                :key="filter.key"
                @input="handleFilterChanged(filter, $event)"
                @open="handleOpened(filter)"
                @close="handleClosed(filter)"
            />
        </template>
    </div>
</template>

<script>
    import { mapGetters } from 'vuex';
    import SharpFilterSelect from '../list/FilterSelect.vue';
    import { BASE_URL } from "../../consts";
    import debounce from 'lodash/debounce';
    import Vue from 'vue';

    export default {
        inject: {
            mainLoading: {
                default: new Vue()
            },
        },
        components: {
            SharpFilterSelect
        },
        computed: {
            ...mapGetters('global-filters', {
                filters: 'filters/filters',
                filterValue: 'filters/value',
            }),
        },
        methods: {
            handleFilterChanged(filter, value) {
                this.$store.dispatch('global-filters/post', { filter, value })
                    .then(() => {
                        this.mainLoading.$emit('show');
                        location.href = BASE_URL;
                    });
            },
            handleOpened() {
                this.$emit('open');
            },
            handleClosed() {
                this.$emit('close');
            },
            updateLayout: debounce(function () {
                [...this.$el.querySelectorAll('.SharpFilterSelect')].forEach(select => {
                    const container = this.$el.parentElement;
                    const height = (container.offsetHeight-select.offsetHeight) - (select.offsetTop-container.offsetTop);
                    const dropdown = select.querySelector('.SharpAutocomplete .multiselect__content');
                    dropdown.style.height = `${height}px`;
                });
            }, 300)
        },
        mounted() {
            this.updateLayout();
            window.addEventListener('resize', this.updateLayout);
        },
        destroyed() {
            window.removeEventListener('resize', this.updateLayout);
        }
    }
</script>

<template>
    <div class="SharpGlobalFilters">
        <template v-for="filter in filters">
            <SharpFilterSelect
                :name="filter.label"
                :values="filter.values"
                :value="filterValue(filter.key)"
                :multiple="filter.multiple"
                :required="filter.required"
                :template="filter.template"
                :search-keys="filter.searchKeys"
                :searchable="filter.searchable"
                :key="filter.key"
                @input="handleFilterChanged(filter, $event)"
                @open="handleOpened(filter)"
                @close="handleClosed(filter)"
            />
        </template>
    </div>
</template>

<script>
    import { mapGetters } from 'vuex';
    import SharpFilterSelect from '../list/FilterSelect.vue';
    import { BASE_URL } from "../../consts";
    import debounce from 'lodash/debounce';

    export default {
        components: {
            SharpFilterSelect
        },
        computed: {
            ...mapGetters('global-filters', {
                filters: 'filters/filters',
                filterValue: 'filters/value',
            }),
        },
        methods: {
            handleFilterChanged(filter, value) {
                this.$store.dispatch('global-filters/post', { filter, value })
                    .then(() => {
                        location.href = BASE_URL;
                    });
            },
            handleOpened() {
                this.$emit('open');
            },
            handleClosed() {
                this.$emit('close');
            },
            updateLayout: debounce(function () {
                [...this.$el.querySelectorAll('.SharpFilterSelect')].forEach(select => {
                    const container = this.$el.parentElement;
                    const height = (container.offsetHeight-select.offsetHeight) - (select.offsetTop-container.offsetTop);
                    const dropdown = select.querySelector('.SharpAutocomplete .multiselect__content');
                    dropdown.style.height = `${height}px`;
                });
            }, 300)
        },
        mounted() {
            this.updateLayout();
            window.addEventListener('resize', this.updateLayout);
        },
        destroyed() {
            window.removeEventListener('resize', this.updateLayout);
        }
    }
</script>


Moved complex condition check into a method (task #12103)

<template>

    <select v-model="aggregate" class="form-control input-sm form-group">
      <option value="">-- Aggregate --</option>
      <option v-for="item in aggregates" :value="item.value">{{ item.text }}</option>
    </select>

        <select v-model="model" class="form-control input-sm">
          <option v-for="item in models">{{ item }}</option>
        </select>

        <select v-model="field" class="form-control input-sm">
          <option value="">-- Aggregate field --</option>
          <option v-for="item in fieldsList" v-if="item.group === model" :value="item.field">{{ item.label }}</option>
        </select>

        <select v-model="filter" class="form-control input-sm">
          <option value=""></option>
          <option v-for="item in filters" :value="item.value">{{ item.text }}</option>
        </select>

            <input type="number" v-model="value" step="any" max="99999999999" class="form-control input-sm" />

</template>


Let Viewer.vue use async compute for md to html conversion


Content Viewer white-space (#719)

Content Viewer white-space (#719)

<script lang="ts" src="./content-viewer"></script>

<template>
	<div class="content-viewer" :class="viewerStyleClass">
		<app-content-viewer-base-component
			v-if="shouldShowContent"
			:content="data.content"
			:owner="owner"
		/>
	</div>
</template>

<style lang="stylus" scoped>
>>> p > code
	white-space: normal

// Because white space is rendered out in the editor, we want the viewer to get as close to that
// as possible. HTML by default collapses white space, and this overrides that behavior.
>>> p > span
	white-space: pre-wrap
	white-space: break-spaces
</style>

<script lang="ts" src="./content-viewer"></script>

<template>
	<div class="content-viewer" :class="viewerStyleClass">
		<app-content-viewer-base-component
			v-if="shouldShowContent"
			:content="data.content"
			:owner="owner"
		/>
	</div>
</template>

<style lang="stylus" scoped>
>>> p > code
	white-space: normal
</style>


Clean up

Clean up

<template>

		<template v-if="$store.getters.isSignedIn">

				<router-link to="/">
					%fa:home%

%i18n:@home%

				</router-link>

				<router-link to="/deck">
					%fa:columns%

%i18n:@deck%

				</router-link>

					%fa:comments%

%i18n:@messaging%

					<template v-if="hasUnreadMessagingMessage">%fa:circle%</template>

					%fa:gamepad%

%i18n:@game%

					<template v-if="hasGameInvitations">%fa:circle%</template>

		</template>

</template>

<template>

		<template v-if="$store.getters.isSignedIn">

				<router-link to="/">
					%fa:home%

%i18n:@home%

				</router-link>

				<router-link to="/deck">
					%fa:columns%

%i18n:@deck% (beta)

				</router-link>

					%fa:comments%

%i18n:@messaging%

					<template v-if="hasUnreadMessagingMessage">%fa:circle%</template>

					%fa:gamepad%

%i18n:@game%

					<template v-if="hasGameInvitations">%fa:circle%</template>

		</template>

</template>


继续开发编辑部分

继续开发编辑部分

<template>

        <button
          class="btn-floating btn-large waves-effect waves-light tooltipped"
          data-position="left" data-delay="0" data-tooltip="选择模式"
          :class="mode === 'select' ? 'purple' : 'white'"
          @click="mode = 'select'"
        >

        </button>

        <button
          class="btn-floating btn-large waves-effect waves-light tooltipped"
          data-position="left" data-delay="0" data-tooltip="背景"
          :class="mode === 'background' ? 'red' : 'white'"
          @click="changeMode('background')"
        >

        </button>

          <button
            class="waves-effect waves-teal btn-flat tooltipped"
            data-position="top" data-delay="0" data-tooltip="添加背景"
            @click="loadBackgroundImg"
          >

          </button>

        <button
          class="btn-floating btn-large waves-effect waves-light tooltipped"
          data-position="left" data-delay="0" data-tooltip="画墙模式"
          :class="mode === 'wall' ? 'yellow darken-2' : 'white'"
          @click="changeMode('wall')"
        >

        </button>

        <button
          class="btn-floating btn-large waves-effect waves-light tooltipped"
          data-position="left" data-delay="0" data-tooltip="画门模式"
          :class="mode === 'door' ? 'green' : 'white'"
          @click="changeMode('door')"
        >

        </button>

        <button
          class="btn-floating btn-large waves-effect waves-light tooltipped"
          data-position="left" data-delay="0" data-tooltip="画窗模式"
          :class="mode === 'window' ? 'blue' : 'white'"
          @click="changeMode('window')"
        >

        </button>

{{'x=' + mousePos.x + ', y=' + mousePos.y}}

        <button
          class="waves-effect waves-teal btn-flat tooltipped"
          data-position="top" data-delay="0" data-tooltip="可见性"
          @click="onBackgroundImgVisibilityChange"
        >

        </button>

        <button
          class="waves-effect waves-teal btn-flat tooltipped"
          data-position="top" data-delay="0" data-tooltip="编辑锁定"
          @click="configs.background.locked = !configs.background.locked"
        >

        </button>

          <input
            id="blueprint-edit-panel__background__opacity"
            type="range"
            min="0" max="100"
            v-model="configs.background.opacity"
            @input="onBackgroundImgOpacityChange"
          />

  <input
    id="blueprint-edit-panel__background-input"
    type="file"
    @change="onBackgroundImgChange"
  />

</template>

<template>

        <button
          class="btn-floating btn-large waves-effect waves-light tooltipped"
          data-position="left" data-delay="0" data-tooltip="选择模式"
          :class="mode === 'select' ? 'purple' : 'white'"
          @click="mode = 'select'"
        >

        </button>

        <button
          class="btn-floating btn-large waves-effect waves-light tooltipped"
          data-position="left" data-delay="0" data-tooltip="背景"
          :class="mode === 'background' ? 'red' : 'white'"
          @click="mode = 'background'"
        >

        </button>

          <button
            class="waves-effect waves-teal btn-flat tooltipped"
            data-position="top" data-delay="0" data-tooltip="添加背景"
            @click="loadBackgroundImg"
          >

          </button>

        <button
          class="btn-floating btn-large waves-effect waves-light tooltipped"
          data-position="left" data-delay="0" data-tooltip="画墙模式"
          :class="mode === 'wall' ? 'yellow darken-2' : 'white'"
          @click="mode = 'wall'"
        >

        </button>

        <button
          class="btn-floating btn-large waves-effect waves-light tooltipped"
          data-position="left" data-delay="0" data-tooltip="画门模式"
          :class="mode === 'door' ? 'green' : 'white'"
          @click="mode = 'door'"
        >

        </button>

        <button
          class="btn-floating btn-large waves-effect waves-light tooltipped"
          data-position="left" data-delay="0" data-tooltip="画窗模式"
          :class="mode === 'window' ? 'blue' : 'white'"
          @click="mode = 'window'"
        >

        </button>

{{'x=' + mousePos.x + ', y=' + mousePos.y}}

        <button
          class="waves-effect waves-teal btn-flat tooltipped"
          data-position="top" data-delay="0" data-tooltip="可见性"
          @click="onBackgroundImgVisibilityChange"
        >

        </button>

        <button
          class="waves-effect waves-teal btn-flat tooltipped"
          data-position="top" data-delay="0" data-tooltip="编辑锁定"
          @click="configs.background.locked = !configs.background.locked"
        >

        </button>

          <input
            id="blueprint-edit-panel__background__opacity"
            type="range"
            min="0" max="100"
            v-model="configs.background.opacity"
            @input="onBackgroundImgOpacityChange"
          />

  <input
    id="blueprint-edit-panel__background-input"
    type="file"
    @change="onBackgroundImgChange"
  />

</template>


Default schedule time from +1h to +1 day

<template>

         <back-button />

Schedule a transaction

{{ $t('interface.sendTxType') }}

                <currency-picker
                  :currency="tokensWithBalance"
                  :page="'sendEgasAmountthAndTokens'"
                  :token="true"
                  @selectedCurrency="selectedCurrency = $event"
                />

              <standard-input
                :options="amountInputOptions()"
                @changedValue="amount = $event"
              />

                Amount higher than balance

                Not enough ETH on account to schedule

          <standard-input
            :options="toAddressInputOptions()"
            @changedValue="toAddress = $event"
          />

            Invalid Address

            <standard-input
              :options="blockNumberInputOptions()"
              @changedValue="selectedBlockNumber = $event"
            />

              Should be at least
              {{ supportedModes[1].executionWindow.min }} blocks in the future

Date & Time

                <datetime-picker
                  v-model="datetime"
                  :min-datetime="now.toISOString()"
                  :value-zone="selectedTimeZone"
                  :zone="selectedTimeZone"
                  class="theme-mew"
                  type="datetime"
                />

                  Make sure that the time is at least
                  {{ supportedModes[0].executionWindow.min }} minutes in the
                  future

Timezone

                <standard-dropdown
                  :options="timezoneOptions"
                  :placeholder="selectedTimeZone"
                  @selection="selectedTimeZone = $event"
                />

                  Time Bounty

                    {{ bounty }}

                  Covers up to
                  {{ estimatedMaximumExecutionGasPrice }} gwei gas
                  price on future execution

                <standard-input
                  :options="customTimeBountyInputOptions()"
                  @changedValue="timeBounty = $event"
                />

                  Please set a bounty of {{ minBounty }} or higher

              <standard-input
                :options="bountyUsdDisplayOptions()"
                class="bounty-usd-display"
              />

Advanced

                      <input
                        type="checkbox"
                        @click="advancedExpand = !advancedExpand"
                      />

Scheduling mode

                      {{ mode.name }}

                <standard-input
                  :options="executionWindowInputOptions()"
                  @changedValue="windowSize = $event"
                />

                  Please set an execution window of
                  {{ selectedMode.executionWindow.min }} or higher

            <standard-input
              :options="requireDepositInputOptions()"
              @changedValue="deposit = $event"
            />

              Invalid deposit number

                <standard-input
                  :options="futureGasPriceInputOptions()"
                  @changedValue="futureGasPrice = $event"
                />

                  Please set a gas price of {{ minGasPrice }} or higher

                <standard-input
                  :options="gasLimitInputOptions()"
                  @changedValue="gasLimit = $event"
                />

                  Please set a gas limit of 0 or higher

                <standard-input
                  :options="futureGasLimitInputOptions()"
                  @changedValue="futureGasLimit = $event"
                />

                  Please set a future gas limit of 0 or higher

            <standard-input
              v-if="!isTokenTransfer"
              :options="dataInputOptions()"
              @changedValue="data = $event"
            />

              Please provide the data in a hexadecimal format.

          Note: You are scheduling a token transfer. Token
          transfers require 2 separate transactions. One for token scheduling
          and another one for token transfer approval.

              Your TX has been scheduled with the transaction hash
              <scheduled-transaction-explorer-link :tx-hash="tx.hash" />
              and is waiting to be mined.

                Please wait for the transaction to be mined before approving...

                Note: If this is taking too long, follow
                <scheduled-transaction-explorer-link
                  :tx-hash="tx.hash"
                  :link-text="'this'"
                />
                link to approve the transaction.

                The transaction has been mined. Please
                approve the token transfer now.

                Approve Token Transfer

          Schedule Transaction

</template>


Enable the form button based on the options that we display at the moment

Signed-off-by: Joas Schilling <ab43a7c9cb5b2380afc4ddf8b3e2583169b39a02@schilljs.com>

<template>
	<Modal v-on="$listeners">

			<template v-if="!loading">

				<form @submit.prevent="handleSubmitPermissions">
					<CheckboxRadioSwitch
						ref="callStart"
						:checked.sync="callStart"
						class="checkbox">
						{{ t('spreed', 'Start a call') }}
					</CheckboxRadioSwitch>
					<CheckboxRadioSwitch
						ref="lobbyIgnore"
						:checked.sync="lobbyIgnore"
						class="checkbox">
						{{ t('spreed', 'Skip the lobby') }}
					</CheckboxRadioSwitch>
					<CheckboxRadioSwitch
						ref="publishAudio"
						:checked.sync="publishAudio"
						class="checkbox">
						{{ t('spreed', 'Enable the microphone') }}
					</CheckboxRadioSwitch>
					<CheckboxRadioSwitch
						ref="publishVideo"
						:checked.sync="publishVideo"
						class="checkbox">
						{{ t('spreed', 'Enable the camera') }}
					</CheckboxRadioSwitch>
					<CheckboxRadioSwitch
						ref="publishScreen"
						:checked.sync="publishScreen"
						class="checkbox">
						{{ t('spreed', 'Share the screen') }}
					</CheckboxRadioSwitch>
					<button ref="submit"
						type="submit"
						:disabled="submitButtonDisabled"
						class="nc-button primary">
						{{ t('spreed', 'Update permissions') }}
					</button>
				</form>
			</template>

{{ t('spreed', 'Updating permissions') }}

	</Modal>
</template>


Add: new logic to calculate some votes

Add: new logic to calculate some votes

<template lang="html">

					<loading text="Carregando Painel de Controle..." :height="80" :width="80"></loading>

Painel de Controle

							<button class="btn btn-warning btn-md" v-on:click="refreshData()">Recarregar Dados</button>

								<vue-chart
								chart-type="LineChart"
								:columns="columns"
								:rows="titles"
								></vue-chart>

{{index}}

									{{index}}

										Voto: {{subSubitem.vote}}

Quantidade de votos do dia: {{subitem.quantVotesDay}}

Media de votos do dia: {{subitem.mediaDay}}

</template>


Added land district numbers to names in pull-down

/*
Licensed under the Apache License, Version 2.0 (the "License");
    you may not use this file except in compliance with the License.
    You may obtain a copy of the License at

        http://www.apache.org/licenses/LICENSE-2.0

    Unless required by applicable law or agreed to in writing, software
    distributed under the License is distributed on an "AS IS" BASIS,
    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    See the License for the specific language governing permissions and
    limitations under the License.
*/
<template>
    <fieldset>

          <legend :id="id">Well Location</legend>

            Save
            Back to top

Please provide as much information as possible. A minimum of one type of well location information is required below:

1) Well location address

Same as owner address

          <form-input
            v-model="streetAddressInput"
            id="wellStreetAddress"
            type="text"
            label="Street address"
            :errors="errors['street_address']"
            :loaded="fieldsLoaded['street_address']"
            :disabled="sameAsOwnerAddress"
          ></form-input>

          <form-input
              id="wellCity"
              label="City"
              type="text"
              v-model="cityInput"
              :errors="errors['city']"
              :loaded="fieldsLoaded['city']"
              :disabled="sameAsOwnerAddress"
              >
          </form-input>

OR

2) Legal description

          <form-input
              id="legalLot"
              label="Lot"
              type="text"
              v-model="legalLotInput"
              :errors="errors['legal_lot']"
              :loaded="fieldsLoaded['legal_lot']"
              >
          </form-input>

          <form-input
              id="legalPlan"
              label="Plan"
              type="text"
              v-model="legalPlanInput"
              :errors="errors['legal_plan']"
              :loaded="fieldsLoaded['legal_plan']"
              >
          </form-input>

          <form-input
              id="legalDistrictLot"
              label="District Lot"
              type="text"
              v-model="legalDistrictLotInput"
              :errors="errors['legal_district_lot']"
              :loaded="fieldsLoaded['legal_district_lot']"
              >
          </form-input>

          <form-input
              id="legalBlock"
              label="Block"
              type="text"
              v-model="legalBlockInput"
              :errors="errors['legal_block']"
              :loaded="fieldsLoaded['legal_block']"
              >
          </form-input>

          <form-input
              id="legalSection"
              label="Section"
              type="text"
              v-model="legalSectionInput"
              :errors="errors['legal_section']"
              :loaded="fieldsLoaded['legal_section']"
              >
          </form-input>

          <form-input
              id="legalTownship"
              label="Township"
              type="text"
              v-model="legalTownshipInput"
              :errors="errors['legal_township']"
              :loaded="fieldsLoaded['legal_township']"
              >
          </form-input>

          <form-input
              id="legalRange"
              label="Range"
              type="text"
              v-model="legalRangeInput"
              :errors="errors['legal_range']"
              :loaded="fieldsLoaded['legal_range']"
              >
          </form-input>

          <form-input
              id="landDistrict"
              label="Land District"
              select
              :options="districtCodes"
              text-field="name"
              value-field="land_district_code"
              v-model="landDistrictInput"
              :errors="errors['land_district']"
              :loaded="fieldsLoaded['land_district']"
              >
          </form-input>

OR

          <form-input
              id="legalPID"
              label="3) Parcel Identifier"
              type="text"
              v-model="legalPIDInput"
              :errors="errors['legal_pid']"
              :loaded="fieldsLoaded['legal_pid']"
              >
          </form-input>

          <form-input
              id="wellLocationDescription"
              label="Description of Well Location"
              type="text"
              v-model="wellLocationDescriptionInput"
              :errors="errors['well_location_description']"
              :loaded="fieldsLoaded['well_location_description']"
              >
          </form-input>

        Must provide well location as either an address, legal description, or parcel identifier.

    </fieldset>
</template>


remove old select

remove old select

<template>
  <select>
    <slot></slot>
  </select>
</template>

<script type="javascript">
  export default  {
    props: ['options', 'value'],
    mounted: function () {
      let vm = this;
      $(this.$el)
      // init select2
              .select2({
                data: this.options,
                language: 'zh-CN'
              })
              // emit event on change.
              .on('change', function () {
                let thisValue = $(this).val();
                if (thisValue && thisValue.constructor == Array) {
                  for (let i = 0; i < thisValue.length; i++) {
                    thisValue[i] = parseInt(thisValue[i]);
                  }
                }
                else {
                  thisValue = parseInt(thisValue);
                  if (isNaN(thisValue)) {
                    thisValue = null;
                  }
                }
                vm.$emit('input', thisValue);
              })
              .val(this.value).trigger('change');
    },
    watch: {
      value: function (value) {
        // update value
        let obj = $(this.$el);
        let oldValue = obj.val();

        if (oldValue === undefined || oldValue == null) {
          oldValue = [];
        }
        if (value === undefined || value == null) {
          value = [];
        }

        let valueLen = value.length;
        for (let i = 0; i < valueLen; i++) {
          let item = value[i];
          if (typeof(item) == 'object') {
            value[i] = item.id;
          }
        }

        if (oldValue.toString() !== value.toString()) {
          obj.val(value).trigger("change");
        }
      },
      options: function (options) {
        // update options
        let select = $(this.$el);
        select.empty();
        select.select2({data: options});
        if (options.constructor == Array && options.length) {
          this.$emit('input', options[0].id);
        }
        else {
          this.$emit('input', null);
        }
      }
    },
    destroyed: function () {
      $(this.$el).off().select2('destroy');
    }
  };

</script>


Minor changes to intelligence view (#2116)

Co-authored-by: Alexander J <c6b7d873bc18588d8938fab164625534ff91aaf2@users.noreply.github.com>

<template>

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

                  Overview
                </router-link>

                <router-link :to="{ name: 'Explore' }" data-explore-element="true">

                  Explore
                </router-link>

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

                  Graph
                </router-link>

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

                  Aggregate
                </router-link>

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

                  Analyze
                </router-link>

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

                  Stories
                </router-link>

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

                  Sigma
                </router-link>

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

                  Attributes
                    {{ attributeCount }}

                </router-link>

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

                  Intelligence
                    {{ intelligenceCount }}

                </router-link>

            <slot></slot>

</template>


Enhance accessibility of notes

Enhance accessibility of notes

<template>

  <AnnotationHandle v-if="hasHandle"
                    :ui-state="uiState">

      Remove note

  </AnnotationHandle>
  <template v-if="isHead">

      <a class="note-icon"
         :href="`#${annotation.id}-head`"
         @click.prevent="handleClick">

      <span :id="`${annotation.id}-content`"
            tabindex="0"
            class="note-content">
        {{annotation.content}}

  </template>
  <a class="selected-text"
     :href="`#${annotation.id}-content`"
     :id="isHead ? `${annotation.id}-head` : ''"
     @click.prevent="handleClick"><slot></slot>

</template>

<template>

  <AnnotationHandle v-if="hasHandle"
                    :ui-state="uiState">

      Remove note

  </AnnotationHandle>
  <template v-if="isHead">

      <button class="note-icon"
              @click="toggleExpansion(uiState)">

      </button>

        {{annotation.content}}

  </template>
  <slot></slot>

</template>


add back $trs back

add back $trs back

<template>

    {{ $tr('back') }}
    <svg v-if="showarrow" role="presentation" src="../../icons/folder_back.svg"></svg>
    {{ text }}

</template>


Changes user menu icon

Changes user menu icon

<template>

                    {{userName}}

            <slot>

            </slot>

                <router-link v-for="(link, index) in sidebarLinks" :key="index" :to="link.path" tag="li"
                             :ref="link.name">

{{link.name}}

                </router-link>

            <moving-arrow :move-y="arrowMovePx">

            </moving-arrow>

</template>


[#2627] - fixed sidebar menu scrolling

[#2627] - fixed sidebar menu scrolling

<template>

        <button
          type="button"
          :aria-label="$t('Close')"
          class="w-100 inline-flex end-xs bg-cl-transparent brdr-none p0 close-btn"
          @click="closeMenu"
        >
          close
        </button>

            <router-link
              class="block px25 py20 cl-accent no-underline"
              :to="localizedRoute('/')"
              exact
            >
              {{ $t('Home') }}
            </router-link>

               0"
              />
              <router-link
                v-else
                class="px25 py20 cl-accent no-underline col-xs"
                :to="localizedRoute({ name: 'category', fullPath: category.url_path, params: { id: category.id, slug: category.slug }})"
              >
                {{ category.name }}
              </router-link>

            <router-link
              class="block px25 py20 brdr-bottom-1 brdr-cl-secondary cl-accent no-underline fs-medium-small"
              :to="localizedRoute('/sale')"
              exact
            >
              {{ $t('Sale') }}
            </router-link>

            <router-link
              class="block px25 py20 brdr-bottom-1 brdr-cl-secondary cl-accent no-underline fs-medium-small"
              :to="localizedRoute('/magazine')"
              exact
            >
              {{ $t('Magazine') }}
            </router-link>

            <router-link
              class="block px25 py20 brdr-bottom-1 brdr-cl-secondary cl-accent no-underline fs-medium-small"
              :to="localizedRoute('/compare')"
              exact
            >
              {{ $t('Compare products') }}
            </router-link>

              {{ $t('My account') }}

</template>


trim opening and closing line breaks

trim opening and closing line breaks

<docs>
  # wysiwyg

  A multi-line text input which allows a rich editing experience. Uses [Quill](http://quilljs.com/).

  ## Arguments

  * **buttons** _(optional)_  array of button names (strings) or groups (arrays) for text styling, passed directly into Quill (defaults to "remove formatting")
  * **styled** _(optional)_   style the content editable element like our `text` and `textarea` inputs.
  * **type** _(optional)_ `single-line`, `multi-line`, or `multi-component`. (defaults to `single-line`)
  * **pseudoBullet** _(optional)_ boolean to enable tab to create pseudo bullet points
  * **paste** _(optional)_ array of paste rules for parsing pasted content and creating new components

  The buttons allowed in our wysiwyg behavior are [defined in Quill's documentation](http://quilljs.com/docs/modules/toolbar/)

  By default, wysiwyg fields will use the styles of the containing component. To use our kiln input styling, set `styled` to `true`.

  The default `type` -- `single-line` -- allows entering one line of rich text, but prevents users from creating new paragraphs or applying paste rules.

  `multi-line` is used for components like blockquotes or lists, and allows paste rules to create new block-level elements in the same component's field (but not create different components).

  `multi-component` enables more affordances, including:

  * keyboard navigation across components (up and down arrows)
  * enter creating new components (and splitting text in front of the cursor into them)
  * delete removing components (and merging text in front of the cursor into the previous component)
  * full paste rule affordances, including creating different components

  **Paste** is an optional array of pasting rules that's used for parsing and creating different components. This is useful for transforming pasted links into embeds, catching pasted blockquotes, etc. Rules have these properties:

  * `match` - regex to match the pasted content. all rules will be wrapped in `^` and `$` (so they don't match urls _inside_ links in the content)
  * `matchLink` - boolean to determine whether _links_ containing the regex should also match. Should be true for embeds, false for components that could potentially contain links inside them.
  * `component` - the name of the component that should be created
  * `field` - the name of the field that the captured data should be populated to on the new component. the (last) new component will focus this field after it's added (note: this is limited to a single regex capture group)
  * `group` - (optional) the group that should be focused when the (last) new component is added (instead of the specific field). this is useful for components with forms that contain both the specified field and other things, and preserves the same editing experience as editing that component normally

  ## Example

  ```
  text:
    _placeholder:
      height: 50px
      text: New Paragraph
      required: true
    _display: inline
    _has:
      -
        fn: wysiwyg
        type: 'multi-component'
        pseudoBullet: true
        styled: false
        buttons:
          - bold
          - italic
          - strikethrough
          - link
        paste:
          -
            match: (https?://twitter\.com/\w+?/status/\d+)
            matchLink: true
            component: clay-tweet
            field: url
          -
            match: (https?://www\.facebook\.com/.+?/posts/\d+)
            matchLink: true
            component: clay-facebook-post
            field: url
            group: inlineForm
          -
            match: <blockquote>(.*?)(?:</blockquote>)?
            component: blockquote
            field: text
          -
            match: (.*)
            component: clay-paragraph
            field: text
  ```
</docs>

<template>

</template>