@@ -30,6 +30,8 @@ export class ConstraintMenu extends AbstractUIExtension implements Switchable {
30
30
private tree : AutoCompleteTree ;
31
31
private forceReadOnly : boolean ;
32
32
private optionsMenu ?: HTMLDivElement ;
33
+ private ignoreCheckboxChange = false ;
34
+ private hasOpenedOptionsMenu = false ;
33
35
34
36
constructor (
35
37
@inject ( ConstraintRegistry ) private readonly constraintRegistry : ConstraintRegistry ,
@@ -233,7 +235,7 @@ export class ConstraintMenu extends AbstractUIExtension implements Switchable {
233
235
const btn = document . createElement ( "button" ) ;
234
236
btn . id = "constraint-options-button" ;
235
237
btn . title = "Filter…" ;
236
- btn . innerHTML = "⋮" ; // or insert a font-awesome icon
238
+ btn . innerHTML = '<span class="codicon codicon-kebab-vertical"></span>' ;
237
239
btn . onclick = ( ) => this . toggleOptionsMenu ( ) ;
238
240
return btn ;
239
241
}
@@ -257,28 +259,39 @@ export class ConstraintMenu extends AbstractUIExtension implements Switchable {
257
259
const allCb = document . createElement ( "input" ) ;
258
260
allCb . type = "checkbox" ;
259
261
allCb . value = "ALL" ;
260
- // initially checked if no specific constraint is selected
261
- allCb . checked = this . constraintRegistry . getSelectedConstraints ( ) . includes ( "ALL" ) ;
262
+ allCb . checked = this . constraintRegistry
263
+ . getConstraintList ( )
264
+ . map ( ( c ) => c . name )
265
+ . every ( ( c ) => this . constraintRegistry . getSelectedConstraints ( ) . includes ( c ) ) ;
262
266
263
267
allCb . onchange = ( ) => {
264
268
if ( ! this . optionsMenu ) return ;
265
- if ( allCb . checked ) {
266
- // uncheck every other constraint-checkbox
267
- this . optionsMenu . querySelectorAll < HTMLInputElement > ( "input[type=checkbox]" ) . forEach ( ( cb ) => {
268
- if ( cb !== allCb ) cb . checked = false ;
269
- } ) ;
270
- // dispatch with empty array to mean “all”
271
- this . dispatcher . dispatch ( ChooseConstraintAction . create ( [ "ALL" ] ) ) ;
272
- } else {
273
- this . dispatcher . dispatch ( ChooseConstraintAction . create ( [ ] ) ) ;
269
+
270
+ this . ignoreCheckboxChange = true ;
271
+ try {
272
+ if ( allCb . checked ) {
273
+ this . optionsMenu . querySelectorAll < HTMLInputElement > ( "input[type=checkbox]" ) . forEach ( ( cb ) => {
274
+ if ( cb !== allCb ) cb . checked = true ;
275
+ } ) ;
276
+ this . dispatcher . dispatch (
277
+ ChooseConstraintAction . create ( this . constraintRegistry . getConstraintList ( ) . map ( ( c ) => c . name ) ) ,
278
+ ) ;
279
+ } else {
280
+ this . optionsMenu . querySelectorAll < HTMLInputElement > ( "input[type=checkbox]" ) . forEach ( ( cb ) => {
281
+ if ( cb !== allCb ) cb . checked = false ;
282
+ } ) ;
283
+ this . dispatcher . dispatch ( ChooseConstraintAction . create ( [ ] ) ) ;
284
+ }
285
+ } finally {
286
+ this . ignoreCheckboxChange = false ;
274
287
}
275
288
} ;
276
289
277
290
allConstraints . appendChild ( allCb ) ;
278
291
allConstraints . appendChild ( document . createTextNode ( "All constraints" ) ) ;
279
292
this . optionsMenu . appendChild ( allConstraints ) ;
280
293
281
- // 2) pull your dynamic items (replace with your real API)
294
+ // 2) pull your dynamic items
282
295
const items = this . constraintRegistry . getConstraintList ( ) ;
283
296
284
297
// 3) for each item build a checkbox
@@ -292,14 +305,14 @@ export class ConstraintMenu extends AbstractUIExtension implements Switchable {
292
305
cb . checked = this . constraintRegistry . getSelectedConstraints ( ) . includes ( cb . value ) ;
293
306
294
307
cb . onchange = ( ) => {
295
- if ( cb . checked ) allCb . checked = false ;
308
+ if ( this . ignoreCheckboxChange ) return ;
296
309
297
- const selected = Array . from (
298
- this . optionsMenu ! . querySelectorAll < HTMLInputElement > ( "input[type=checkbox]:checked" ) ,
299
- ) . map ( ( cb ) => cb . value ) ;
310
+ const checkboxes = this . optionsMenu ! . querySelectorAll < HTMLInputElement > ( "input[type=checkbox]" ) ;
311
+ const individualCheckboxes = Array . from ( checkboxes ) . filter ( ( cb ) => cb !== allCb ) ;
312
+ const selected = individualCheckboxes . filter ( ( cb ) => cb . checked ) . map ( ( cb ) => cb . value ) ;
313
+
314
+ allCb . checked = individualCheckboxes . every ( ( cb ) => cb . checked ) ;
300
315
301
- // dispatch your action with either an array or
302
- // a comma-joined string—whatever your action expects
303
316
this . dispatcher . dispatch ( ChooseConstraintAction . create ( selected ) ) ;
304
317
} ;
305
318
@@ -312,15 +325,16 @@ export class ConstraintMenu extends AbstractUIExtension implements Switchable {
312
325
313
326
// optional: click-outside handler
314
327
const onClickOutside = ( e : MouseEvent ) => {
315
- if (
316
- this . optionsMenu &&
317
- ! this . optionsMenu . contains ( e . target as Node ) &&
318
- ! ( e . target as Element ) . matches ( "#constraint-options-button" )
319
- ) {
320
- this . toggleOptionsMenu ( ) ;
321
- document . removeEventListener ( "click" , onClickOutside ) ;
322
- }
328
+ const target = e . target as Node ;
329
+ if ( ! this . optionsMenu || this . optionsMenu . contains ( target ) ) return ;
330
+
331
+ const button = document . getElementById ( "constraint-options-button" ) ;
332
+ if ( button && button . contains ( target ) ) return ;
333
+
334
+ this . optionsMenu . remove ( ) ;
335
+ this . optionsMenu = undefined ;
336
+ document . removeEventListener ( "click" , onClickOutside ) ;
323
337
} ;
324
- setTimeout ( ( ) => document . addEventListener ( "click" , onClickOutside ) , 0 ) ;
338
+ document . addEventListener ( "click" , onClickOutside ) ;
325
339
}
326
340
}
0 commit comments