2025-10-06 09:38:54 -04:00
( function ( window ) {
2016-08-07 09:17:49 +01:00
"use strict" ;
2025-10-06 09:38:54 -04:00
var Modules = window . NotifyModules ;
2019-05-22 13:11:48 +01:00
2025-10-28 17:42:25 -04:00
const escapeHtml = ( unsafe ) => {
if ( ! unsafe ) return '' ;
return String ( unsafe )
. replace ( /&/g , "&" )
. replace ( /</g , "<" )
. replace ( />/g , ">" )
. replace ( /"/g , """ )
. replace ( /'/g , "'" ) ;
} ;
2025-07-31 12:47:40 -04:00
// Template functions for rendering component states
let renderStates = {
'initial' : function ( data ) {
return `
$ { data . showNowAsDefault ? `
< div class = "radio-select__column margin-y-2" >
< div class = "usa-radio" >
2025-10-28 17:42:25 -04:00
< input class = "usa-radio__input" checked = "checked" id = "${escapeHtml(data.name)}-0" name = "${escapeHtml(data.name)}" type = "radio" value = "" >
< label class = "usa-radio__label" for = "${escapeHtml(data.name)}-0" > Now < / l a b e l >
2025-07-31 12:47:40 -04:00
< / d i v >
2020-07-16 10:49:21 +01:00
< / d i v >
2025-07-31 12:47:40 -04:00
` : ''}
2023-09-15 12:26:31 -04:00
< div class = "radio-select__column margin-y-2" >
2025-07-31 12:47:40 -04:00
$ { data . categories . map ( category =>
2025-10-28 17:42:25 -04:00
` <input type='button' class='usa-button usa-button--outline radio-select__button--category' aria-expanded="false" value=' ${ escapeHtml ( category ) } ' /> `
2025-07-31 12:47:40 -04:00
) . join ( '' ) }
2016-12-19 10:36:17 +00:00
< / d i v >
2025-07-31 12:47:40 -04:00
` ;
} ,
'choose' : function ( data ) {
return `
$ { data . showNowAsDefault ? `
< div class = "radio-select__column margin-y-2" >
< div class = "usa-radio" >
2025-10-28 17:42:25 -04:00
< input class = "usa-radio__input" checked = "checked" id = "${escapeHtml(data.name)}-0" name = "${escapeHtml(data.name)}" type = "radio" value = "" >
< label class = "usa-radio__label" for = "${escapeHtml(data.name)}-0" > Now < / l a b e l >
2025-07-31 12:47:40 -04:00
< / d i v >
2016-12-19 10:36:17 +00:00
< / d i v >
2025-07-31 12:47:40 -04:00
` : ''}
2023-09-15 12:26:31 -04:00
< div class = "radio-select__column margin-y-2" >
2025-07-31 12:47:40 -04:00
$ { data . choices . map ( choice => `
< div class = "usa-radio js-option" >
2025-10-28 17:42:25 -04:00
< input class = "usa-radio__input" type = "radio" value = "${escapeHtml(choice.value)}" id = "${escapeHtml(choice.id)}" name = "${escapeHtml(data.name)}" / >
< label class = "usa-radio__label" for = "${escapeHtml(choice.id)}" > $ { escapeHtml ( choice . label ) } < / l a b e l >
2025-07-31 12:47:40 -04:00
< / d i v >
` ).join('')}
< input type = 'button' class = 'usa-button usa-button--outline radio-select__button--done margin-top-4' aria - expanded = 'true' value = 'Back to select a new time' / >
2016-12-19 10:36:17 +00:00
< / d i v >
2025-07-31 12:47:40 -04:00
` ;
} ,
'chosen' : function ( data ) {
return `
$ { data . showNowAsDefault ? `
< div class = "radio-select__column margin-y-2" >
< div class = "usa-radio" >
2025-10-28 17:42:25 -04:00
< input class = "usa-radio__input" id = "${escapeHtml(data.name)}-0" name = "${escapeHtml(data.name)}" type = "radio" value = "" >
< label class = "usa-radio__label" for = "${escapeHtml(data.name)}-0" > Now < / l a b e l >
2025-07-31 12:47:40 -04:00
< / d i v >
2016-12-19 10:36:17 +00:00
< / d i v >
2025-07-31 12:47:40 -04:00
` : ''}
< div class = "radio-select__column margin-y-2" >
$ { data . choices . map ( choice => `
< div class = "usa-radio" >
2025-10-28 17:42:25 -04:00
< input class = "usa-radio__input" checked = "checked" type = "radio" value = "${escapeHtml(choice.value)}" id = "${escapeHtml(choice.id)}" name = "${escapeHtml(data.name)}" / >
< label class = "usa-radio__label" for = "${escapeHtml(choice.id)}" > $ { escapeHtml ( choice . label ) } < / l a b e l >
2025-07-31 12:47:40 -04:00
< / d i v >
` ).join('')}
< / d i v >
< div class = "radio-select__column margin-y-2" >
< input type = 'button' class = 'usa-button usa-button--outline radio-select__button--reset' aria - expanded = 'false' value = 'Choose a different time' / >
< / d i v >
` ;
}
2016-10-11 14:17:29 +01:00
} ;
2016-08-07 09:17:49 +01:00
2021-01-20 11:23:01 +00:00
let shiftFocus = function ( elementToFocus , component ) {
2025-10-21 16:38:35 -04:00
const radios = component . querySelectorAll ( '[type=radio]' ) ;
2021-01-20 11:23:01 +00:00
// The first option is always the default
2025-10-21 16:38:35 -04:00
if ( elementToFocus === 'default' && radios [ 0 ] ) {
radios [ 0 ] . focus ( ) ;
2021-01-19 14:12:29 +00:00
}
2025-10-21 16:38:35 -04:00
if ( elementToFocus === 'option' && radios [ 1 ] ) {
radios [ 1 ] . focus ( ) ;
2021-01-19 14:12:29 +00:00
}
2016-10-11 14:17:29 +01:00
} ;
2016-08-07 09:17:49 +01:00
2025-10-06 09:38:54 -04:00
Modules [ 'radio-select' ] = function ( ) {
2016-08-07 09:17:49 +01:00
this . start = function ( component ) {
2016-12-19 10:36:17 +00:00
let render = ( state , data ) => {
2025-10-21 16:38:35 -04:00
component . innerHTML = renderStates [ state ] ( data ) ;
2016-12-19 10:36:17 +00:00
} ;
2021-01-20 11:23:01 +00:00
// store array of all options in component
2025-10-21 16:38:35 -04:00
let choices = Array . from ( component . querySelectorAll ( 'label' ) ) . map ( function ( element ) {
2016-10-11 14:17:29 +01:00
return {
2025-10-21 16:38:35 -04:00
'id' : element . htmlFor ,
'label' : element . textContent . trim ( ) ,
'value' : element . previousElementSibling . value
2016-10-11 14:17:29 +01:00
} ;
2016-08-07 09:17:49 +01:00
} ) ;
2025-10-21 16:38:35 -04:00
let categories = component . dataset . categories . split ( ',' ) ;
let name = component . querySelector ( 'input' ) . name ;
2019-07-01 11:48:09 +01:00
let mousedownOption = null ;
2020-07-16 10:49:21 +01:00
let showNowAsDefault = (
2025-10-21 16:38:35 -04:00
component . dataset . showNowAsDefault === 'true' ?
2020-07-16 10:49:21 +01:00
{ 'name' : name } : false
) ;
2021-01-20 11:23:01 +00:00
// functions for changing the state of the component's HTML
2019-07-01 11:48:09 +01:00
const reset = ( ) => {
2019-07-08 10:42:54 +01:00
render ( 'initial' , {
'categories' : categories ,
2020-07-16 10:49:21 +01:00
'name' : name ,
'showNowAsDefault' : showNowAsDefault
2019-07-08 10:42:54 +01:00
} ) ;
2021-01-20 11:23:01 +00:00
shiftFocus ( 'default' , component ) ;
2019-07-08 10:42:54 +01:00
} ;
2019-07-01 11:48:09 +01:00
const selectOption = ( value ) => {
render ( 'chosen' , {
'choices' : choices . filter (
element => element . value == value
) ,
2020-07-16 10:49:21 +01:00
'name' : name ,
'showNowAsDefault' : showNowAsDefault
2019-07-01 11:48:09 +01:00
} ) ;
2021-01-20 11:23:01 +00:00
shiftFocus ( 'option' , component ) ;
2019-07-01 11:48:09 +01:00
} ;
2021-01-20 11:23:01 +00:00
// use mousedown + mouseup event sequence to confirm option selection
2019-07-01 11:48:09 +01:00
const trackMouseup = ( event ) => {
const parentNode = event . target . parentNode ;
if ( parentNode === mousedownOption ) {
2025-10-21 16:38:35 -04:00
const input = parentNode . querySelector ( 'input' ) ;
const value = input ? input . value : '' ;
2019-07-01 11:48:09 +01:00
selectOption ( value ) ;
// clear tracking
mousedownOption = null ;
2025-10-21 16:38:35 -04:00
document . removeEventListener ( 'mouseup' , trackMouseup ) ;
2019-07-01 11:48:09 +01:00
}
} ;
2016-10-11 14:17:29 +01:00
2025-10-21 16:38:35 -04:00
// set events using event delegation
component . addEventListener ( 'click' , function ( event ) {
// Handle category button clicks
if ( event . target . classList . contains ( 'radio-select__button--category' ) ) {
2016-10-11 14:17:29 +01:00
event . preventDefault ( ) ;
2025-10-21 16:38:35 -04:00
let wordsInDay = event . target . value . split ( ' ' ) ;
2016-10-11 17:59:09 +01:00
let day = wordsInDay [ wordsInDay . length - 1 ] . toLowerCase ( ) ;
2016-10-11 14:17:29 +01:00
render ( 'choose' , {
'choices' : choices . filter (
2016-10-11 17:59:09 +01:00
element => element . label . toLowerCase ( ) . indexOf ( day ) > - 1
2016-10-11 14:17:29 +01:00
) ,
2020-07-16 19:27:33 +01:00
'name' : name ,
'showNowAsDefault' : showNowAsDefault
2016-10-11 14:17:29 +01:00
} ) ;
2021-01-20 11:23:01 +00:00
shiftFocus ( 'option' , component ) ;
2025-10-21 16:38:35 -04:00
}
2016-10-11 14:17:29 +01:00
2025-10-21 16:38:35 -04:00
// Handle done button clicks
if ( event . target . classList . contains ( 'radio-select__button--done' ) ) {
2016-10-11 14:17:29 +01:00
event . preventDefault ( ) ;
2025-10-21 16:38:35 -04:00
let selection = event . target . parentNode . querySelector ( 'input[type=radio]:checked' ) ;
if ( selection ) {
2019-07-08 10:42:54 +01:00
render ( 'chosen' , {
'choices' : choices . filter (
2025-10-21 16:38:35 -04:00
element => element . value == selection . value
2019-07-08 10:42:54 +01:00
) ,
2020-07-16 10:49:21 +01:00
'name' : name ,
'showNowAsDefault' : showNowAsDefault
2019-07-08 10:42:54 +01:00
} ) ;
2021-01-20 11:23:01 +00:00
shiftFocus ( 'option' , component ) ;
2019-07-08 10:42:54 +01:00
} else {
reset ( ) ;
2021-01-20 11:23:01 +00:00
shiftFocus ( 'default' , component ) ;
2019-07-08 10:42:54 +01:00
}
2025-10-21 16:38:35 -04:00
}
2016-10-11 14:17:29 +01:00
2025-10-21 16:38:35 -04:00
// Handle reset button clicks
if ( event . target . classList . contains ( 'radio-select__button--reset' ) ) {
2019-07-08 10:42:54 +01:00
event . preventDefault ( ) ;
reset ( ) ;
2021-01-20 11:23:01 +00:00
shiftFocus ( 'default' , component ) ;
2025-10-21 16:38:35 -04:00
}
} ) ;
2019-07-08 10:42:54 +01:00
2025-10-21 16:38:35 -04:00
component . addEventListener ( 'mousedown' , function ( event ) {
// Handle option mousedown
const option = event . target . closest ( '.js-option' ) ;
if ( option ) {
mousedownOption = option ;
// mouseup on the same option completes the click action
document . addEventListener ( 'mouseup' , trackMouseup ) ;
}
} ) ;
component . addEventListener ( 'keydown' , function ( event ) {
// Handle radio keydown (space and enter)
if ( event . target . type === 'radio' ) {
// allow keypresses which aren't enter or space through
if ( event . which !== 13 && event . which !== 32 ) {
return true ;
}
event . preventDefault ( ) ;
let value = event . target . value ;
selectOption ( value ) ;
}
} ) ;
2016-10-11 14:17:29 +01:00
2021-01-20 11:23:01 +00:00
// set HTML to initial state
2016-10-11 14:17:29 +01:00
render ( 'initial' , {
'categories' : categories ,
2020-07-16 10:49:21 +01:00
'name' : name ,
'showNowAsDefault' : showNowAsDefault
2016-08-07 09:17:49 +01:00
} ) ;
2025-10-21 16:38:35 -04:00
component . style . height = 'auto' ;
2016-10-11 18:11:20 +01:00
2016-08-07 09:17:49 +01:00
} ;
} ;
2019-05-22 13:11:48 +01:00
} ) ( window ) ;