import { OptimisticAction } from "./OptimisticAction.js" ;
export class PollManager extends OptimisticAction {
constructor ( ) {
super ( ) ;
document . addEventListener ( "click" , ( event ) => this . onClick ( event ) ) ;
document . addEventListener ( "submit" , ( event ) => this . onSubmit ( event ) , true ) ;
}
onClick ( event ) {
const option = event . target . closest ( ".poll-option" ) ;
if ( option && ! option . disabled ) {
event . preventDefault ( ) ;
this . vote ( option ) ;
return ;
}
const toggle = event . target . closest ( "[data-poll-toggle]" ) ;
if ( toggle ) {
event . preventDefault ( ) ;
this . toggleBuilder ( toggle ) ;
return ;
}
const addOption = event . target . closest ( "[data-poll-add-option]" ) ;
if ( addOption ) {
event . preventDefault ( ) ;
this . addOption ( addOption ) ;
return ;
}
const removeOption = event . target . closest ( "[data-poll-remove-option]" ) ;
if ( removeOption ) {
event . preventDefault ( ) ;
this . removeOption ( removeOption ) ;
}
}
async vote ( option ) {
const poll = option . closest ( ".poll" ) ;
if ( ! poll ) {
return ;
}
const pollUid = poll . dataset . pollUid ;
const optionUid = option . dataset . optionUid ;
await this . submit ( ` /polls/ ${ pollUid } /vote ` , { option _uid : optionUid } , null , ( result ) => this . render ( poll , result ) ) ;
}
render ( poll , result ) {
const byUid = { } ;
( result . options || [ ] ) . forEach ( ( option ) => {
byUid [ option . uid ] = option ;
} ) ;
poll . querySelectorAll ( ".poll-option" ) . forEach ( ( button ) => {
const data = byUid [ button . dataset . optionUid ] ;
if ( ! data ) {
return ;
}
const chosen = result . my _choice === data . uid ;
button . classList . toggle ( "chosen" , chosen ) ;
button . setAttribute ( "aria-pressed" , chosen ? "true" : "false" ) ;
const bar = button . querySelector ( ".poll-option-bar" ) ;
if ( bar ) {
bar . style . width = ` ${ data . pct } % ` ;
}
const pct = button . querySelector ( ".poll-option-pct" ) ;
if ( pct ) {
pct . textContent = ` ${ data . pct } % ` ;
}
const check = button . querySelector ( ".poll-option-check" ) ;
if ( check ) {
check . hidden = ! chosen ;
}
} ) ;
const total = poll . querySelector ( ".poll-total" ) ;
if ( total ) {
total . textContent = ` ${ result . total } vote ${ result . total === 1 ? "" : "s" } ` ;
}
}
toggleBuilder ( toggle ) {
const form = toggle . closest ( "form" ) ;
const builder = form ? form . querySelector ( "[data-poll-builder]" ) : null ;
if ( ! builder ) {
return ;
}
const activating = builder . hidden ;
builder . hidden = ! activating ;
this . setPollEnabled ( builder , activating ) ;
const label = toggle . querySelector ( "[data-poll-toggle-label]" ) ;
if ( label ) {
label . textContent = activating ? "Remove poll" : "Add poll" ;
}
}
setPollEnabled ( builder , enabled ) {
builder . querySelectorAll ( "input[name='poll_question'], input[name='poll_options']" ) . forEach ( ( input ) => {
input . disabled = ! enabled ;
if ( ! enabled ) {
input . value = "" ;
}
} ) ;
const error = builder . querySelector ( "[data-poll-error]" ) ;
if ( error ) {
error . hidden = true ;
error . textContent = "" ;
}
}
addOption ( button ) {
const form = button . closest ( "form" ) ;
const container = form ? form . querySelector ( "[data-poll-options]" ) : null ;
if ( ! container ) {
return ;
}
const rows = container . querySelectorAll ( ".poll-builder-row" ) ;
if ( rows . length >= 6 ) {
return ;
}
const row = document . createElement ( "div" ) ;
row . className = "poll-builder-row" ;
const input = document . createElement ( "input" ) ;
input . type = "text" ;
input . name = "poll_options" ;
input . maxLength = 100 ;
input . placeholder = ` Option ${ rows . length + 1 } ` ;
const remove = document . createElement ( "button" ) ;
remove . type = "button" ;
remove . className = "poll-builder-remove" ;
remove . dataset . pollRemoveOption = "" ;
remove . setAttribute ( "aria-label" , "Remove option" ) ;
remove . textContent = "× " ;
row . appendChild ( input ) ;
row . appendChild ( remove ) ;
container . appendChild ( row ) ;
}
removeOption ( button ) {
const container = button . closest ( "[data-poll-options]" ) ;
if ( ! container ) {
return ;
}
if ( container . querySelectorAll ( ".poll-builder-row" ) . length <= 2 ) {
return ;
}
const row = button . closest ( ".poll-builder-row" ) ;
if ( row ) {
row . remove ( ) ;
}
}
onSubmit ( event ) {
const form = event . target ;
if ( ! form || typeof form . querySelector !== "function" ) {
return ;
}
const builder = form . querySelector ( "[data-poll-builder]" ) ;
if ( ! builder || builder . hidden ) {
return ;
}
const question = builder . querySelector ( "input[name='poll_question']" ) ;
const options = [ ... builder . querySelectorAll ( "input[name='poll_options']" ) ]
. map ( ( input ) => input . value . trim ( ) )
. filter ( Boolean ) ;
let problem = "" ;
if ( ! question || ! question . value . trim ( ) ) {
problem = "Add a poll question, or remove the poll." ;
} else if ( options . length < 2 ) {
problem = "Add at least two poll options, or remove the poll." ;
}
if ( problem ) {
event . preventDefault ( ) ;
event . stopImmediatePropagation ( ) ;
const error = builder . querySelector ( "[data-poll-error]" ) ;
if ( error ) {
error . textContent = problem ;
error . hidden = false ;
}
}
}
}