Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/nextcloud/polls.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/js/components/Base/Counter.vue3
-rw-r--r--src/js/components/Base/UserItem.vue5
-rw-r--r--src/js/components/Options/OptionItem.vue200
-rw-r--r--src/js/components/VoteTable/VoteItem.vue (renamed from src/js/components/VoteTable/VoteTableVoteItem.vue)29
-rw-r--r--src/js/components/VoteTable/VoteTable.vue426
-rw-r--r--src/js/components/VoteTable/VoteTableHeaderItem.vue29
-rw-r--r--src/js/views/Vote.vue64
7 files changed, 297 insertions, 459 deletions
diff --git a/src/js/components/Base/Counter.vue b/src/js/components/Base/Counter.vue
index 7a0a8883..2ff72cf1 100644
--- a/src/js/components/Base/Counter.vue
+++ b/src/js/components/Base/Counter.vue
@@ -57,7 +57,7 @@
<span />
</div>
- <div v-if="option.maybe && showMaybe" class="maybe" :style="{flex: option.maybe }">
+ <div v-if="option.maybe && showMaybe" class="maybe" :style="{ flex: option.maybe }">
<span />
</div>
</div>
@@ -95,6 +95,7 @@ export default {
.counter {
display: flex;
+ justify-content: space-around;
}
.counter--icon {
diff --git a/src/js/components/Base/UserItem.vue b/src/js/components/Base/UserItem.vue
index c78cc6e2..582c8479 100644
--- a/src/js/components/Base/UserItem.vue
+++ b/src/js/components/Base/UserItem.vue
@@ -142,10 +142,7 @@ export default {
flex: 1;
align-items: center;
max-width: 100%;
-}
-
-.user-item__avatar {
- margin: 2px 4px;
+ padding: 4px;
}
.type-icon {
diff --git a/src/js/components/Options/OptionItem.vue b/src/js/components/Options/OptionItem.vue
index 607f4163..62b2791d 100644
--- a/src/js/components/Options/OptionItem.vue
+++ b/src/js/components/Options/OptionItem.vue
@@ -24,38 +24,45 @@
<Component :is="tag" class="option-item" :class="{ draggable: isDraggable, 'date-box': show === 'dateBox' }">
<div v-if="isDraggable" class="option-item__handle icon icon-handle" />
- <div v-if="showRank" class="option-item__rank">
- {{ option.rank }}
- </div>
-
<div v-if="show === 'textBox'" v-tooltip.auto="optionTooltip" class="option-item__option--text">
{{ optionText }}
</div>
<div v-if="show === 'dateBox'" v-tooltip.auto="dateLocalFormatUTC" class="option-item__option--datebox">
- <div class="event-from">
- <div class="month">
- {{ dateBoxMonth }}
- </div>
- <div class="day">
- {{ dateBoxDay }}
+ <div class="event-date">
+ <div class="event-from">
+ <div class="month">
+ {{ event.from.month }}
+ </div>
+ <div class="day">
+ {{ event.from.day }}
+ </div>
+ <div class="dow">
+ {{ event.from.dow }}
+ </div>
</div>
- <div class="dow">
- {{ dateBoxDow }}
+ <div v-if="option.duration && !event.oneDay && !event.to.sameDay" class="devider">
+ -
</div>
- <div v-if="!oneDayEvent && !wholeDayDuration" class="time">
- {{ dateBoxTime }}
+ <div v-if="option.duration && !event.oneDay && !event.to.sameDay" class="event-to">
+ <div class="month">
+ {{ event.to.month }}
+ </div>
+ <div class="day">
+ {{ event.to.day }}
+ </div>
+ <div class="dow">
+ {{ event.to.dow }}
+ </div>
</div>
</div>
- <div v-if="option.duration && !oneDayEvent" class="event-to">
- <div v-if="option.duration && !oneDayEvent" class="devider">
- -
- </div>
- <div v-if="option.duration && !oneDayEvent && !sameDayUntil" class="until">
- {{ dateUntil }}
+
+ <div class="event-time">
+ <div v-if="!event.oneDay && !event.wholeDay" class="time-from">
+ {{ event.from.t }}
</div>
- <div v-if="option.duration && !oneDayEvent && !wholeDayDuration" class="until">
- {{ timeUntil }}
+ <div v-if="option.duration && !event.oneDay && !event.wholeDay" class="time-to">
+ {{ event.to.t }}
</div>
</div>
</div>
@@ -79,10 +86,6 @@ export default {
type: Object,
required: true,
},
- showRank: {
- type: Boolean,
- default: false,
- },
tag: {
type: String,
default: 'div',
@@ -103,65 +106,61 @@ export default {
dateLocalFormat() {
if (this.option.duration) {
- if (this.oneDayEvent) {
- return moment.unix(this.option.timestamp).format('ll')
- } else if (this.wholeDayDuration) {
- return moment.unix(this.option.timestamp).format('ll') + ' - ' + this.dateUntil
- } else if (this.sameDayUntil) {
- return moment.unix(this.option.timestamp).format('llll') + ' - ' + this.timeUntil
+ if (this.event.oneDay) {
+ return this.event.from.df
+ } else if (this.event.wholeDay) {
+ return this.event.from.df + ' - ' + this.event.to.df
+ } else if (this.event.to.sameDay) {
+ return this.event.from.dtf + ' - ' + this.event.to.t
} else {
- return moment.unix(this.option.timestamp).format('llll') + ' - ' + this.dateUntil + ' ' + this.timeUntil
+ return this.event.from.dtf + ' - ' + this.event.to.dtf
}
} else {
- return moment.unix(this.option.timestamp).format('llll')
+ return this.event.from.dtf
}
},
dateLocalFormatUTC() {
if (this.option.duration) {
- return moment.unix(this.option.timestamp).utc().format('llll') + ' - ' + moment.unix(this.option.timestamp).add(this.option.duration, 'seconds').utc().format('llll') + ' UTC'
+ return this.event.from.utc + ' - ' + this.event.to.utc + ' UTC'
} else {
- return moment.unix(this.option.timestamp).utc().format('llll') + ' UTC'
+ return this.event.from.utc + ' UTC'
}
},
- oneDayEvent() {
- // this event starts at 0:00 local time and lasts exact 24 hours
- return this.option.duration === 86400 && moment.unix(this.option.timestamp).startOf('day').diff(moment.unix(this.option.timestamp), 'seconds') === 0
- },
-
- wholeDayDuration() {
- // This event starts at 0:00 local time and lasts one or more full days
- return this.option.duration && this.option.duration % 86400 === 0 && moment.unix(this.option.timestamp).startOf('day').diff(moment.unix(this.option.timestamp), 'seconds') === 0
- },
-
- sameDayUntil() {
- return moment.unix(this.option.timestamp).format('L') === moment.unix(this.option.timestamp).add(Math.max(0, this.option.duration - 1), 'seconds').format('L')
- },
-
- dateBoxMonth() {
- return moment.unix(this.option.timestamp).format('MMM') + " '" + moment.unix(this.option.timestamp).format('YY')
- },
-
- dateBoxDay() {
- return moment.unix(this.option.timestamp).format('Do')
- },
-
- dateBoxDow() {
- return moment.unix(this.option.timestamp).format('ddd')
- },
-
- dateBoxTime() {
- return moment.unix(this.option.timestamp).format('LT')
- },
-
- timeUntil() {
- return moment.unix(this.option.timestamp).add(this.option.duration, 'seconds').format('LT')
- },
-
- dateUntil() {
- return moment.unix(this.option.timestamp).add(Math.max(0, this.option.duration - 1), 'seconds').format('L')
- // return '(+ ' + n('polls', '%n day', '%n days', Math.floor(this.option.duration / 86400)) + ')'
+ event() {
+ const from = moment.unix(this.option.timestamp)
+ const to = moment.unix(this.option.timestamp + Math.max(0, this.option.duration))
+
+ // If we have a fullDay event, reduce time by 1 second to
+ // represent the end of the day (00:00:00 - 1 sec = 23:59:59)
+ const dayModifier = this.option.duration && this.option.duration % 86400 === 0 && from.startOf('day').diff(from, 'seconds') === 0 ? 1 : 0
+ const toShort = moment.unix(this.option.timestamp + Math.max(0, this.option.duration - dayModifier))
+ return {
+ from: {
+ month: from.format('MMM [ \']YY'),
+ day: from.format('Do'),
+ dow: from.format('ddd'),
+ t: from.format('LT'),
+ df: from.format('ll'),
+ dtf: from.format('llll'),
+ utc: from.utc().format('llll'),
+ },
+ to: {
+ month: toShort.format('MMM'),
+ day: toShort.format('D'),
+ dow: toShort.format('ddd'),
+ t: to.format('LT'),
+ df: toShort.format('ll'),
+ dtf: to.format('llll'),
+ utc: to.utc().format('llll'),
+ sameDay: from.format('L') === toShort.format('L'),
+ },
+ // this event starts at 0:00 (!)local time and lasts exact 24 hours
+ oneDay: this.option.duration === 86400 && from.startOf('day').diff(from, 'seconds') === 0,
+ // This event starts at 0:00 (!)local time and lasts one or more full days
+ wholeDay: !!dayModifier,
+ }
},
optionTooltip() {
@@ -195,10 +194,12 @@ export default {
.option-item {
display: flex;
align-items: center;
+ flex: 1;
+ position: relative;
&.date-box {
- flex: 1;
+ // flex: 1;
+ align-items: stretch;
flex-direction: column;
- align-items: flex-start;
}
}
@@ -208,12 +209,34 @@ export default {
align-items: center;
}
- .event-from {
- flex: 0 0 110px;
+ .devider {
+ align-self: center;
+ color: var(--color-text-lighter);
}
- .event-to {
- flex: 0 0 75px;
+ .event-date {
+ flex-direction: row !important;
+ align-items: stretch !important;
+ justify-content: center;
+ .event-from {
+ padding-bottom: 8px;
+ flex: 0;
+ }
+ .event-to {
+ flex: 0;
+ font-size: 80%;
+ justify-content: flex-end;
+ .day {
+ margin: 0;
+ }
+ }
+ }
+
+ .event-time {
+ margin-top: 8px;
+ .time-to {
+ font-size: 80%;
+ }
}
[class*='option-item__option'] {
@@ -258,11 +281,13 @@ export default {
display: flex;
flex-direction: column;
padding: 0 2px;
- align-items: center;
+ align-items: stretch;
justify-content: flex-start;
text-align: center;
+ hyphens: auto;
- .month, .dow {
+ .month, .dow, .time {
+ white-space: pre;
font-size: 1.1em;
color: var(--color-text-lighter);
}
@@ -272,17 +297,4 @@ export default {
}
}
- .mobile {
- .option-item {
- flex: 2;
- order: 1;
- min-width: 0;
- .option-item__option--text {
- hyphens: auto;
- align-items: center;
- white-space: nowrap;
- width: 50px;
- }
- }
- }
</style>
diff --git a/src/js/components/VoteTable/VoteTableVoteItem.vue b/src/js/components/VoteTable/VoteItem.vue
index 9f8d4d6f..7ce7b4d8 100644
--- a/src/js/components/VoteTable/VoteTableVoteItem.vue
+++ b/src/js/components/VoteTable/VoteItem.vue
@@ -21,9 +21,10 @@
-->
<template>
- <div class="vote-table-vote-item" :class="[answer, isConfirmed, { active: isVotable }]">
+ <div class="vote-item" :class="[answer, isConfirmed, { active: isVotable }, {currentUser: isCurrentUser}]">
<div v-if="isActive && !isLocked" class="icon" @click="setVote()" />
<div v-else class="icon" />
+ <slot name="indicator" />
</div>
</template>
@@ -31,7 +32,7 @@
import { mapGetters, mapState } from 'vuex'
export default {
- name: 'VoteTableVoteItem',
+ name: 'VoteItem',
props: {
option: {
@@ -42,16 +43,14 @@ export default {
type: String,
default: '',
},
- isActive: {
- type: Boolean,
- default: false,
- },
},
computed: {
...mapState({
voteLimit: state => state.poll.voteLimit,
optionLimit: state => state.poll.optionLimit,
+ currentUser: state => state.poll.acl.userId,
+ allowVote: state => state.poll.acl.allowVote,
}),
...mapGetters({
@@ -64,6 +63,14 @@ export default {
return this.isActive && this.isValidUser && !this.pollIsClosed && !this.isLocked && !this.isBlocked
},
+ isActive() {
+ return this.isCurrentUser && this.allowVote
+ },
+
+ isCurrentUser() {
+ return this.currentUser === this.userId
+ },
+
answer() {
return this.$store.getters['poll/votes/getVote']({
option: this.option,
@@ -122,7 +129,7 @@ export default {
<style lang="scss">
-.vote-table-vote-item {
+.vote-item {
display: flex;
flex: 1;
align-items: center;
@@ -173,7 +180,7 @@ export default {
}
}
-.theme--dark .vote-table-vote-item {
+.theme--dark .vote-item {
background-color: var(--color-polls-background-no--dark);
&.yes {
background-color: var(--color-polls-background-yes--dark);
@@ -186,8 +193,12 @@ export default {
}
}
-.vote-table-vote-item.confirmed:not(.yes):not(.maybe) .icon {
+.vote-item.confirmed:not(.yes):not(.maybe) .icon {
background-image: var(--icon-polls-no);
}
+.vote-item.confirmed {
+ background-color: transparent;
+}
+
</style>
diff --git a/src/js/components/VoteTable/VoteTable.vue b/src/js/components/VoteTable/VoteTable.vue
index b1517af4..0e8d289d 100644
--- a/src/js/components/VoteTable/VoteTable.vue
+++ b/src/js/components/VoteTable/VoteTable.vue
@@ -22,7 +22,9 @@
<template lang="html">
<div class="vote-table" :class="[viewMode, { closed: closed }]">
- <div class="vote-table__users fixed">
+ <div class="vote-table__users">
+ <div class="spacer" />
+
<UserItem v-for="(participant) in participants"
:key="participant.userId"
v-bind="participant"
@@ -33,76 +35,45 @@
</ActionButton>
</Actions>
</UserItem>
- </div>
- <transition-group name="list" tag="div" class="vote-table__header">
- <VoteTableHeaderItem v-for="(option) in rankedOptions"
- :key="option.id"
- :option="option"
- :class="{ 'confirmed' : option.confirmed }"
- :poll-type="poll.type"
- :view-mode="viewMode" />
- </transition-group>
-
- <transition-group v-if="poll.type === 'datePoll' && getCurrentUser() && settings.calendarPeek"
- name="list"
- tag="div"
- class="vote-table__calendar">
- <CalendarPeek
- v-for="(option) in rankedOptions"
- :key="option.id"
- :class="{ 'confirmed' : option.confirmed }"
- :option="option"
- :open="false" />
- </transition-group>
+ <div v-if="closed" class="confirm" />
+ </div>
<div class="vote-table__votes">
- <transition-group v-for="(participant) in participants"
- :key="participant.userId"
- name="list"
- tag="div"
- :class=" {currentuser: (participant.userId === acl.userId) }"
- class="vote-table__vote-row">
- <VoteTableVoteItem v-for="(option) in rankedOptions"
- :key="option.id"
- :class="{ 'confirmed' : option.confirmed && poll.closed }"
- :user-id="participant.userId"
+ <div v-for="(option) in rankedOptions" :key="option.id" :class="['vote-column', { 'confirmed' : option.confirmed }]">
+ <VoteTableHeaderItem :option="option" :view-mode="viewMode" />
+
+ <Confirmation v-if="option.confirmed && poll.closed" :option="option" />
+
+ <Counter v-else :show-maybe="!!poll.allowMaybe"
:option="option"
- :is-active="acl.userId === participant.userId && acl.allowVote" />
- </transition-group>
- </div>
+ :counter-style="viewMode === 'desktop' ? 'iconStyle' : 'barStyle'"
+ :show-no="viewMode === 'mobile'" />
+ <CalendarPeek v-if="poll.type === 'datePoll' && getCurrentUser() && settings.calendarPeek" :option="option" />
+ <div v-for="(participant) in participants" :key="participant.userId" class="vote-item-wrapper"
+ :class="{currentuser: participant.userId === acl.userId}">
+ <VoteItem :user-id="participant.userId" :option="option" />
+ </div>
- <transition-group v-if="closed"
- name="list"
- tag="div"
- class="vote-table__footer">
- <div v-for="(option) in rankedOptions"
- :key="option.id" class="vote-table-footer-item"
- :class="{ 'confirmed' : option.confirmed }">
- <Actions v-if="acl.allowEdit"
- class="action">
+ <Actions v-if="acl.allowEdit && closed" class="action confirm">
<ActionButton v-if="closed" :icon="option.confirmed ? 'icon-polls-confirmed' : 'icon-polls-unconfirmed'"
@click="confirmOption(option)">
{{ option.confirmed ? t('polls', 'Unconfirm option') : t('polls', 'Confirm option') }}
</ActionButton>
</Actions>
+ <!-- <div v-if="closed" class="vote-table__footer">
+ </div> -->
</div>
- </transition-group>
-
- <div class="vote-table__footer-blind fixed" />
-
- <div class="vote-table__calendar-blind fixed" />
+ </div>
- <div class="vote-table__header-blind fixed" />
+ <!-- -->
<Modal v-if="modal">
<div class="modal__content">
<h2>{{ t('polls', 'Do you want to remove {username} from poll?', { username: userToRemove }) }}</h2>
<div class="modal__buttons">
- <ButtonDiv :title="t('polls', 'No')"
- @click="modal = false" />
- <ButtonDiv :primary="true" :title="t('polls', 'Yes')"
- @click="removeUser()" />
+ <ButtonDiv :title="t('polls', 'No')" @click="modal = false" />
+ <ButtonDiv :primary="true" :title="t('polls', 'Yes')" @click="removeUser()" />
</div>
</div>
</Modal>
@@ -115,7 +86,9 @@ import { showSuccess } from '@nextcloud/dialogs'
import { Actions, ActionButton, Modal } from '@nextcloud/vue'
import orderBy from 'lodash/orderBy'
import CalendarPeek from '../Calendar/CalendarPeek'
-import VoteTableVoteItem from './VoteTableVoteItem'
+import Counter from '../Base/Counter'
+import Confirmation from '../Base/Confirmation'
+import VoteItem from './VoteItem'
import VoteTableHeaderItem from './VoteTableHeaderItem'
import { confirmOption } from '../../mixins/optionMixins'
@@ -125,9 +98,11 @@ export default {
Actions,
ActionButton,
CalendarPeek,
+ Counter,
+ Confirmation,
Modal,
VoteTableHeaderItem,
- VoteTableVoteItem,
+ VoteItem,
},
mixins: [confirmOption],
@@ -169,14 +144,10 @@ export default {
},
methods: {
- removeUser() {
- this.$store.dispatch('poll/votes/deleteUser', {
- userId: this.userToRemove,
- })
- .then(() => {
- showSuccess(t('polls', 'User {userId} removed', { userId: this.userToRemove }))
- })
+ async removeUser() {
this.modal = false
+ await this.$store.dispatch('poll/votes/deleteUser', { userId: this.userToRemove })
+ showSuccess(t('polls', 'User {userId} removed', { userId: this.userToRemove }))
this.userToRemove = ''
},
@@ -190,283 +161,154 @@ export default {
<style lang="scss">
-// use grid
.vote-table {
- display: grid;
- overflow: scroll;
-
- // define default flex items
- .vote-table__users,
- .vote-table__header,
- .vote-table__calendar,
- .vote-table__votes,
- .vote-table__footer,
- .vote-table__vote-row,
- .vote-table-header-item,
- .vote-table-vote-item {
- display: flex;
+ display: flex;
+ flex: 1;
+ .user-item, .vote-item-wrapper {
+ flex: 0;
+ height: 53px;
+ min-height: 53px;
+ border-top: solid 1px var(--color-border-dark);
}
-
- .vote-table-header-item,
- .calendar-peek,
- .vote-table-vote-item,
- .vote-table-footer-item {
- order: 2;
+ .vote-table-header-item {
+ flex: 1;
+ flex-direction: column;
+ align-items: stretch;
+ padding: 0 8px;
+ order:1;
}
-
- //set default style for confirmed options
- &.closed .confirmed {
- &.vote-table-header-item,
- &.calendar-peek,
- &.vote-table-vote-item,
- &.vote-table-footer-item {
- order: 1;
- flex: 1;
- border-radius: 10px;
- border: 1px solid var(--color-polls-foreground-yes) !important;
- border-top: 1px solid var(--color-polls-foreground-yes) !important;
- border-bottom: 1px solid var(--color-polls-foreground-yes) !important;
- background-color: var(--color-polls-background-yes) !important;
- padding: 8px 2px;
+ .confirmation {
+ order:3;
+ }
+ .counter {
+ order:3;
+ }
+ .calendar-peek {
+ order:2;
+ }
+ .confirm {
+ height: 45px;
+ order: 20;
+ }
+ .spacer {
+ order: 1;
+ }
+ .user-item, .vote-item-wrapper {
+ order: 10;
+ &.currentuser {
+ order:5;
}
}
-}
-
-.theme--dark .vote-table.closed .confirmed {
- &.vote-table-header-item,
- &.calendar-peek,
- &.vote-table-vote-item,
- &.vote-table-footer-item {
- background-color: var(--color-polls-background-yes--dark) !important;
+ .spacer {
+ flex: 1;
}
-}
-// justify styles for mobile view
-.vote-table.mobile {
- grid-template-columns: auto auto 1fr;
- grid-template-rows: auto;
- grid-template-areas: 'vote calendar header';
- justify-items: stretch;
- .vote-table__header {
- grid-area: header;
+ .vote-table__users {
+ display: flex;
flex-direction: column;
+ overflow-x: scroll;
+ min-width: 90px;
+ .user-item__name {
+ min-width: initial;
+ }
+ // margin-bottom: 8px;
+ // padding: 8px 0;
}
.vote-table__votes {
- grid-area: vote;
- .vote-table__vote-row {
- flex-direction: column;
- }
+ display: flex;
+ flex: 1;
+ overflow-x: scroll;
}
- .vote-table__calendar {
- grid-area: calendar;
+ .vote-column {
+ display: flex;
+ flex: 1 0 auto;
flex-direction: column;
- }
- .calendar-peek {
- flex-direction: row;
- flex: 1;
- align-items: center;
+ align-items: stretch;
+ min-width: 85px;
+ max-width: 280px;
+ // padding: 8px 0;
+ .vote-item {
+ flex-direction: column;
+ }
}
- .vote-table__header-blind,
- .vote-table__users,
- .vote-table__vote-row:not(.currentuser),
- .vote-table__footer-blind,
- .vote-table__footer {
- display: none;
+ &.closed .vote-column.confirmed {
+ border-radius: 10px;
+ border: 1px solid var(--color-polls-foreground-yes) !important;
+ background-color: var(--color-polls-background-yes) !important;
+ padding: 8px 2px;
}
- .vote-table-header-item,
- .calendar-peek,
- .vote-table-vote-item {
- padding-left: 12px;
- padding-right: 12px;
- border-bottom: 1px solid var(--color-border-dark);
- min-height: 3em;
- height: 3em;
+ .vote-item-wrapper {
+ display: flex;
+ padding: 4px 1px;
}
- &.closed .confirmed {
- margin-top: 8px;
- margin-bottom: 8px;
- font-weight: bold;
-
- &.vote-table-vote-item {
- border-right: none !important;
- border-bottom-right-radius: 0;
- border-top-right-radius: 0;
- }
-
- &.calendar-peek{
- border-left: none !important;
- border-right: none !important;
- border-radius: 0;
- }
-
- &.vote-table-header-item {
- border-left: none !important;
- border-bottom-left-radius: 0;
- border-top-left-radius: 0;
- }
+ .vote-table__footer {
+ align-items: center;
}
}
-.vote-table.desktop {
- grid-template-columns: auto repeat(var(--polls-vote-columns), 1fr);
- grid-template-rows: auto repeat(var(--polls-vote-rows), 1fr) auto;
- grid-template-areas:
- 'blind1 options'
- 'blind2 calendar'
- 'users vote'
- 'blind3 footer';
- justify-items: stretch;
- padding-bottom: 14px; // leave space for the scrollbar!
-
- .vote-table__header {
- grid-area: options;
- flex-direction: row;
-
- .vote-table-header-item {
- flex-direction: column;
- flex: 1;
- align-items: center;
- }
- }
-
- .vote-table__calendar {
- grid-area: calendar;
- flex-direction: row;
+.vote-table.mobile {
+ flex-direction: column;
- .calendar-peek {
- flex-direction: column;
- flex: 1;
- align-items: center;
- }
+ .counter {
+ position: absolute;
+ bottom: 0;
+ width: 100%;
+ padding-left: 40px;
}
- .vote-table__header-blind {
- grid-area: blind1;
+ .option-item {
+ padding: 8px 4px;
}
- .vote-table__calendar-blind {
- grid-area: blind2;
+ .vote-item-wrapper.currentuser {
+ border: none;
}
- .vote-table__footer-blind {
- grid-area: blind3;
+ .vote-column {
+ flex-direction: row-reverse;
+ align-items: center;
+ max-width: initial;
+ position: relative;
+ border-top: solid 1px var(--color-border);
+ padding: 0;
}
.vote-table__votes {
- grid-area: vote;
+ align-items: stretch;
flex-direction: column;
-
- .vote-table__vote-row {
- flex-direction: row;
- order: 1;
- flex: 1;
- min-height: 45px;
-
- &.currentuser {
- order: 0;
- }
- }
}
.vote-table__users {
- grid-area: users;
- flex-direction: column;
- > .user-item {
- order: 1;
- min-height: 45px;
- &.currentuser {
- order: 0;
- }
- }
+ margin: 0
}
- .vote-table__footer {
- grid-area: footer;
+ .vote-table-header-item {
flex-direction: row;
-
- .vote-table-footer-item {
- display: flex;
- flex: 1;
- align-items: center;
- justify-content: center;
- }
- }
-
- // fixed column
- .fixed {
- background-color: var(--color-main-background);
- position: sticky;
- left: 0;
}
- // Bottom border for table rows
- .vote-table__users .user-item,
- .vote-table__header-blind,
- .vote-table__header > div,
- .vote-table__vote-row > div {
- border-bottom: 1px solid var(--color-border-dark);
- }
-
- .option-item {
- .option-item__option--datebox {
- min-width: 80px;
- }
- .option-item__option--text {
- hyphens: auto;
- text-align: center;
- align-items: center;
- // hack for the hyphens, because hyphenating works different
- // in different browsers and with different languages.
- min-width: 160px;
- }
+ .user-item.user.currentuser, .vote-item-wrapper.currentuser {
+ display: flex;
}
- &.closed .confirmed {
- margin-left: 8px;
- margin-right: 8px;
- font-weight: bold;
-
- &.vote-table-header-item {
- border-bottom: none !important;
- border-bottom-left-radius: 0;
- border-bottom-right-radius: 0;
- }
-
- &.calendar-peek {
- border-bottom: none !important;
- border-top: none !important;
- border-radius: 0;
- }
-
- &.vote-table-vote-item {
- border-top: none !important;
- border-bottom: none !important;
- border-radius: 0;
- }
-
- &.vote-table-footer-item {
- border-top: none !important;
- border-top-left-radius: 0;
- border-top-right-radius: 0;
- }
+ .user-item.user, .vote-item-wrapper {
+ display: none;
}
-
- // some little hacks
- .user-item {
- max-width: 280px;
+ .calendar-peek {
+ order: 0;
}
-
- @media (max-width: 576px) {
- .vote-table.desktop .user-item__name {
- display: none;
- }
+ .calendar-peek__conflict.icon {
+ width: 24px;
+ height: 24px;
}
+}
+.theme--dark .closed .confirmed {
+ background-color: var(--color-polls-background-yes--dark) !important;
}
</style>
diff --git a/src/js/components/VoteTable/VoteTableHeaderItem.vue b/src/js/components/VoteTable/VoteTableHeaderItem.vue
index 08c6cf92..35ef80dd 100644
--- a/src/js/components/VoteTable/VoteTableHeaderItem.vue
+++ b/src/js/components/VoteTable/VoteTableHeaderItem.vue
@@ -24,27 +24,18 @@
<div class="vote-table-header-item"
:class=" { winner: isWinner }">
<OptionItem :option="option" :display="optionStyle" />
- <Confirmation v-if="isConfirmed" :option="option" />
- <Counter v-else :show-maybe="Boolean(poll.allowMaybe)"
- :option="option"
- :counter-style="counterStyle"
- :show-no="showNo" />
</div>
</template>
<script>
import { mapState, mapGetters } from 'vuex'
import OptionItem from '../Options/OptionItem'
-import Counter from '../Base/Counter'
-import Confirmation from '../Base/Confirmation'
export default {
name: 'VoteTableHeaderItem',
components: {
OptionItem,
- Counter,
- Confirmation,
},
props: {
@@ -89,7 +80,7 @@ export default {
isWinner() {
// highlight best option until poll is closed and
// at least one option is confirmed
- return this.option.rank === 1 && !(this.closed && this.confirmedOptions.length)
+ return this.option.rank === 1 && this.option.yes && !(this.closed && this.confirmedOptions.length)
},
isConfirmed() {
@@ -117,22 +108,4 @@ export default {
height: 2em;
}
-.mobile {
- .vote-table-header-item {
- flex-direction: column;
- &.confirmed {
- flex-direction: row;
- }
- }
- .counter {
- order: 2;
- }
-
- .confirmation {
- background-position: left;
- order: 0;
- padding: 0 15px;
- }
-}
-
</style>
diff --git a/src/js/views/Vote.vue b/src/js/views/Vote.vue
index 14dba799..afaf400a 100644
--- a/src/js/views/Vote.vue
+++ b/src/js/views/Vote.vue
@@ -21,7 +21,7 @@
-->
<template>
- <AppContent :class="{ closed: closed }">
+ <AppContent :class="[{ closed: closed }, poll.type]">
<div class="header-actions">
<Actions>
<ActionButton :icon="sortIcon" @click="ranked = !ranked">
@@ -95,6 +95,7 @@
<script>
import axios from '@nextcloud/axios'
+import { showError } from '@nextcloud/dialogs'
import { generateUrl } from '@nextcloud/router'
import linkifyUrls from 'linkify-urls'
import { mapState, mapGetters } from 'vuex'
@@ -305,35 +306,6 @@ export default {
},
methods: {
- async watchPoll() {
- this.cancelToken = axios.CancelToken.source()
- let watching = true
- let lastUpdated = 0
- while (watching) {
- await axios.get(generateUrl('apps/polls/watch/' + this.$route.params.id + '?offset=' + lastUpdated), { cancelToken: this.cancelToken.token })
- .then((response) => {
- console.debug('update detected', response.data.updates)
- response.data.updates.forEach((item) => {
- lastUpdated = (item.updated > lastUpdated) ? item.updated : lastUpdated
- if (item.table === 'polls') {
- this.$store.dispatch('poll/get')
- } else {
- this.$store.dispatch('poll/' + item.table + '/list')
- }
- })
- })
- .catch((error) => {
- if (axios.isCancel(error)) {
- watching = false
- } else if (error?.response) {
- if (error.response.status !== 304) {
- console.error(error.response)
- }
- }
- })
- }
- },
-
openOptions() {
emit('toggle-sidebar', { open: true, activeTab: 'options' })
},
@@ -366,13 +338,43 @@ export default {
}
}
},
+ async watchPoll() {
+ this.cancelToken = axios.CancelToken.source()
+ let watching = true
+ let lastUpdated = 0
+ try {
+ while (watching) {
+ const response = await axios.get(generateUrl('apps/polls/watch/' + this.$route.params.id + '?offset=' + lastUpdated), { cancelToken: this.cancelToken.token })
+ console.debug('update detected', response.data.updates)
+ response.data.updates.forEach((item) => {
+ lastUpdated = (item.updated > lastUpdated) ? item.updated : lastUpdated
+ if (item.table === 'polls') {
+ this.$store.dispatch('poll/get')
+ } else {
+ this.$store.dispatch('poll/' + item.table + '/list')
+ }
+ })
+ watching = true
+ }
+ } catch (error) {
+ watching = false
+
+ if (!axios.isCancel(error)) {
+ if (error.response.status !== 304) {
+ showError(t('polls', 'Error retrieving updates from server, reload page'))
+ console.error(error.response)
+ }
+ }
+ }
+ },
},
}
+
</script>
<style lang="scss" scoped>
.description {
- white-space: pre;
+ white-space: pre-wrap;
}
.header-actions {