|
10 | 10 | DENSITY = { |
11 | 11 | "adjust-layout": "compact", |
12 | 12 | "adjust-databounds": "default", |
13 | | - "select-slice-time": "default", |
| 13 | + "select-slice-time": "compact", |
14 | 14 | "animation-controls": "compact", |
15 | 15 | } |
16 | 16 |
|
17 | 17 | SIZES = { |
18 | 18 | "adjust-layout": 49, |
19 | 19 | "adjust-databounds": 65, |
20 | | - "select-slice-time": 70, |
| 20 | + "select-slice-time": 49, |
21 | 21 | "animation-controls": 49, |
22 | 22 | } |
23 | 23 |
|
@@ -447,102 +447,99 @@ def __init__(self): |
447 | 447 | ) |
448 | 448 | super().__init__(**style) |
449 | 449 |
|
| 450 | + self.state.setdefault("expanded_slice_track", None) |
| 451 | + |
450 | 452 | with self: |
451 | | - with v3.VTooltip( |
452 | | - text=( |
453 | | - "slice_slider_edit ? 'Toggle to text edit' : 'Toggle to slider edit'", |
454 | | - ), |
455 | | - ): |
456 | | - with v3.Template(v_slot_activator="{ props }"): |
457 | | - v3.VIcon( |
458 | | - "mdi-tune-variant", |
459 | | - v_bind="props", |
460 | | - classes="ml-3 mr-2 opacity-50", |
461 | | - click="slice_slider_edit = !slice_slider_edit", |
462 | | - ) |
| 453 | + v3.VIcon("mdi-tune-variant", classes="ml-3 mr-2 opacity-50") |
463 | 454 |
|
464 | | - with v3.VRow( |
465 | | - classes="ma-0 pr-2 flex-wrap flex-grow-1", |
466 | | - dense=True, |
467 | | - v_if=("slice_slider_edit", True), |
| 455 | + with html.Div( |
| 456 | + classes="d-flex align-center flex-wrap flex-grow-1 ga-1 py-1 pr-2" |
468 | 457 | ): |
469 | | - # Debug: Show animation_tracks array |
470 | | - # html.Div( |
471 | | - # "Animation Tracks: {{ JSON.stringify(available_animation_tracks) }}", |
472 | | - # classes="col-12", |
473 | | - # ) |
474 | | - # Each track gets a column (3 per row) |
475 | | - with v3.VCol( |
476 | | - cols=("utils.quickview.cols(available_animation_tracks.length)",), |
| 458 | + with html.Template( |
477 | 459 | v_for="(track, idx) in available_animation_tracks", |
478 | 460 | key="idx", |
479 | | - classes="pa-2", |
480 | 461 | ): |
481 | 462 | with client.Getter(name=("track",), value_name="t_values"): |
482 | 463 | with client.Getter( |
483 | 464 | name=("track + '_idx'",), value_name="t_idx" |
484 | 465 | ): |
485 | | - with v3.VRow(classes="ma-0 align-center", dense=True): |
486 | | - v3.VLabel( |
487 | | - "{{track}}", |
488 | | - classes="text-subtitle-2", |
489 | | - ) |
490 | | - v3.VSpacer() |
491 | | - v3.VLabel( |
492 | | - "{{ dim_units[track] ? parseFloat(t_values[t_idx]).toFixed(2) + ' ' + dim_units[track] : 'Index value: ' + t_idx }} (k={{ t_idx }})", |
493 | | - classes="text-body-2", |
494 | | - ) |
495 | | - v3.VSlider( |
496 | | - model_value=("t_idx",), |
497 | | - update_modelValue=( |
498 | | - self.on_update_slider, |
499 | | - "[track, $event]", |
| 466 | + # --- Per-variable group --- |
| 467 | + with v3.VSheet( |
| 468 | + classes="d-flex align-center rounded px-1 ga-1", |
| 469 | + color=( |
| 470 | + "expanded_slice_track === track ? 'grey-lighten-3' : 'grey-lighten-4'", |
500 | 471 | ), |
501 | | - min=0, |
502 | | - # max=100,#("get(track.value).length - 1",), |
503 | | - max=("t_values.length - 1",), |
504 | | - step=1, |
505 | | - density="compact", |
506 | | - hide_details=True, |
507 | | - ) |
508 | | - with v3.VRow( |
509 | | - classes="ma-0 pl-6 pr-2 align-center ga-4", |
510 | | - v_if="!slice_slider_edit", |
511 | | - ): |
512 | | - with v3.VCol( |
513 | | - v_for="(track, idx) in available_animation_tracks", |
514 | | - key="idx", |
515 | | - ): |
516 | | - with client.Getter(name=("track",), value_name="t_values"): |
517 | | - with client.Getter( |
518 | | - name=("track + '_idx'",), value_name="t_idx" |
519 | | - ): |
520 | | - with v3.VRow(classes="ma-0 align-center", dense=True): |
521 | | - v3.VNumberInput( |
522 | | - model_value=("Number(t_idx)",), |
523 | | - update_modelValue=( |
524 | | - self.on_update_slider, |
525 | | - "[track, Number($event)]", |
526 | | - ), |
527 | | - key=("track + '_' + t_idx",), |
528 | | - min=[0], |
529 | | - max=["t_values ? t_values.length - 1 : 0"], |
530 | | - step=[1], |
531 | | - hide_details=True, |
532 | | - density="comfortable", |
533 | | - variant="plain", |
| 472 | + ): |
| 473 | + # Toggle button with track name |
| 474 | + v3.VBtn( |
| 475 | + "{{ track }}", |
| 476 | + v_tooltip_bottom="'Toggle ' + track + ' controls'", |
534 | 477 | flat=True, |
535 | | - control_variant="stacked", |
536 | | - style="max-width: 100px;", |
537 | | - reverse=True, |
| 478 | + variant=( |
| 479 | + "expanded_slice_track === track ? 'flat' : 'outlined'", |
| 480 | + ), |
| 481 | + rounded=True, |
| 482 | + click="expanded_slice_track = expanded_slice_track === track ? null : track", |
| 483 | + color=( |
| 484 | + "expanded_slice_track === track ? 'primary' : ''", |
| 485 | + ), |
| 486 | + style=( |
| 487 | + "'text-transform: none;' + (expanded_slice_track === track ? '' : ' background-color: white;')", |
| 488 | + ), |
538 | 489 | ) |
| 490 | + # Expanded controls |
| 491 | + with html.Div( |
| 492 | + v_if="expanded_slice_track === track", |
| 493 | + classes="d-flex align-center ga-1", |
| 494 | + style="height: 36px; overflow: visible;", |
| 495 | + ): |
| 496 | + v3.VDivider(vertical=True, classes="mx-1") |
| 497 | + # Text input |
| 498 | + html.Input( |
| 499 | + type="number", |
| 500 | + value=("t_idx",), |
| 501 | + min=[0], |
| 502 | + max=["t_values ? t_values.length - 1 : 0"], |
| 503 | + step=[1], |
| 504 | + change=( |
| 505 | + self.on_update_slider, |
| 506 | + "[track, Number($event.target.value)]", |
| 507 | + ), |
| 508 | + style="width: 60px; height: 28px; padding: 16px 4px; border: 1px solid #ccc; border-radius: 4px; font-size: 14px; box-sizing: border-box; text-align: right;", |
| 509 | + ) |
| 510 | + # Slider |
| 511 | + v3.VSlider( |
| 512 | + v_tooltip_bottom=( |
| 513 | + "dim_units[track] ? parseFloat(t_values[t_idx]).toFixed(2) + ' ' + dim_units[track] : 'Index: ' + t_idx", |
| 514 | + ), |
| 515 | + model_value=("t_idx",), |
| 516 | + update_modelValue=( |
| 517 | + self.on_update_slider, |
| 518 | + "[track, $event]", |
| 519 | + ), |
| 520 | + min=0, |
| 521 | + max=("t_values.length - 1",), |
| 522 | + step=1, |
| 523 | + show_ticks="always", |
| 524 | + hide_details=True, |
| 525 | + density="compact", |
| 526 | + style="min-width: 200px; max-width: 400px;", |
| 527 | + ) |
| 528 | + # Index label (shown when collapsed) |
539 | 529 | v3.VLabel( |
540 | | - "{{track}}", |
541 | | - classes="text-subtitle-2 ml-2 mt-1", |
| 530 | + v_if="expanded_slice_track !== track", |
| 531 | + v_html="'<b>(' + t_idx + ')</b>'", |
| 532 | + style="opacity: 1; color: rgba(0,0,0,0.87);", |
542 | 533 | ) |
| 534 | + # Value + units label |
543 | 535 | v3.VLabel( |
544 | | - "{{ dim_units[track] ? parseFloat(t_values[Number(t_idx)]).toFixed(2) + ' ' + dim_units[track] : 'Index value: ' + t_idx }}", |
545 | | - classes="text-body-2 text-no-wrap ml-2 mt-1", |
| 536 | + v_if=( |
| 537 | + "dim_units[track] && isNaN(Number(dim_units[track]))", |
| 538 | + ), |
| 539 | + v_html=( |
| 540 | + "'<i style=\\x27opacity:0.5\\x27>' + parseFloat(t_values[t_idx]).toFixed(2) + ' ' + dim_units[track] + '</i>'", |
| 541 | + ), |
| 542 | + style="opacity: 1; color: rgba(0,0,0,0.87);", |
546 | 543 | ) |
547 | 544 |
|
548 | 545 | def on_update_slider(self, dimension, index, *_, **__): |
|
0 commit comments