114 lines
3.3 KiB
JavaScript
Raw Normal View History

2025-12-04 20:29:35 +01:00
/**
* @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 };