@@ -318,265 +318,6 @@ struct OptionGroup{T}
318
318
options:: Vector{T}
319
319
end
320
320
321
- struct MultiSelect{T}
322
- options:: Observable {Vector{Union{T, OptionGroup{T}}}}
323
- selection:: Observable{Vector{T}}
324
- placeholder:: String
325
- multi:: Bool
326
- option_to_string:: Any
327
- class:: String
328
- id:: String
329
- function MultiSelect (_options, _selection= nothing ; T= Any, placeholder= " " , multi= true , option_to_string= repr, class= " " , id= " " )
330
- options = _options isa Observable ? _options : Observable {Vector{T}} (_options)
331
- selection = if isnothing (_selection)
332
- Observable (T[])
333
- elseif _selection isa Observable
334
- @assert _selection isa Observable{Vector{T}}
335
- _selection
336
- else
337
- Observable {Vector{T}} (_selection)
338
- end
339
- _class = multi ? " bonito-select2-multi" : " bonito-select2-single"
340
- if class != " "
341
- _class *= " " * class
342
- end
343
- if id == " "
344
- id = gendomid (" selectbox" )
345
- end
346
- new {T} (options, selection, placeholder, multi, option_to_string, _class, id)
347
- end
348
- end
349
-
350
- function Bonito. jsrender (session:: Session , multiselect:: MultiSelect )
351
- # jquery = Asset("https://cdn.jsdelivr.net/npm/[email protected] /dist/jquery.min.js")
352
- # select2_css = Asset("https://cdn.jsdelivr.net/npm/[email protected] /dist/css/select2.min.css")
353
- # select2_js = Asset("https://cdn.jsdelivr.net/npm/[email protected] /dist/js/select2.min.js")
354
-
355
- # generate internal observables of js representation of options and selection
356
- jsoptions = @lift options_to_jsoptions ($ (multiselect. options); option_to_string= multiselect. option_to_string)
357
- jsselection = Observable {Vector{Int}} ()
358
-
359
- onany (jsselection) do _jssel
360
- sel = jsselection_to_selection (multiselect. options[], _jssel)
361
- if sel != multiselect. selection[]
362
- multiselect. selection[] = sel
363
- end
364
- nothing
365
- end
366
-
367
- onany (multiselect. options, multiselect. selection; update= true ) do _opts, _sel
368
- if ! multiselect. multi && length (_sel) > 1
369
- deleteat! (_sel, firstindex (_sel)+ 1 : lastindex (_sel))
370
- end
371
- jssel = selection_to_jsselection (_opts, _sel)
372
- @debug " MS \" $(multiselect. placeholder) \" : New opts or sel triggers new jsselection:" _opts _sel jssel
373
-
374
- # filter out invalid selections
375
- if any (isnothing, jssel)
376
- invalid = findall (isnothing, jssel)
377
- deleteat! (_sel, invalid)
378
- filter! (! isnothing, jssel)
379
- end
380
-
381
- jsselection[] = jssel
382
- nothing
383
- end
384
-
385
- # Create a multi-select element
386
- style = Styles (
387
- " width" => " 100%" ,
388
- )
389
-
390
- select = DOM. select (
391
- DOM. option ();
392
- multiple = multiselect. multi,
393
- class= multiselect. class,
394
- # name = "states[]",
395
- style,
396
- id= multiselect. id,
397
- )
398
-
399
- # node fence see https://github.com/electron/electron/issues/254
400
- container = DOM. div (
401
- NODE_FENCE,
402
- JQUERY,
403
- SELECT2_JS,
404
- NODE_UNFENCE,
405
- SELECT2_CSS,
406
- select
407
- )
408
-
409
- jqdocument = Bonito. JSString (raw " $(document)" )
410
- jqselect = Bonito. JSString (raw " $('#" * multiselect. id * raw " ')" )
411
-
412
- esc = Bonito. JSString (raw " $" )
413
- js_onload = js """
414
- (select ) => {
415
- $ (jqdocument).ready (function (){
416
- $jqselect .select2 ({
417
- width: ' 100%' ,
418
- placeholder: $ (multiselect .placeholder )
419
- });
420
- });
421
-
422
- function array_equal (a1 , a2 ){
423
- return a1 .length === a2 .length &&
424
- a1 .every (function (val , i ) { return val == a2[i]})
425
- }
426
-
427
- function updateDisplayedOptions (new_options ) {
428
- const jq_select = $jqselect;
429
-
430
- // Clear previous options
431
- jq_select .empty ();
432
-
433
- // Loop through each group and create optgroups
434
- new_options .forEach (opt_or_group => {
435
- if (opt_or_group .options === undefined ) {
436
- let newOption = new Option (opt_or_group .text , opt_or_group .id , false , false );
437
- jq_select .append (newOption);
438
- } else {
439
- let jq_optgroup = $esc (' <optgroup>' , { label: opt_or_group .label });
440
-
441
- opt_or_group .options .forEach (option => {
442
- let newOption = new Option (option .text , option .id , false , false );
443
- jq_optgroup .append (newOption);
444
- });
445
-
446
- jq_select .append (jq_optgroup);
447
- }
448
- });
449
- }
450
- updateDisplayedOptions ($ (jsoptions).value );
451
- $ (jsoptions).on (updateDisplayedOptions);
452
-
453
- function updateDisplayedSelection (new_sel_nr ) {
454
- const jq_select = $jqselect
455
- const new_sel = new_sel_nr .map (String );
456
-
457
- jq_select .data (' preserved-order' , new_sel);
458
- jq_select .val (new_sel).trigger (' change' );
459
- select2_renderSelections ();
460
- }
461
- updateDisplayedSelection ($ (jsselection).value )
462
- $ (jsselection).on (updateDisplayedSelection)
463
-
464
- // Trigger update for Select2 to recognize new options
465
- $jqselect .trigger (' change' );
466
-
467
- // Don't reorder
468
- // https://github.com/select2/select2/issues/3106#issuecomment-333341636
469
- function select2_renderSelections (){
470
- jq_select2 = $jqselect
471
- const def_order = jq_select2 .val ();
472
- const pre_order = jq_select2 .data (' preserved-order' ) || [];
473
- const jq_tags = jq_select2 .next (' .select2-container' ).find (' li.select2-selection__choice' );
474
- const jq_tags_ul = jq_tags .first ().parent ()
475
-
476
- const new_order = pre_order .map (val => {
477
- return def_order .indexOf (val);
478
- });
479
-
480
- const sortedElements = new_order .map (i => jq_tags .eq (i));
481
- jq_tags_ul .append (sortedElements);
482
- // console.log("Rendered selection")
483
- }
484
- function selectionHandler (e ){
485
- const jq_select2 = $esc (this );
486
- const val = e .params .data .id ;
487
-
488
- let order;
489
- if ($ (multiselect .multi )){
490
- order = jq_select2 .data (' preserved-order' ) || [];
491
- } else {
492
- order = [];
493
- }
494
-
495
- switch (e .type ){
496
- case ' select2:select' :
497
- order[ order .length ] = val;
498
- break ;
499
- case ' select2:unselect' :
500
- let found_index = order .indexOf (val);
501
- if (found_index >= 0 )
502
- order .splice (found_index,1 );
503
- break ;
504
- }
505
-
506
- jq_select2 .data (' preserved-order' , order); // store it for later
507
- select2_renderSelections ();
508
-
509
- // notify julia about changed selection
510
- $ (jsselection).notify (order .map (Number ));
511
- }
512
- $jqselect .on (' select2:select select2:unselect' , selectionHandler);
513
-
514
- // prevent dropdown on unselect
515
- // https://github.com/select2/select2/issues/3209#issuecomment-149663474
516
- $jqselect .on (" select2:unselect" , function (evt ) {
517
- if (! evt .params .originalEvent ) {
518
- return ;
519
- }
520
- evt .params .originalEvent .stopPropagation ();
521
- });
522
- }
523
- """
524
- Bonito. onload (session, select, js_onload)
525
-
526
- return Bonito. jsrender (session, container)
527
- end
528
-
529
- function options_to_jsoptions (options; option_to_string= repr)
530
- jsoptions = []
531
- id = 1
532
- for option in options
533
- if option isa OptionGroup
534
- jssuboptions = []
535
- for suboption in option. options
536
- push! (jssuboptions, (;text= option_to_string (suboption), id= id))
537
- id += 1
538
- end
539
- push! (jsoptions, (;label= option. label, options= jssuboptions))
540
- else
541
- push! (jsoptions, (;text= option_to_string (option), id= id))
542
- id += 1
543
- end
544
- end
545
- jsoptions
546
- end
547
-
548
- jsselection_to_selection (options, jsselection) = _jsselection_to_selection .(Ref (options), jsselection)
549
- function _jsselection_to_selection (options, jsselection:: Int )
550
- id = 1
551
- for option in options
552
- if option isa OptionGroup
553
- for suboption in option. options
554
- id == jsselection && return suboption
555
- id += 1
556
- end
557
- else
558
- id == jsselection && return option
559
- id += 1
560
- end
561
- end
562
- end
563
-
564
- selection_to_jsselection (options, selection) = _selection_to_jsselection .(Ref (options), selection)
565
- function _selection_to_jsselection (options, selection)
566
- id = 1
567
- for option in options
568
- if option isa OptionGroup
569
- for suboption in option. options
570
- suboption == selection && return id
571
- id += 1
572
- end
573
- else
574
- option == selection && return id
575
- id += 1
576
- end
577
- end
578
- end
579
-
580
321
struct TomSelect{T}
581
322
options:: Observable {Vector{Union{T, OptionGroup{T}}}}
582
323
selection:: Observable{Vector{T}}
0 commit comments