1- import { inject , injectable , optional } from "inversify" ;
1+ import { inject , injectable , optional } from "inversify" ;
22import "./constraintMenu.css" ;
33import { AbstractUIExtension , IActionDispatcher , LocalModelSource , TYPES } from "sprotty" ;
4- import { ConstraintRegistry } from "./constraintRegistry" ;
4+ import { Constraint , ConstraintRegistry } from "./constraintRegistry" ;
55
66// Enable hover feature that is used to show validation errors.
77// Inline completions are enabled to allow autocompletion of keywords and inputs/label types/label values.
@@ -19,6 +19,7 @@ import { LabelTypeRegistry } from "../labels/labelTypeRegistry";
1919import { EditorModeController } from "../editorMode/editorModeController" ;
2020import { Switchable , ThemeManager } from "../settingsMenu/themeManager" ;
2121import { AnalyzeDiagramAction } from "../serialize/analyze" ;
22+ import { ChooseConstraintAction } from "./actions" ;
2223
2324@injectable ( )
2425export class ConstraintMenu extends AbstractUIExtension implements Switchable {
@@ -28,6 +29,7 @@ export class ConstraintMenu extends AbstractUIExtension implements Switchable {
2829 private editor ?: monaco . editor . IStandaloneCodeEditor ;
2930 private tree : AutoCompleteTree ;
3031 private forceReadOnly : boolean ;
32+ private optionsMenu ?: HTMLDivElement ;
3133
3234 constructor (
3335 @inject ( ConstraintRegistry ) private readonly constraintRegistry : ConstraintRegistry ,
@@ -72,6 +74,10 @@ export class ConstraintMenu extends AbstractUIExtension implements Switchable {
7274 </div>
7375 </label>
7476 ` ;
77+
78+ const title = containerElement . querySelector ( "#constraint-menu-expand-title" ) as HTMLElement ;
79+ title . appendChild ( this . buildOptionsButton ( ) ) ;
80+
7581 const accordionContent = document . createElement ( "div" ) ;
7682 accordionContent . classList . add ( "accordion-content" ) ;
7783 const contentDiv = document . createElement ( "div" ) ;
@@ -225,4 +231,105 @@ export class ConstraintMenu extends AbstractUIExtension implements Switchable {
225231 switchTheme ( useDark : boolean ) : void {
226232 this . editor ?. updateOptions ( { theme : useDark ? "vs-dark" : "vs" } ) ;
227233 }
234+
235+ private buildOptionsButton ( ) : HTMLElement {
236+ const btn = document . createElement ( "button" ) ;
237+ btn . id = "constraint-options-button" ;
238+ btn . title = "Filter…" ;
239+ btn . innerHTML = "⋮" ; // or insert a font-awesome icon
240+ btn . onclick = ( ) => this . toggleOptionsMenu ( ) ;
241+ return btn ;
242+ }
243+
244+ /** show or hide the menu, generate checkboxes on the fly */
245+ private toggleOptionsMenu ( ) : void {
246+ if ( this . optionsMenu ) {
247+ this . optionsMenu . remove ( ) ;
248+ this . optionsMenu = undefined ;
249+ return ;
250+ }
251+
252+ // 1) create container
253+ this . optionsMenu = document . createElement ( "div" ) ;
254+ this . optionsMenu . id = "constraint-options-menu" ;
255+
256+ // 2) add the “All constraints” checkbox at the top
257+ const allConstraints = document . createElement ( "label" ) ;
258+ allConstraints . classList . add ( "options-item" ) ;
259+
260+ const allCb = document . createElement ( "input" ) ;
261+ allCb . type = "checkbox" ;
262+ allCb . value = "ALL" ;
263+ // initially checked if no specific constraint is selected
264+ allCb . checked = this . constraintRegistry . getSelectedConstraints ( ) . includes ( "ALL" ) ;
265+
266+ allCb . onchange = ( ) => {
267+ if ( ! this . optionsMenu ) return ;
268+ if ( allCb . checked ) {
269+ // uncheck every other constraint-checkbox
270+ this . optionsMenu
271+ . querySelectorAll < HTMLInputElement > ( "input[type=checkbox]" )
272+ . forEach ( cb => {
273+ if ( cb !== allCb ) cb . checked = false ;
274+ } ) ;
275+ // dispatch with empty array to mean “all”
276+ this . dispatcher . dispatch (
277+ ChooseConstraintAction . create ( [ "ALL" ] )
278+ ) ;
279+ } else {
280+ this . dispatcher . dispatch (
281+ ChooseConstraintAction . create ( [ ] )
282+ ) ;
283+ }
284+
285+ } ;
286+
287+ allConstraints . appendChild ( allCb ) ;
288+ allConstraints . appendChild ( document . createTextNode ( "All constraints" ) ) ;
289+ this . optionsMenu . appendChild ( allConstraints ) ;
290+
291+ // 2) pull your dynamic items (replace with your real API)
292+ const items = this . constraintRegistry . getConstraintList ( ) ;
293+
294+ // 3) for each item build a checkbox
295+ items . forEach ( item => {
296+ const label = document . createElement ( "label" ) ;
297+ label . classList . add ( "options-item" ) ;
298+
299+ const cb = document . createElement ( "input" ) ;
300+ cb . type = "checkbox" ;
301+ cb . value = item . name ;
302+ cb . checked = this . constraintRegistry . getSelectedConstraints ( ) . includes ( cb . value ) ;
303+
304+ cb . onchange = ( ) => {
305+ if ( cb . checked ) allCb . checked = false ;
306+
307+ const selected = Array . from (
308+ this . optionsMenu ! . querySelectorAll < HTMLInputElement > ( "input[type=checkbox]:checked" )
309+ ) . map ( cb => cb . value ) ;
310+
311+ // dispatch your action with either an array or
312+ // a comma-joined string—whatever your action expects
313+ this . dispatcher . dispatch (
314+ ChooseConstraintAction . create ( selected )
315+ ) ;
316+ } ;
317+
318+ label . appendChild ( cb ) ;
319+ label . appendChild ( document . createTextNode ( item . name ) ) ;
320+ this . optionsMenu ! . appendChild ( label ) ;
321+ } ) ;
322+
323+ this . editorContainer . appendChild ( this . optionsMenu ) ;
324+
325+ // optional: click-outside handler
326+ const onClickOutside = ( e : MouseEvent ) => {
327+ if ( this . optionsMenu && ! this . optionsMenu . contains ( e . target as Node )
328+ && ! ( e . target as Element ) . matches ( "#constraint-options-button" ) ) {
329+ this . toggleOptionsMenu ( ) ;
330+ document . removeEventListener ( "click" , onClickOutside ) ;
331+ }
332+ } ;
333+ setTimeout ( ( ) => document . addEventListener ( "click" , onClickOutside ) , 0 ) ;
334+ }
228335}
0 commit comments