114 lines
3.3 KiB
JavaScript
114 lines
3.3 KiB
JavaScript
|
|
/**
|
||
|
|
* @fileoverview Vote Buttons Component for Rantii
|
||
|
|
* @author retoor <retoor@molodetz.nl>
|
||
|
|
* @description Upvote and downvote controls for rants and comments
|
||
|
|
* @keywords vote, upvote, downvote, score, rating
|
||
|
|
*/
|
||
|
|
|
||
|
|
import { BaseComponent } from './base-component.js';
|
||
|
|
|
||
|
|
class VoteButtons extends BaseComponent {
|
||
|
|
static get observedAttributes() {
|
||
|
|
return ['score', 'vote-state', 'type', 'item-id', 'disabled'];
|
||
|
|
}
|
||
|
|
|
||
|
|
init() {
|
||
|
|
this.render();
|
||
|
|
this.bindEvents();
|
||
|
|
}
|
||
|
|
|
||
|
|
render() {
|
||
|
|
const score = parseInt(this.getAttr('score') || '0', 10);
|
||
|
|
const voteState = parseInt(this.getAttr('vote-state') || '0', 10);
|
||
|
|
const disabled = this.hasAttr('disabled');
|
||
|
|
|
||
|
|
this.addClass('vote-buttons');
|
||
|
|
|
||
|
|
this.setHtml(`
|
||
|
|
<button class="vote-btn upvote ${voteState === 1 ? 'active' : ''}"
|
||
|
|
aria-label="Upvote"
|
||
|
|
${disabled ? 'disabled' : ''}>
|
||
|
|
<svg viewBox="0 0 24 24" width="20" height="20">
|
||
|
|
<path fill="currentColor" d="M7.41 15.41L12 10.83l4.59 4.58L18 14l-6-6-6 6z"/>
|
||
|
|
</svg>
|
||
|
|
</button>
|
||
|
|
<span class="vote-score ${voteState === 1 ? 'positive' : ''} ${voteState === -1 ? 'negative' : ''}">${score}</span>
|
||
|
|
<button class="vote-btn downvote ${voteState === -1 ? 'active' : ''}"
|
||
|
|
aria-label="Downvote"
|
||
|
|
${disabled ? 'disabled' : ''}>
|
||
|
|
<svg viewBox="0 0 24 24" width="20" height="20">
|
||
|
|
<path fill="currentColor" d="M7.41 8.59L12 13.17l4.59-4.58L18 10l-6 6-6-6z"/>
|
||
|
|
</svg>
|
||
|
|
</button>
|
||
|
|
`);
|
||
|
|
}
|
||
|
|
|
||
|
|
bindEvents() {
|
||
|
|
this.on(this, 'click', this.handleClick);
|
||
|
|
}
|
||
|
|
|
||
|
|
handleClick(e) {
|
||
|
|
const btn = e.target.closest('.vote-btn');
|
||
|
|
if (!btn || btn.disabled) return;
|
||
|
|
|
||
|
|
if (!this.isLoggedIn()) {
|
||
|
|
this.getRouter()?.goToLogin();
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
const currentState = parseInt(this.getAttr('vote-state') || '0', 10);
|
||
|
|
let newVote;
|
||
|
|
|
||
|
|
if (btn.classList.contains('upvote')) {
|
||
|
|
newVote = currentState === 1 ? 0 : 1;
|
||
|
|
} else if (btn.classList.contains('downvote')) {
|
||
|
|
newVote = currentState === -1 ? 0 : -1;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (newVote !== undefined) {
|
||
|
|
this.emit('vote', {
|
||
|
|
vote: newVote,
|
||
|
|
type: this.getAttr('type'),
|
||
|
|
itemId: this.getAttr('item-id')
|
||
|
|
});
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
setScore(score) {
|
||
|
|
this.setAttr('score', score.toString());
|
||
|
|
const scoreEl = this.$('.vote-score');
|
||
|
|
if (scoreEl) {
|
||
|
|
scoreEl.textContent = score;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
setVoteState(state) {
|
||
|
|
this.setAttr('vote-state', state.toString());
|
||
|
|
this.render();
|
||
|
|
}
|
||
|
|
|
||
|
|
updateVote(score, voteState) {
|
||
|
|
this.setAttr('score', score.toString());
|
||
|
|
this.setAttr('vote-state', voteState.toString());
|
||
|
|
this.render();
|
||
|
|
}
|
||
|
|
|
||
|
|
disable() {
|
||
|
|
this.setAttr('disabled', '');
|
||
|
|
this.render();
|
||
|
|
}
|
||
|
|
|
||
|
|
enable() {
|
||
|
|
this.removeAttribute('disabled');
|
||
|
|
this.render();
|
||
|
|
}
|
||
|
|
|
||
|
|
onAttributeChanged(name, oldValue, newValue) {
|
||
|
|
this.render();
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
customElements.define('vote-buttons', VoteButtons);
|
||
|
|
|
||
|
|
export { VoteButtons };
|