This is an automated email from the git hooks/post-receive script. New commit to branch develop in repository pollen. See https://gitlab.nuiton.org/chorem/pollen.git commit 22eebd62cc97bec7dc27eb325b2cb87aa10d3f3d Author: Sylvain Bavencoff <bavencoff@codelutin.com> Date: Tue Oct 24 14:36:00 2017 +0200 decoupage du composant de vote (ref #165) --- .../src/main/web/tag/poll/EditVote.tag.html | 425 ++++++++++++ .../src/main/web/tag/poll/Votes.tag.html | 720 +-------------------- .../src/main/web/tag/poll/VotesTable.tag.html | 346 ++++++++++ 3 files changed, 779 insertions(+), 712 deletions(-) diff --git a/pollen-ui-riot-js/src/main/web/tag/poll/EditVote.tag.html b/pollen-ui-riot-js/src/main/web/tag/poll/EditVote.tag.html new file mode 100644 index 00000000..831a0bee --- /dev/null +++ b/pollen-ui-riot-js/src/main/web/tag/poll/EditVote.tag.html @@ -0,0 +1,425 @@ +<EditVote> + + <div class="form-wrapper"> + <form id="voteForm" class="voter separator" ref="formAddVote"> + <HumanInput onsubmit="{voteInEdition ? updateVote : addVote}"></HumanInput> + <div class="header"> + <div class="current-voter separator-right"> + <div class="o-field o-field--icon-left o-field--icon-right" + if={poll.canVote || voteInEdition} > + <i class="fa fa-fw fa-user c-icon"></i> + <input class="c-field {c-field--error: !voteInEdition && error && error['voter.name']}" + type="text" + ref="voterName" + name="voterName" + required + maxlength="255" + tabindex="1" + placeholder={__.authorPlaceHolder} + value={poll.voterName}> + </div> + <div if={!poll.canVote && !voteInEdition} + class="choices-label"> + {__.choices} + </div> + </div> + <div if={poll.resultIsVisible} class="result-label"> + <strong>{__.results}</strong> + </div> + </div> + <div each={choice, index in poll.choices} class="choice separator-top"> + <div class="choice-vote separator-right"> + <div class="choice-value"> + <ChoiceView choice={choice} center="true"></ChoiceView> + </div> + <div class="current-choice" + if={poll.canVote || voteInEdition}> + <input if={pollTypeCheckbox} + class="check" + type="checkbox" + onchange="{onVoteChanged}" + ref="{choice.id}_voteValue" + tabindex="{10 * (index + 1)}"> + <input if={!pollTypeCheckbox && !pollTypeSelect} + class="text c-field {c-field--error: !voteInEdition && error && (error['vote.voteValue#' + choice.id] || error['vote.totalVoteValue'])}" + type="number" + required={!poll.voteCountingConfig.maxChoiceNumber} + min="{poll.voteCountingTypeValue && poll.voteCountingTypeValue.minimumValue}" + max="{poll.voteCountingTypeValue && poll.voteCountingTypeValue.maximumValue}" + onchange="{onVoteChanged}" + ref="{choice.id}_voteValue" + tabindex="{10 * (index + 1)}"> + <select if={pollTypeSelect} + class="c-field {c-field--error: !voteInEdition && error && error['vote.voteValue#' + choice.id]}" + required + onchange={onVoteChanged} + tabindex="{10 * (index + 1)}"> + <option value=""></option> + <option each={grad, index in poll.voteCountingConfig.grades} + value={index} + ref="{choice.id}_voteValue"> + {grad} + </option> + </select> + </div> + </div> + <div if={poll.resultIsVisible} class="score-choice"> + <span if={!choice.score}>{parent.__.noVote}</span> + <span if={choice.score}> + <i if="{choice.score.scoreOrder === 0}" class="fa fa-trophy fa-15x winner"></i> + <span if={!pollTypeSelect}> + {choice.score.scoreValue} + {parent.__["results_unit_" + poll.voteCountingType + "_" + (choice.score.scoreValue > 1 ? "many" : "one")]} + </span> + <span if={pollTypeSelect}> + {poll.voteCountingConfig.grades[choice.score.scoreValue]} + </span> + </span> + </div> + </div> + <div class="footer separator-top" if="{poll.choices && (poll.canVote || voteInEdition)}"> + <div class="current-voter-actions separator-right"> + <div class="current-voter-buttons"> + <button if={!voteInEdition} + class="c-button c-button--brand pull-right" + type="submit" + name="newVote" + tabindex="{(poll.choices.length + 1) * 10}" + disabled={tooManyChoicesSelected || voting}> + <i class="fa fa-envelope"></i> + {__.toVote} + </button> + <button if="{voteInEdition}" + class="c-button c-button--error" + type="button" + tabindex="{(poll.choices.length + 2) * 10}" + onclick="{cancelEditVote}" + disabled={voting}> + <i class="fa fa-remove"></i> + {__.cancelEdition} + </button> + <button if="{voteInEdition}" + class="c-button c-button--success" + tabindex="{(poll.choices.length + 1) * 10}" + disabled={tooManyChoicesSelected || voting} + type="submit"> + <i class="fa fa-check"></i> + {__.validateEdition} + </button> + </div> + <div class="c-hint--static c-hint--error" if="{tooManyChoicesSelected}"> + {__.tooManyChoicesSelected} {poll.voteCountingConfig.maxChoiceNumber} + </div> + <div class="c-hint--static c-hint--error" if="{error}"> + <div each={fields in error}> + {fields} + </div> + </div> + </div> + </div> + </form> + </div> + <script type="es6"> + this.loaded = false; + let session = require("../../js/Session"); + let Choice = require("../../js/Choice"); + this.moment = require("moment"); + this.installBundle(session, "poll_votes"); + this.tooManyChoicesSelected = false; + + this.voting = false; + + this.poll = require("../../js/Poll.js"); + this.poll.loadForVotes().then(() => { + this.update(); + }); + + this.onPollChange = poll => { + this.poll = poll; + this.pollTypeCheckbox = poll.voteCountingTypeValue && poll.voteCountingTypeValue.renderType === "checkbox"; + this.pollTypeSelect = poll.voteCountingTypeValue && poll.voteCountingTypeValue.renderType === "select"; + this.onVoteChanged(); + this.update(); + }; + + this.listen("poll", this.onPollChange); + this.listen("user", (user, oldUser) => { + if (user !== oldUser) { + if (user && this.refs.voterName && this.refs.voterName.value !== "") { + this.refs.voterName.value = user.name; + } + this.update(); + } + }); + + this.voteInEdition = null; + this.error = null; + + this.cancelEditVote = () => { + this.voteInEdition = null; + this.resetVoteForm(); + }; + + this.editVote = vote => { + this.voteInEdition = vote; + this.error = null; + this.update(); + this.refs.voterName.value = vote.voterName; + vote.choice.forEach(choice => { + let input = this.refs[choice.choiceId + "_voteValue"]; + if (input) { + if (this.poll.voteCountingTypeValue.renderType === "text") { + input.value = choice.voteValue; + } else if (this.poll.voteCountingTypeValue.renderType === "select") { + input.forEach(option => {option.selected = option.value === choice.voteValue + "";}); + } else { + input.checked = choice.voteValue === 1; + } + } + }); + if (this.poll.voteCountingTypeValue.renderType === "select") { + this.refs[this.poll.choices[0].id + "_voteValue"][0].parentNode.focus(); + } else { + this.refs[this.poll.choices[0].id + "_voteValue"].focus(); + } + }; + + this.getChoiceVoteValue = ref => { + let input = this.refs[ref]; + if (!input) { + return 0; + } + if (this.poll.voteCountingTypeValue.renderType === "text") { + return input.value; + } + if (this.poll.voteCountingTypeValue.renderType === "select") { + return input.find(option => option.selected).value; + } + return input.checked ? 1 : 0; + }; + + this.onVoteChanged = () => { + if (this.loaded && this.poll.voteCountingConfig.maxChoiceNumber) { + var selectedChoiceNb = 0; + this.poll.choices.forEach(c => { + var choiceValue = this.getChoiceVoteValue(c.id + "_voteValue"); + if (choiceValue && choiceValue !== "0") { + selectedChoiceNb++; + } + }); + this.tooManyChoicesSelected = selectedChoiceNb > this.poll.voteCountingConfig.maxChoiceNumber; + } else { + this.tooManyChoicesSelected = false; + } + }; + + this.resetVoteForm = () => { + if (this.poll.canVote) { + this.refs.voterName.value = null; + this.poll.choices.forEach(choice => { + let input = this.refs[choice.id + "_voteValue"]; + if (this.poll.voteCountingTypeValue.renderType === "text") { + input.value = ""; + } else if (this.poll.voteCountingTypeValue.renderType === "select") { + input.forEach(option => {option.selected = false;}); + } else { + input.checked = ""; + } + }); + } + this.selectedChoiceNb = 0; + this.error = null; + }; + + this.addVote = (e) => { + e.preventDefault(); + e.stopPropagation(); + + this.voting = true; + this.update(); + + let vote = { + id: null, + voterName: this.refs.voterName.value, + choice: [] + }; + + this.poll.choices.forEach(c => { + vote.choice.push({ + choiceId: c.id, + voteValue: this.getChoiceVoteValue(c.id + "_voteValue") + }); + }); + + this.poll.addVote(vote).then(() => { + this.resetVoteForm(); + if (this.poll.resultIsVisible) { + this.poll.loadResults().then(() => { + this.voting = false; + this.update(); + }, () => { + this.voting = false; + this.update(); + }); + } else { + this.voting = false; + this.update(); + } + }, + (error) => { + this.error = error; + this.voting = false; + this.update(); + }); + }; + + this.updateVote = e => { + e.preventDefault(); + e.stopPropagation(); + + this.voting = true; + this.update(); + + let updateVote = Object.assign({}, this.voteInEdition); // don't modify original vote + updateVote.voterName = this.refs.voterName.value; + + this.poll.choices.forEach(choice => { + let voteChoice = this.poll.getVoteChoice(updateVote, choice); + if (!voteChoice) { + voteChoice = { + choiceId: choice.id + }; + updateVote.choice.push(voteChoice); + } + voteChoice.voteValue = this.getChoiceVoteValue(choice.id + "_voteValue"); + }); + + this.poll.updateVote(updateVote).then(() => { + this.voteInEdition = null; + this.resetVoteForm(); + if (this.poll.resultIsVisible) { + this.poll.loadResults().then(() => { + this.voting = false; + this.update(); + }, () => { + this.voting = false; + this.update(); + }); + } else { + this.voting = false; + this.update(); + } + }, + (error) => { + this.voting = false; + this.error = error; + this.update(); + }); + }; + + </script> + <style> + + .form-wrapper { + display: flex; + flex-direction: row; + justify-content: center; + align-items: stretch; + } + + .voter { + display: flex; + flex-direction: column; + flex-wrap: nowrap; + margin-top: 20px; + justify-content: center; + align-items: stretch; + } + + .voter > div { + flex-grow: 0; + display: flex; + flex-direction: row; + min-height: 40px; + } + + .voter .header > * { + height: 40px; + text-align: center; + padding: 3px; + line-height: 35px; + } + + .voter .header .current-voter { + flex-grow: 1; + min-width: 200px; + } + + .voter .result-label, .voter .score-choice, .voter .results-actions { + width: 120px; + min-width: 120px; + flex-grow: 0; + text-align: center; + padding: 3px; + align-self: center; + } + + .voter .choice { + flex-wrap: nowrap; + align-items: stretch; + justify-content: center; + } + + .voter .choice .choice-vote { + display: flex; + flex-direction: row; + flex-wrap: nowrap; + align-items: center; + justify-content: center; + flex-grow: 1; + } + + .voter .choice .choice-value { + flex-grow: 1; + font-size: 1.2em; + min-width: 0; + padding: 5px; + } + + .voter .choice .current-choice { + display: flex; + justify-content: center; + min-width: 50px; + } + + .voter .current-voter-actions { + display: flex; + flex-direction: column; + flex-grow: 1; + } + + .voter .current-voter-buttons { + display: flex; + flex-direction: row; + padding: 5px; + justify-content: center; + align-items: center; + } + + .check { + width: 25px; + height: 25px; + } + + .text { + width: 60px; + height: 30px; + margin: 0 0 0 5px; + } + + .choice.c-field { + padding: 0 0 0 1px; + } + + </style> +</EditVote> diff --git a/pollen-ui-riot-js/src/main/web/tag/poll/Votes.tag.html b/pollen-ui-riot-js/src/main/web/tag/poll/Votes.tag.html index 195c0f10..2ad0b406 100644 --- a/pollen-ui-riot-js/src/main/web/tag/poll/Votes.tag.html +++ b/pollen-ui-riot-js/src/main/web/tag/poll/Votes.tag.html @@ -20,136 +20,17 @@ --> require("./Choice.tag.html"); require("./ChoiceView.tag.html"); -require("./Podium.tag.html"); +require("./EditVote.tag.html"); +require("./VotesTable.tag.html"); require("../components/HumanInput.tag.html"); require("../components/LazyLoad.tag.html"); require("../components/Avatar.tag.html"); <Votes> <div class="container" show="{loaded}"> - <!-- Form to vote --> - <div class="form-wrapper"> - <form id="voteForm" class="voter separator" ref="formAddVote"> - <HumanInput onsubmit="{voteInEdition ? updateVote : addVote}"></HumanInput> - <div class="header"> - <div class="current-voter separator-right"> - <div class="o-field o-field--icon-left o-field--icon-right" - if={poll.canVote || voteInEdition} > - <i class="fa fa-fw fa-user c-icon"></i> - <input class="c-field {c-field--error: !voteInEdition && error && error['voter.name']}" - type="text" - ref="voterName" - name="voterName" - required - maxlength="255" - tabindex="1" - placeholder={__.authorPlaceHolder} - value={poll.voterName}> - </div> - <div if={!poll.canVote && !voteInEdition} - class="choices-label"> - {__.choices} - </div> - </div> - <div if={poll.resultIsVisible} class="result-label"> - <strong>{__.results}</strong> - </div> - </div> - <div each={choice, index in poll.choices} class="choice separator-top"> - <div class="choice-vote separator-right"> - <div class="choice-value"> - <ChoiceView choice={choice} center="true"></ChoiceView> - </div> - <div class="current-choice" - if={poll.canVote || voteInEdition}> - <input if={pollTypeCheckbox} - class="check" - type="checkbox" - onchange="{onVoteChanged}" - ref="{choice.id}_voteValue" - tabindex="{10 * (index + 1)}"> - <input if={!pollTypeCheckbox && !pollTypeSelect} - class="text c-field {c-field--error: !voteInEdition && error && (error['vote.voteValue#' + choice.id] || error['vote.totalVoteValue'])}" - type="number" - required={!poll.voteCountingConfig.maxChoiceNumber} - min="{poll.voteCountingTypeValue && poll.voteCountingTypeValue.minimumValue}" - max="{poll.voteCountingTypeValue && poll.voteCountingTypeValue.maximumValue}" - onchange="{onVoteChanged}" - ref="{choice.id}_voteValue" - tabindex="{10 * (index + 1)}"> - <select if={pollTypeSelect} - class="c-field {c-field--error: !voteInEdition && error && error['vote.voteValue#' + choice.id]}" - required - onchange={onVoteChanged} - tabindex="{10 * (index + 1)}"> - <option value=""></option> - <option each={grad, index in poll.voteCountingConfig.grades} - value={index} - ref="{choice.id}_voteValue"> - {grad} - </option> - </select> - </div> - </div> - <div if={poll.resultIsVisible} class="score-choice"> - <span if={!choice.score}>{parent.__.noVote}</span> - <span if={choice.score}> - <i if="{choice.score.scoreOrder === 0}" class="fa fa-trophy fa-15x winner"></i> - <span if={!pollTypeSelect}> - {choice.score.scoreValue} - {parent.__["results_unit_" + poll.voteCountingType + "_" + (choice.score.scoreValue > 1 ? "many" : "one")]} - </span> - <span if={pollTypeSelect}> - {poll.voteCountingConfig.grades[choice.score.scoreValue]} - </span> - </span> - </div> - </div> - <div class="footer separator-top" if="{loaded && (poll.canVote || voteInEdition)}"> - <div class="current-voter-actions separator-right"> - <div class="current-voter-buttons"> - <button if={!voteInEdition} - class="c-button c-button--brand pull-right" - type="submit" - name="newVote" - tabindex="{(poll.choices.length + 1) * 10}" - disabled={tooManyChoicesSelected || voting}> - <i class="fa fa-envelope"></i> - {__.toVote} - </button> - <button if="{voteInEdition}" - class="c-button c-button--error" - type="button" - tabindex="{(poll.choices.length + 2) * 10}" - onclick="{cancelEditVote}" - disabled={voting}> - <i class="fa fa-remove"></i> - {__.cancelEdition} - </button> - <button if="{voteInEdition}" - class="c-button c-button--success" - tabindex="{(poll.choices.length + 1) * 10}" - disabled={tooManyChoicesSelected || voting} - type="submit"> - <i class="fa fa-check"></i> - {__.validateEdition} - </button> - </div> - <div class="c-hint--static c-hint--error" if="{tooManyChoicesSelected}"> - {__.tooManyChoicesSelected} {poll.voteCountingConfig.maxChoiceNumber} - </div> - <div class="c-hint--static c-hint--error" if="{error}"> - <div each={fields in error}> - {fields} - </div> - </div> - </div> - <div if={poll.resultIsVisible} class="results-actions"> + <EditVote ref="editVote"/> - </div> - </div> - </form> - </div> + <!-- Form to vote --> <p class="warning-label warning" if="{loaded && !poll.canVote}"> <span if="{moment().isBefore(poll.beginDate)}">{__.voteNotOpen}</span> <span if="{moment().isAfter(poll.endDate)}">{__.voteClosed}</span> @@ -178,335 +59,32 @@ require("../components/Avatar.tag.html"); </form> <!-- Show votes --> - <div class="voters" if="{poll.resultIsVisible && poll.voteCount > 0}"> - <div class="row header separator-bottom"> - <div></div> - <div class="choices separator-left" if="{showChoiceHeader}"> - <div each="{choice in poll.choices}" class="choice separator-top separator-right" - onmouseenter="{parent.showTooltip(null, choice)}" onclick="{parent.showTooltip(null, choice)}" onmouseleave="{parent.hideTooltip}"> - <ChoiceView choice={choice} center="true" hideReport="true"></ChoiceView> - </div> - </div> - </div> - <LazyLoad pagination={pagination} onload={lazyLoad} load-size="20" not-load-on-start="true" ref="lazyLoad" class="elements"> - <yield to="element"> - <div class="row separator-bottom separator-right"> - <div class="name separator-left"> - <Avatar class="voter-avatar" avatar={element.voterAvatar} name={element.voterName} rounded="true"/> - <div class="voter-name" onmouseenter="{parent.parent.showTooltip(element)}" onclick="{parent.parent.showTooltip(vote)}" onmouseleave="{parent.parent.hideTooltip}"> - <span if="{!element.anonymous || element.permission}">{element.voterName}</span> - <span if="{element.anonymous && !element.permission}" class="anonymous-voter">{parent.parent.__.anonymousVoter}</span> - </div> - <button type="button" - class="c-button c-button--rounded u-xsmall c-button--brand" - if="{!parent.parent.poll.closed && element.permission}" - disabled="{parent.parent.voteInEdition != null || parent.parent.voting}" - onclick="{parent.parent.onEditVote(element)}"> - <i class="fa fa-pencil-square-o"></i> - </button> - <button type="button" - class="c-button c-button--rounded u-xsmall c-button--error" - if="{!parent.parent.poll.closed && (parent.parent.poll.permission || element.permission)}" - disabled="{parent.parent.voteInEdition != null || parent.parent.voting}" - onclick="{parent.parent.deleteVote(element)}"> - <i class="fa fa-trash"></i> - </button> - </div> - <div class="results separator-left {'ignored': element.ignored}" ref="results{index}"> - <div each="{choice in parent.parent.poll.choices}" - class="result separator-right {'checkbox' : parent.parent.pollTypeCheckbox} {'selected' : parent.parent.pollTypeCheckbox && parent.parent.poll.getVoteValue(element, choice) == 1}" - onmouseenter="{parent.parent.parent.showTooltip(element, choice)}" onclick="{parent.parent.parent.showTooltip(element, choice)}" onmouseleave="{parent.parent.parent.hideTooltip}"> - <span if="{!pollTypeCheckbox}">{parent.parent.parent.poll.getVoteValue(element, choice)}</span> - </div> - </div> - </div> - </yield> - <yield to="loading"> - <div class="row separator-bottom separator-right"> - <div class="name separator-left"> - <div class="voter-name"> - <i class="fa fa-user-circle c-icon"></i> - <i class="fa fa-spinner fa-pulse"></i> - </div> - </div> - <div class="results separator-left" ref="results{index}"> - <div each="{choice in parent.poll.choices}" - class="result separator-right"> - <i class="fa fa-spinner fa-pulse"></i> - </div> - </div> - </div> - </yield> - </LazyLoad> - <div class="vote-tooltip" ref="voteTooltip" show="{voteTooltip.show}"> - <ChoiceView if="{voteTooltip.choice}" - choice="{voteTooltip.choice}" center="true" hideReport="true"></ChoiceView> - <div class="vote-value" if="{!pollTypeCheckbox && voteTooltip.vote && voteTooltip.choice}"> - {voteTooltip.vote.ignored ? __.ignored :poll.getVoteValue(voteTooltip.vote, voteTooltip.choice)}</div> - <div if="{voteTooltip.vote && !voteTooltip.choice}">{voteTooltip.vote.voterName}</div> - </div> - </div> + <VotesTable if="{poll.resultIsVisible && poll.voteCount > 0}" /> </div> <script type="es6"> - const MIN_CHOICE_COLUMN_WIDTH = 100; this.loaded = false; let session = require("../../js/Session"); let Choice = require("../../js/Choice"); this.moment = require("moment"); this.installBundle(session, "poll_votes"); - this.tooManyChoicesSelected = false; - this.showChoiceHeader = true; - this.voteTooltip = { - show: false, - choice: null, - vote: null - }; - this.voting = false; this.addingChoice = false; this.poll = require("../../js/Poll.js"); - this.poll.loadForVotes().then(() => { - this.refresh(); - this.update(); - }); - - this.pagination = { - order: "topiaCreateDate", - desc: true - }; - - this.lazyLoad = pagination => { - return this.poll.loadLazyVotes(pagination).then((result) => { - return result; - }); - }; - - this.refresh = () => { - this.refs.lazyLoad && this.refs.lazyLoad.reload().then(() => { - this.updateShowChoiceContainer(); - this.update(); - }, () => {}); - }; this.choiceToAdd = this.poll.initChoice(); - this.updateShowChoiceContainer = () => { - let results0 = this.refs.lazyLoad && this.refs.lazyLoad.refs.results0; - this.showChoiceHeader = results0 && this.poll.choices && results0.offsetWidth / this.poll.choices.length >= MIN_CHOICE_COLUMN_WIDTH; - }; - this.onPollChange = poll => { this.loaded = poll.choices !== undefined; this.poll = poll; - this.pollTypeCheckbox = poll.voteCountingTypeValue && poll.voteCountingTypeValue.renderType === "checkbox"; - this.pollTypeSelect = poll.voteCountingTypeValue && poll.voteCountingTypeValue.renderType === "select"; - this.choiceToAdd = this.poll.initChoice(this.choiceToAdd); - this.onVoteChanged(); - this.refresh(); this.update(); }; this.listen("poll", this.onPollChange); - this.listen("user", (user, oldUser) => { - if (user !== oldUser) { - this.refresh(); - this.update(); - } - }); - - this.voteInEdition = null; - this.error = null; - - this.cancelEditVote = () => { - this.voteInEdition = null; - this.resetPoll(); - }; - - this.onEditVote = (vote) => () => { - this.voteInEdition = vote; - this.error = null; - this.update(); - this.refs.voterName.value = vote.voterName; - vote.choice.forEach(choice => { - let input = this.refs[choice.choiceId + "_voteValue"]; - if (input) { - if (this.poll.voteCountingTypeValue.renderType === "text") { - input.value = choice.voteValue; - } else if (this.poll.voteCountingTypeValue.renderType === "select") { - input.forEach(option => {option.selected = option.value === choice.voteValue + "";}); - } else { - input.checked = choice.voteValue === 1; - } - } - }); - if (this.poll.voteCountingTypeValue.renderType === "select") { - this.refs[this.poll.choices[0].id + "_voteValue"][0].parentNode.focus(); - } else { - this.refs[this.poll.choices[0].id + "_voteValue"].focus(); - } - }; - - this.getChoiceVoteValue = ref => { - let input = this.refs[ref]; - if (!input) { - return 0; - } - if (this.poll.voteCountingTypeValue.renderType === "text") { - return input.value; - } - if (this.poll.voteCountingTypeValue.renderType === "select") { - return input.find(option => option.selected).value; - } - return input.checked ? 1 : 0; - }; - - this.onVoteChanged = () => { - if (this.loaded && this.poll.voteCountingConfig.maxChoiceNumber) { - var selectedChoiceNb = 0; - this.poll.choices.forEach(c => { - var choiceValue = this.getChoiceVoteValue(c.id + "_voteValue"); - if (choiceValue && choiceValue !== "0") { - selectedChoiceNb++; - } - }); - this.tooManyChoicesSelected = selectedChoiceNb > this.poll.voteCountingConfig.maxChoiceNumber; - } else { - this.tooManyChoicesSelected = false; - } - }; - this.resetPoll = () => { - if (this.poll.canVote) { - this.refs.voterName.value = null; - this.poll.choices.forEach(choice => { - let input = this.refs[choice.id + "_voteValue"]; - if (this.poll.voteCountingTypeValue.renderType === "text") { - input.value = ""; - } else if (this.poll.voteCountingTypeValue.renderType === "select") { - input.forEach(option => {option.selected = false;}); - } else { - input.checked = ""; - } - }); - } - this.selectedChoiceNb = 0; - this.error = null; - }; - - this.addVote = (e) => { - e.preventDefault(); - e.stopPropagation(); - - this.voting = true; - this.update(); - - let vote = { - id: null, - voterName: this.refs.voterName.value, - choice: [] - }; - - this.poll.choices.forEach(c => { - vote.choice.push({ - choiceId: c.id, - voteValue: this.getChoiceVoteValue(c.id + "_voteValue") - }); - }); - - this.poll.addVote(vote).then(() => { - this.resetPoll(); - if (this.poll.resultIsVisible) { - this.poll.loadResults().then(() => { - this.voting = false; - this.updateShowChoiceContainer(); - this.update(); - }, () => { - this.voting = false; - this.update(); - }); - } else { - this.voting = false; - this.update(); - } - }, - (error) => { - this.error = error; - this.voting = false; - this.update(); - }); - }; - - this.updateVote = e => { - e.preventDefault(); - e.stopPropagation(); - - this.voting = true; - this.update(); - - let updateVote = Object.assign({}, this.voteInEdition); // don't modify original vote - updateVote.voterName = this.refs.voterName.value; - - this.poll.choices.forEach(choice => { - let voteChoice = this.poll.getVoteChoice(updateVote, choice); - if (!voteChoice) { - voteChoice = { - choiceId: choice.id - }; - updateVote.choice.push(voteChoice); - } - voteChoice.voteValue = this.getChoiceVoteValue(choice.id + "_voteValue"); - }); - - this.poll.updateVote(updateVote).then(() => { - this.voteInEdition = null; - this.resetPoll(); - if (this.poll.voteIsVisible) { - this.refresh(); - } - if (this.poll.resultIsVisible) { - this.poll.loadResults().then(() => { - this.voting = false; - this.update(); - }, () => { - this.voting = false; - this.update(); - }); - } else { - this.voting = false; - this.update(); - } - }, - (error) => { - this.voting = false; - this.error = error; - this.update(); - }); - }; - - this.deleteVote = (vote) => (e) => { - e.preventDefault(); - e.stopPropagation(); - - this.confirm(this.__.delete).then((confirm) => { - if (confirm) { - this.voting = true; - this.update(); - - this.poll.deleteVote(vote).then(() => { - this.selectedChoiceNb = 0; - this.updateShowChoiceContainer(); - this.voting = false; - this.update(); - }, - (error) => { - this.voting = false; - this.error = error; - this.update(); - }); - } - }); + this.editVote = (vote) => () => { + this.refs.editVote.editVote(vote); }; this.addChoice = (e) => { @@ -529,146 +107,8 @@ require("../components/Avatar.tag.html"); }); }; - document.body.onresize = () => { - this.updateShowChoiceContainer(); - this.update(); - }; - - this.tooltipTimeout = null; - - this.showTooltip = (vote, choice) => (e) => { - e.preventDefault(); - e.stopPropagation(); - - this.tooltipTimeout = setTimeout(() => { - this.voteTooltip = { - choice: choice, - vote: vote, - show: choice || !vote || !vote.anonymous - }; - this.update(); - if (this.voteTooltip.show) { - let voteTooltip = this.refs.voteTooltip; - voteTooltip.style.top = (e.target.offsetTop + e.target.offsetHeight + 10) + "px"; - voteTooltip.style.left = (e.target.offsetLeft + (e.target.offsetWidth - voteTooltip.offsetWidth) / 2) + "px"; - } - }, 500); - }; - - this.hideTooltip = (e) => { - e.preventDefault(); - e.stopPropagation(); - clearTimeout(this.tooltipTimeout); - this.tooltipTimeout = null; - if (this.voteTooltip.show) { - this.voteTooltip = { - choice: null, - vote: null, - show: false - }; - this.update(); - } - }; - </script> - <style> - - .form-wrapper { - display: flex; - flex-direction: row; - justify-content: center; - align-items: stretch; - } - - .voter { - display: flex; - flex-direction: column; - flex-wrap: nowrap; - margin-top: 20px; - justify-content: center; - align-items: stretch; - } - - .voter > div { - flex-grow: 0; - display: flex; - flex-direction: row; - min-height: 40px; - } - - .voter .header > * { - height: 40px; - text-align: center; - padding: 3px; - line-height: 35px; - } - - .voter .header .current-voter { - flex-grow: 1; - min-width: 200px; - } - - .voter .result-label, .voter .score-choice, .voter .results-actions { - width: 120px; - min-width: 120px; - flex-grow: 0; - text-align: center; - padding: 3px; - align-self: center; - } - - .voter .choice { - flex-wrap: nowrap; - align-items: stretch; - justify-content: center; - } - - .voter .choice .choice-vote { - display: flex; - flex-direction: row; - flex-wrap: nowrap; - align-items: center; - justify-content: center; - flex-grow: 1; - } - - .voter .choice .choice-value { - flex-grow: 1; - font-size: 1.2em; - min-width: 0; - padding: 5px; - } - - .voter .choice .current-choice { - display: flex; - justify-content: center; - min-width: 50px; - } - - .voter .current-voter-actions { - display: flex; - flex-direction: column; - flex-grow: 1; - } - - .voter .current-voter-buttons { - display: flex; - flex-direction: row; - padding: 5px; - justify-content: center; - align-items: center; - } - - .check { - width: 25px; - height: 25px; - } - - .text { - width: 60px; - height: 30px; - margin: 0 0 0 5px; - } + <style> .choice.c-field { padding: 0 0 0 1px; @@ -678,150 +118,6 @@ require("../components/Avatar.tag.html"); margin-top: 20px; } - .voters { - display: flex; - flex-direction: column; - justify-content: center; - align-items: center; - margin-top: 50px; - } - - .voters .elements, - .voters .loading { - width: 100%; - display: flex; - flex-direction: column; - justify-content: center; - align-items: center; - } - - .voters .row { - display: flex; - width: 95%; - align-items: center; - } - - .voters .row > :first-child { - width: 200px; - min-width: 200px; - display: flex; - height: 2em; - padding: 0 5px; - } - - .voters .row:not(:first-child):hover { - border-bottom-color: var(--brand); - } - - .voters :not(.vote-tooltip) { - overflow: hidden; - } - - .voters .choice-view * { - white-space: nowrap; - text-overflow: ellipsis; - } - - .voters .voter-name { - flex-grow: 1; - padding: 5px; - white-space:nowrap; - text-overflow:ellipsis; - } - - .voters .voter-avatar { - width: 24px; - min-width: 24px; - height: 24px; - margin: 2px 0; - font-size: 0.85em; - } - - .voters .row:hover .name { - background-color: var(--vote-hover); - border-bottom-color: var(--brand); - } - - .voters .name button { - margin-left: 3px; - overflow: visible; - } - - .voters .results, - .voters .choices { - display: flex; - width: 100%; - } - - .voters .result, .voters .choice { - min-width: 15px; - width: 100%; - height: 2em; - padding: 5px; - text-align: center; - white-space:nowrap; - text-overflow:ellipsis; - } - - .voters .row .ignored { - background: repeating-linear-gradient( - 45deg, - transparent, - transparent 10px, - var(--hatching) 10px, - var(--hatching) 20px); - } - - .voters .row:hover { - background-color: var(--vote-hover); - } - - .voters .row:hover .ignored { - background: repeating-linear-gradient( - 45deg, - var(--vote-hover), - var(--vote-hover) 10px, - var(--hatching) 10px, - var(--hatching) 20px); - } - - .voters .result.checkbox { - background-color: var(--vote-not-selected); - } - - .voters .result.checkbox.selected { - background-color: var(--vote-selected); - } - - .voters .vote-tooltip { - position: absolute; - padding: 10px; - color: var(--tooltip-color); - background: var(--tooltip-background); - border-radius: 5px; - box-shadow: 0px 0px 10px var(--shadow); - opacity: 0.8; - text-align: center; - z-index: 9999; - } - - .voters .vote-tooltip:after { - content: ''; - position: absolute; - bottom: 100%; - left: 50%; - margin-left: -8px; - width: 0; height: 0; - border-bottom: 8px solid var(--tooltip-background); - border-right: 8px solid transparent; - border-left: 8px solid transparent; - } - - .voters .vote-tooltip .vote-value { - margin-top: 5px; - font-weight: bold; - } - .warning-label { text-align: center; margin-top: 1em; diff --git a/pollen-ui-riot-js/src/main/web/tag/poll/VotesTable.tag.html b/pollen-ui-riot-js/src/main/web/tag/poll/VotesTable.tag.html new file mode 100644 index 00000000..5352e09e --- /dev/null +++ b/pollen-ui-riot-js/src/main/web/tag/poll/VotesTable.tag.html @@ -0,0 +1,346 @@ +<VotesTable> + <!-- Show votes --> + <div class="voters"> + <div class="row header separator-bottom"> + <div></div> + <div class="choices separator-left" if="{showChoiceHeader}"> + <div each="{choice in poll.choices}" class="choice separator-top separator-right" + onmouseenter="{parent.showTooltip(null, choice)}" onclick="{parent.showTooltip(null, choice)}" onmouseleave="{parent.hideTooltip}"> + <ChoiceView choice={choice} center="true" hideReport="true"></ChoiceView> + </div> + </div> + </div> + <LazyLoad pagination={pagination} onload={lazyLoad} load-size="20" not-load-on-start="true" ref="lazyLoad" class="elements"> + <yield to="element"> + <div class="row separator-bottom separator-right"> + <div class="name separator-left"> + <Avatar class="voter-avatar" avatar={element.voterAvatar} name={element.voterName} rounded="true"/> + <div class="voter-name" onmouseenter="{parent.parent.showTooltip(element)}" onclick="{parent.parent.showTooltip(vote)}" onmouseleave="{parent.parent.hideTooltip}"> + <span if="{!element.anonymous || element.permission}">{element.voterName}</span> + <span if="{element.anonymous && !element.permission}" class="anonymous-voter">{parent.parent.__.anonymousVoter}</span> + </div> + <button type="button" + class="c-button c-button--rounded u-xsmall c-button--brand" + if="{!parent.parent.poll.closed && element.permission}" + disabled="{parent.parent.voteInEdition != null || parent.parent.voting}" + onclick="{parent.parent.editVote(element)}"> + <i class="fa fa-pencil-square-o"></i> + </button> + <button type="button" + class="c-button c-button--rounded u-xsmall c-button--error" + if="{!parent.parent.poll.closed && (parent.parent.poll.permission || element.permission)}" + disabled="{parent.parent.voteInEdition != null || parent.parent.voting}" + onclick="{parent.parent.deleteVote(element)}"> + <i class="fa fa-trash"></i> + </button> + </div> + <div class="results separator-left {'ignored': element.ignored}" ref="results{index}"> + <div each="{choice in parent.parent.poll.choices}" + class="result separator-right {'checkbox' : parent.parent.pollTypeCheckbox} {'selected' : parent.parent.pollTypeCheckbox && parent.parent.poll.getVoteValue(element, choice) == 1}" + onmouseenter="{parent.parent.parent.showTooltip(element, choice)}" onclick="{parent.parent.parent.showTooltip(element, choice)}" onmouseleave="{parent.parent.parent.hideTooltip}"> + <span if="{!pollTypeCheckbox}">{parent.parent.parent.poll.getVoteValue(element, choice)}</span> + </div> + </div> + </div> + </yield> + <yield to="loading"> + <div class="row separator-bottom separator-right"> + <div class="name separator-left"> + <div class="voter-name"> + <i class="fa fa-user-circle c-icon"></i> + <i class="fa fa-spinner fa-pulse"></i> + </div> + </div> + <div class="results separator-left" ref="results{index}"> + <div each="{choice in parent.poll.choices}" + class="result separator-right"> + <i class="fa fa-spinner fa-pulse"></i> + </div> + </div> + </div> + </yield> + </LazyLoad> + <div class="vote-tooltip" ref="voteTooltip" show="{voteTooltip.show}"> + <ChoiceView if="{voteTooltip.choice}" + choice="{voteTooltip.choice}" center="true" hideReport="true"></ChoiceView> + <div class="vote-value" if="{!pollTypeCheckbox && voteTooltip.vote && voteTooltip.choice}"> + {voteTooltip.vote.ignored ? __.ignored :poll.getVoteValue(voteTooltip.vote, voteTooltip.choice)}</div> + <div if="{voteTooltip.vote && !voteTooltip.choice}">{voteTooltip.vote.voterName}</div> + </div> + </div> + + <script type="es6"> + const MIN_CHOICE_COLUMN_WIDTH = 100; + let session = require("../../js/Session"); + let Choice = require("../../js/Choice"); + this.moment = require("moment"); + this.installBundle(session, "poll_votes"); + this.showChoiceHeader = true; + this.voteTooltip = { + show: false, + choice: null, + vote: null + }; + + this.poll = require("../../js/Poll.js"); + + this.pagination = { + order: "topiaCreateDate", + desc: true + }; + + this.lazyLoad = pagination => { + return this.poll.loadLazyVotes(pagination).then((result) => { + return result; + }); + }; + + this.refresh = () => { + this.refs.lazyLoad && this.refs.lazyLoad.reload().then(() => { + this.updateShowChoiceContainer(); + this.update(); + }, () => {}); + }; + + this.updateShowChoiceContainer = () => { + let results0 = this.refs.lazyLoad && this.refs.lazyLoad.refs.results0; + this.showChoiceHeader = results0 && this.poll.choices && results0.offsetWidth / this.poll.choices.length >= MIN_CHOICE_COLUMN_WIDTH; + }; + + this.onPollChange = poll => { + this.poll = poll; + this.refresh(); + }; + + this.listen("poll", this.onPollChange); + this.listen("user", (user, oldUser) => { + if (user !== oldUser) { + this.refresh(); + this.update(); + } + }); + + this.editVote = (vote) => () => { + this.parent.refs.editVote.editVote(vote); + }; + + this.deleteVote = (vote) => (e) => { + e.preventDefault(); + e.stopPropagation(); + + this.confirm(this.__.delete).then((confirm) => { + if (confirm) { + this.voting = true; + this.update(); + + this.poll.deleteVote(vote).then(() => { + this.selectedChoiceNb = 0; + this.updateShowChoiceContainer(); + this.voting = false; + this.update(); + }, + (error) => { + this.voting = false; + this.error = error; + this.update(); + }); + } + }); + }; + + document.body.onresize = () => { + this.updateShowChoiceContainer(); + this.update(); + }; + + this.tooltipTimeout = null; + + this.showTooltip = (vote, choice) => (e) => { + e.preventDefault(); + e.stopPropagation(); + + this.tooltipTimeout = setTimeout(() => { + this.voteTooltip = { + choice: choice, + vote: vote, + show: choice || !vote || !vote.anonymous + }; + this.update(); + if (this.voteTooltip.show) { + let voteTooltip = this.refs.voteTooltip; + voteTooltip.style.top = (e.target.offsetTop + e.target.offsetHeight + 10) + "px"; + voteTooltip.style.left = (e.target.offsetLeft + (e.target.offsetWidth - voteTooltip.offsetWidth) / 2) + "px"; + } + }, 500); + }; + + this.hideTooltip = (e) => { + e.preventDefault(); + e.stopPropagation(); + clearTimeout(this.tooltipTimeout); + this.tooltipTimeout = null; + if (this.voteTooltip.show) { + this.voteTooltip = { + choice: null, + vote: null, + show: false + }; + this.update(); + } + }; + + </script> + <style> + + .voters { + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + margin-top: 50px; + } + + .voters .elements, + .voters .loading { + width: 100%; + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + } + + .voters .row { + display: flex; + width: 95%; + align-items: center; + } + + .voters .row > :first-child { + width: 200px; + min-width: 200px; + display: flex; + height: 2em; + padding: 0 5px; + } + + .voters .row:not(:first-child):hover { + border-bottom-color: var(--brand); + } + + .voters :not(.vote-tooltip) { + overflow: hidden; + } + + .voters .choice-view * { + white-space: nowrap; + text-overflow: ellipsis; + } + + .voters .voter-name { + flex-grow: 1; + padding: 5px; + white-space:nowrap; + text-overflow:ellipsis; + } + + .voters .voter-avatar { + width: 24px; + min-width: 24px; + height: 24px; + margin: 2px 0; + font-size: 0.85em; + } + + .voters .row:hover .name { + background-color: var(--vote-hover); + border-bottom-color: var(--brand); + } + + .voters .name button { + margin-left: 3px; + overflow: visible; + } + + .voters .results, + .voters .choices { + display: flex; + width: 100%; + } + + .voters .result, .voters .choice { + min-width: 15px; + width: 100%; + height: 2em; + padding: 5px; + text-align: center; + white-space:nowrap; + text-overflow:ellipsis; + } + + .voters .row .ignored { + background: repeating-linear-gradient( + 45deg, + transparent, + transparent 10px, + var(--hatching) 10px, + var(--hatching) 20px); + } + + .voters .row:hover { + background-color: var(--vote-hover); + } + + .voters .row:hover .ignored { + background: repeating-linear-gradient( + 45deg, + var(--vote-hover), + var(--vote-hover) 10px, + var(--hatching) 10px, + var(--hatching) 20px); + } + + .voters .result.checkbox { + background-color: var(--vote-not-selected); + } + + .voters .result.checkbox.selected { + background-color: var(--vote-selected); + } + + .voters .vote-tooltip { + position: absolute; + padding: 10px; + color: var(--tooltip-color); + background: var(--tooltip-background); + border-radius: 5px; + box-shadow: 0px 0px 10px var(--shadow); + opacity: 0.8; + text-align: center; + z-index: 9999; + } + + .voters .vote-tooltip:after { + content: ''; + position: absolute; + bottom: 100%; + left: 50%; + margin-left: -8px; + width: 0; height: 0; + border-bottom: 8px solid var(--tooltip-background); + border-right: 8px solid transparent; + border-left: 8px solid transparent; + } + + .voters .vote-tooltip .vote-value { + margin-top: 5px; + font-weight: bold; + } + + .warning-label { + text-align: center; + margin-top: 1em; + } + + </style> + +</VotesTable> -- To stop receiving notification emails like this one, please contact chorem.org SCM administrator <admin+scm@chorem.org>.