@@ -30,6 +30,8 @@ export class ConstraintMenu extends AbstractUIExtension implements Switchable {
3030 private tree : AutoCompleteTree ;
3131 private forceReadOnly : boolean ;
3232 private optionsMenu ?: HTMLDivElement ;
33+ private ignoreCheckboxChange = false ;
34+ private hasOpenedOptionsMenu = false ;
3335
3436 constructor (
3537 @inject ( ConstraintRegistry ) private readonly constraintRegistry : ConstraintRegistry ,
@@ -233,7 +235,7 @@ export class ConstraintMenu extends AbstractUIExtension implements Switchable {
233235 const btn = document . createElement ( "button" ) ;
234236 btn . id = "constraint-options-button" ;
235237 btn . title = "Filter…" ;
236- btn . innerHTML = "⋮" ; // or insert a font-awesome icon
238+ btn . innerHTML = '<span class="codicon codicon-kebab-vertical"></span>' ;
237239 btn . onclick = ( ) => this . toggleOptionsMenu ( ) ;
238240 return btn ;
239241 }
@@ -257,28 +259,39 @@ export class ConstraintMenu extends AbstractUIExtension implements Switchable {
257259 const allCb = document . createElement ( "input" ) ;
258260 allCb . type = "checkbox" ;
259261 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 ) ) ;
262266
263267 allCb . onchange = ( ) => {
264268 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 ;
274287 }
275288 } ;
276289
277290 allConstraints . appendChild ( allCb ) ;
278291 allConstraints . appendChild ( document . createTextNode ( "All constraints" ) ) ;
279292 this . optionsMenu . appendChild ( allConstraints ) ;
280293
281- // 2) pull your dynamic items (replace with your real API)
294+ // 2) pull your dynamic items
282295 const items = this . constraintRegistry . getConstraintList ( ) ;
283296
284297 // 3) for each item build a checkbox
@@ -292,14 +305,14 @@ export class ConstraintMenu extends AbstractUIExtension implements Switchable {
292305 cb . checked = this . constraintRegistry . getSelectedConstraints ( ) . includes ( cb . value ) ;
293306
294307 cb . onchange = ( ) => {
295- if ( cb . checked ) allCb . checked = false ;
308+ if ( this . ignoreCheckboxChange ) return ;
296309
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 ) ;
300315
301- // dispatch your action with either an array or
302- // a comma-joined string—whatever your action expects
303316 this . dispatcher . dispatch ( ChooseConstraintAction . create ( selected ) ) ;
304317 } ;
305318
@@ -312,15 +325,16 @@ export class ConstraintMenu extends AbstractUIExtension implements Switchable {
312325
313326 // optional: click-outside handler
314327 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 ) ;
323337 } ;
324- setTimeout ( ( ) => document . addEventListener ( "click" , onClickOutside ) , 0 ) ;
338+ document . addEventListener ( "click" , onClickOutside ) ;
325339 }
326340}
0 commit comments