@@ -13,6 +13,8 @@ export default class SettingsGroup<T extends ConfigValue> {
1313 public mode : Mode ;
1414 public setCallback ?: ( ) => void ;
1515 public updateCallback ?: ( ) => void ;
16+ private elements : Element [ ] ;
17+
1618 constructor (
1719 configName : string ,
1820 configFunction : ( param : T , nosave ?: boolean ) => boolean ,
@@ -27,32 +29,48 @@ export default class SettingsGroup<T extends ConfigValue> {
2729 this . setCallback = setCallback ;
2830 this . updateCallback = updateCallback ;
2931
30- this . updateUI ( ) ;
31-
3232 if ( this . mode === "select" ) {
33- const selectElement = document . querySelector (
33+ const el = document . querySelector (
3434 `.pageSettings .section[data-config-name=${ this . configName } ] select`
3535 ) ;
36- selectElement ?. addEventListener ( "change" , ( e ) => {
36+
37+ if ( el === null ) {
38+ throw new Error ( `Failed to find select element for ${ configName } ` ) ;
39+ }
40+
41+ if ( el . hasAttribute ( "multiple" ) ) {
42+ throw new Error (
43+ "multi-select dropdowns not supported. Config: " + this . configName
44+ ) ;
45+ }
46+
47+ el . addEventListener ( "change" , ( e ) => {
3748 const target = $ ( e . target as HTMLSelectElement ) ;
3849 if ( target . hasClass ( "disabled" ) || target . hasClass ( "no-auto-handle" ) ) {
3950 return ;
4051 }
52+
4153 this . setValue ( target . val ( ) as T ) ;
4254 } ) ;
55+
56+ this . elements = [ el ] ;
4357 } else if ( this . mode === "button" ) {
44- $ ( ".pageSettings" ) . on (
45- "click" ,
46- `.section[data-config-name='${ this . configName } '] button` ,
47- ( e ) => {
48- const target = $ ( e . currentTarget ) ;
58+ const els = document . querySelectorAll ( `
59+ .pageSettings .section[data-config-name=${ this . configName } ] button` ) ;
60+
61+ if ( els . length === 0 ) {
62+ throw new Error ( `Failed to find a button element for ${ configName } ` ) ;
63+ }
64+
65+ for ( const button of els ) {
66+ button . addEventListener ( "click" , ( e ) => {
4967 if (
50- target . hasClass ( "disabled" ) ||
51- target . hasClass ( "no-auto-handle" )
68+ button . classList . contains ( "disabled" ) ||
69+ button . classList . contains ( "no-auto-handle" )
5270 ) {
5371 return ;
5472 }
55- const value = target . attr ( ` data-config-value` ) ;
73+ const value = button . getAttribute ( " data-config-value" ) ;
5674 if ( value === undefined || value === "" ) {
5775 console . error (
5876 `Failed to handle settings button click for ${ configName } : data-${ configName } is missing or empty.`
@@ -67,82 +85,105 @@ export default class SettingsGroup<T extends ConfigValue> {
6785 if ( typed === "true" ) typed = true as T ;
6886 if ( typed === "false" ) typed = false as T ;
6987 this . setValue ( typed ) ;
70- }
71- ) ;
88+ } ) ;
89+ }
90+
91+ this . elements = Array . from ( els ) ;
7292 } else if ( this . mode === "range" ) {
73- const rangeElement = document . querySelector (
93+ const el = document . querySelector (
7494 `.pageSettings .section[data-config-name=${ this . configName } ] input[type=range]`
7595 ) ;
7696
77- if ( ! rangeElement ) {
78- Notifications . add ( `Failed to find range element for ${ configName } ` , - 1 ) ;
79- return ;
97+ if ( el === null ) {
98+ throw new Error ( `Failed to find range element for ${ configName } ` ) ;
8099 }
81100
82101 const debounced = debounce < ( val : T ) => void > ( 250 , ( val ) => {
83102 this . setValue ( val ) ;
84103 } ) ;
85104
86- rangeElement . addEventListener ( "input" , ( e ) => {
87- const target = $ ( e . target as HTMLInputElement ) ;
88- if ( target . hasClass ( "disabled" ) || target . hasClass ( "no-auto-handle" ) ) {
105+ el . addEventListener ( "input" , ( e ) => {
106+ if (
107+ el . classList . contains ( "disabled" ) ||
108+ el . classList . contains ( "no-auto-handle" )
109+ ) {
89110 return ;
90111 }
91- const val = parseFloat ( target . val ( ) as string ) as unknown as T ;
112+ const val = parseFloat ( ( el as HTMLInputElement ) . value ) as unknown as T ;
92113 this . updateUI ( val ) ;
93114 debounced ( val ) ;
94115 } ) ;
116+
117+ this . elements = [ el ] ;
118+ } else {
119+ this . elements = [ ] ;
95120 }
121+
122+ if ( this . elements . length === 0 || this . elements === undefined ) {
123+ throw new Error (
124+ `Failed to find elements for ${ configName } with mode ${ mode } `
125+ ) ;
126+ }
127+
128+ this . updateUI ( ) ;
96129 }
97130
98131 setValue ( value : T ) : void {
132+ if ( Config [ this . configName as keyof typeof Config ] === value ) {
133+ return ;
134+ }
99135 this . configFunction ( value ) ;
100136 this . updateUI ( ) ;
101137 if ( this . setCallback ) this . setCallback ( ) ;
102138 }
103139
104140 updateUI ( valueOverride ?: T ) : void {
105- this . configValue =
141+ const newValue =
106142 valueOverride ?? ( Config [ this . configName as keyof typeof Config ] as T ) ;
107- $ (
108- `.pageSettings .section[data-config-name='${ this . configName } '] button`
109- ) . removeClass ( "active" ) ;
110- if ( this . mode === "select" ) {
111- const select = document . querySelector < HTMLSelectElement > (
112- `.pageSettings .section[data-config-name='${ this . configName } '] select`
113- ) ;
114143
115- if ( select === null ) {
144+ if ( this . mode === "select" ) {
145+ const select = this . elements ?. [ 0 ] as HTMLSelectElement | null | undefined ;
146+ if ( ! select ) {
116147 return ;
117148 }
118149
119- select . value = this . configValue as string ;
120-
121150 //@ts -expect-error this is fine, slimselect adds slim to the element
122151 const ss = select . slim as SlimSelect | undefined ;
123- ss ?. store . setSelectedBy ( "value" , [ this . configValue as string ] ) ;
124- ss ?. render . renderValues ( ) ;
125- ss ?. render . renderOptions ( ss . store . getData ( ) ) ;
152+ if ( ss !== undefined ) {
153+ const currentSelected = ss . getSelected ( ) [ 0 ] ?? null ;
154+ if ( newValue !== currentSelected ) {
155+ ss . setSelected ( newValue as string ) ;
156+ }
157+ } else {
158+ if ( select . value !== newValue ) select . value = newValue as string ;
159+ }
126160 } else if ( this . mode === "button" ) {
127- $ (
128- // this cant be an object?
129- // eslint-disable-next-line @typescript-eslint/no-base-to-string
130- `.pageSettings .section[data-config-name='${ this . configName } '] button[data-config-value='${ this . configValue } ']`
131- ) . addClass ( "active" ) ;
161+ for ( const button of this . elements ) {
162+ let value = button . getAttribute ( "data-config-value" ) ;
163+
164+ let typed = value as T ;
165+ if ( typed === "true" ) typed = true as T ;
166+ if ( typed === "false" ) typed = false as T ;
167+
168+ if ( typed !== newValue ) {
169+ button . classList . remove ( "active" ) ;
170+ } else {
171+ button . classList . add ( "active" ) ;
172+ }
173+ }
132174 } else if ( this . mode === "range" ) {
133- const range = document . querySelector < HTMLInputElement > (
134- `.pageSettings .section[data-config-name='${ this . configName } '] input[type=range]`
135- ) ;
175+ const range = this . elements ?. [ 0 ] as HTMLInputElement | null | undefined ;
176+
136177 const rangeValue = document . querySelector (
137178 `.pageSettings .section[data-config-name='${ this . configName } '] .value`
138179 ) ;
139180
140- if ( range === null || rangeValue === null ) {
181+ if ( range === undefined || range === null || rangeValue === null ) {
141182 return ;
142183 }
143184
144- range . value = this . configValue as unknown as string ;
145- rangeValue . textContent = `${ ( this . configValue as number ) . toFixed ( 1 ) } ` ;
185+ range . value = newValue as unknown as string ;
186+ rangeValue . textContent = `${ ( newValue as number ) . toFixed ( 1 ) } ` ;
146187 }
147188 if ( this . updateCallback ) this . updateCallback ( ) ;
148189 }
0 commit comments