131131 color : var (--crit ); padding : 10px ; border-radius : 6px ;
132132 margin-bottom : 16px ; display : none;
133133 }
134+ .isp-dropdown { position : relative; }
135+ .isp-selected {
136+ display : flex; align-items : center; gap : 8px ;
137+ background : var (--input-bg ); border : 1px solid var (--input-border );
138+ border-radius : 4px ; padding : 8px 10px ; cursor : pointer;
139+ font-size : 0.95em ; font-family : inherit; color : var (--text );
140+ }
141+ .isp-selected : hover { border-color : var (--accent ); }
142+ .isp-selected .isp-arrow { margin-left : auto; font-size : 0.8em ; color : var (--muted ); }
143+ .isp-options {
144+ display : none; position : absolute; top : 100% ; left : 0 ; right : 0 ;
145+ background : var (--input-bg ); border : 1px solid var (--input-border );
146+ border-radius : 4px ; margin-top : 2px ; z-index : 50 ;
147+ max-height : 240px ; overflow-y : auto;
148+ }
149+ .isp-options .open { display : block; }
150+ .isp-option {
151+ display : flex; align-items : center; gap : 8px ;
152+ padding : 8px 10px ; cursor : pointer; font-size : 0.95em ;
153+ }
154+ .isp-option : hover { background : var (--card ); }
155+ .isp-badge {
156+ display : inline-block; width : 10px ; height : 10px ;
157+ border-radius : 3px ; flex-shrink : 0 ;
158+ }
134159 @media (max-width : 600px ) { .form-grid { grid-template-columns : 1fr ; } }
135160 </ style >
136161</ head >
@@ -212,16 +237,28 @@ <h2>{{ t.mqtt_broker }} <span style="font-size:0.75em; font-weight:normal; color
212237 < h2 > {{ t.general }}</ h2 >
213238 < div class ="form-grid ">
214239 < div class ="form-row ">
215- < label for ="isp_select "> {{ t.isp_name }}</ label >
216- < div style ="display:flex;align-items:center;gap:8px; ">
217- < select id ="isp_select " name ="isp_select " onchange ="onIspChange() " style ="flex:1; ">
218- < option value =""> {{ t.isp_select }}</ option >
240+ < label > {{ t.isp_name }}</ label >
241+ < div class ="isp-dropdown " id ="isp-dropdown ">
242+ < div class ="isp-selected " id ="isp-selected " onclick ="toggleIspDropdown() ">
243+ < span class ="isp-badge " id ="isp-sel-badge "> </ span >
244+ < span class ="isp-text " id ="isp-sel-text "> {{ t.isp_select }}</ span >
245+ < span class ="isp-arrow "> ▾</ span >
246+ </ div >
247+ < div class ="isp-options " id ="isp-options ">
248+ < div class ="isp-option " data-value ="" onclick ="selectIsp(this) ">
249+ < span class ="isp-text "> {{ t.isp_select }}</ span >
250+ </ div >
219251 {% for isp in t.isp_options %}
220- < option value ="{{ isp }} " {% if config.isp_name == isp %}selected{% endif %} > {{ isp }}</ option >
252+ < div class ="isp-option " data-value ="{{ isp }} " onclick ="selectIsp(this) ">
253+ {% if isp in isp_colors %}< span class ="isp-badge " style ="background:{{ isp_colors[isp] }} "> </ span > {% endif %}
254+ < span class ="isp-text "> {{ isp }}</ span >
255+ </ div >
221256 {% endfor %}
222- < option value ="__other__ " {% if config.isp_name and config.isp_name not in t.isp_options %}selected{% endif %} > {{ t.isp_other }}</ option >
223- </ select >
224- < span id ="isp-dot " style ="width:12px;height:12px;border-radius:50%;display:none;flex-shrink:0; "> </ span >
257+ < div class ="isp-option " data-value ="__other__ " onclick ="selectIsp(this) ">
258+ < span class ="isp-text "> {{ t.isp_other }}</ span >
259+ </ div >
260+ </ div >
261+ < input type ="hidden " id ="isp_value " value ="{{ config.isp_name or '' }} ">
225262 </ div >
226263 < span class ="hint "> {{ t.isp_hint }}</ span >
227264 </ div >
@@ -283,16 +320,47 @@ <h2>{{ t.general }}</h2>
283320} ) ( ) ;
284321
285322var ISP_COLORS = { { isp_colors| tojson } } ;
286- function onIspChange ( ) {
287- var sel = document . getElementById ( 'isp_select' ) ;
288- var row = document . getElementById ( 'isp-other-row' ) ;
289- row . style . display = sel . value === '__other__' ? 'flex' : 'none' ;
290- var dot = document . getElementById ( 'isp-dot' ) ;
291- var color = ISP_COLORS [ sel . value ] ;
292- dot . style . display = color ? 'inline-block' : 'none' ;
293- dot . style . background = color || 'transparent' ;
323+ var ispOpen = false ;
324+
325+ function toggleIspDropdown ( ) {
326+ ispOpen = ! ispOpen ;
327+ document . getElementById ( 'isp-options' ) . classList . toggle ( 'open' , ispOpen ) ;
328+ }
329+
330+ function selectIsp ( el ) {
331+ var val = el . getAttribute ( 'data-value' ) ;
332+ document . getElementById ( 'isp_value' ) . value = val ;
333+ var badge = document . getElementById ( 'isp-sel-badge' ) ;
334+ var text = document . getElementById ( 'isp-sel-text' ) ;
335+ var color = ISP_COLORS [ val ] ;
336+ badge . style . background = color || 'transparent' ;
337+ badge . style . display = color ? 'inline-block' : 'none' ;
338+ text . textContent = el . querySelector ( '.isp-text' ) . textContent ;
339+ ispOpen = false ;
340+ document . getElementById ( 'isp-options' ) . classList . remove ( 'open' ) ;
341+ document . getElementById ( 'isp-other-row' ) . style . display = val === '__other__' ? 'flex' : 'none' ;
294342}
295- onIspChange ( ) ;
343+
344+ document . addEventListener ( 'click' , function ( e ) {
345+ if ( ! e . target . closest ( '.isp-dropdown' ) ) {
346+ ispOpen = false ;
347+ document . getElementById ( 'isp-options' ) . classList . remove ( 'open' ) ;
348+ }
349+ } ) ;
350+
351+ ( function ( ) {
352+ var val = document . getElementById ( 'isp_value' ) . value ;
353+ if ( ! val ) return ;
354+ var opts = document . querySelectorAll ( '#isp-options .isp-option' ) ;
355+ var found = false ;
356+ for ( var i = 0 ; i < opts . length ; i ++ ) {
357+ if ( opts [ i ] . getAttribute ( 'data-value' ) === val ) { selectIsp ( opts [ i ] ) ; found = true ; break ; }
358+ }
359+ if ( ! found ) {
360+ var other = document . querySelector ( '#isp-options .isp-option[data-value="__other__"]' ) ;
361+ selectIsp ( other ) ;
362+ }
363+ } ) ( ) ;
296364
297365function showToast ( msg , ok ) {
298366 var el = document . getElementById ( 'toast' ) ;
@@ -308,18 +376,18 @@ <h2>{{ t.general }}</h2>
308376function getFormData ( ) {
309377 var form = document . getElementById ( 'settings-form' ) ;
310378 var data = { } ;
311- form . querySelectorAll ( 'input:not(#theme-check):not(#isp_other_input), select :not(#isp_select) ' ) . forEach ( function ( inp ) {
379+ form . querySelectorAll ( 'input:not(#theme-check):not(#isp_other_input):not(#isp_value), select ' ) . forEach ( function ( inp ) {
312380 if ( SECRET_FIELDS . indexOf ( inp . name ) !== - 1 ) {
313381 data [ inp . name ] = inp . value || MASK ;
314382 } else {
315383 data [ inp . name ] = inp . value ;
316384 }
317385 } ) ;
318- var ispSel = document . getElementById ( 'isp_select' ) ;
319- if ( ispSel . value === '__other__' ) {
386+ var ispVal = document . getElementById ( 'isp_value' ) . value ;
387+ if ( ispVal === '__other__' ) {
320388 data . isp_name = document . getElementById ( 'isp_other_input' ) . value ;
321389 } else {
322- data . isp_name = ispSel . value ;
390+ data . isp_name = ispVal ;
323391 }
324392 data . theme = themeCheck . checked ? 'dark' : 'light' ;
325393 return data ;
0 commit comments