Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 7 additions & 19 deletions src/e3sm_quickview/app.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import asyncio
import datetime
import json
import math
import os
import time
from functools import partial
Expand Down Expand Up @@ -221,11 +220,12 @@ def _build_ui(self, **_):

with v3.VContainer(classes="h-100 pa-0", fluid=True):
with client.SizeObserver("main_size"):
# Take space to push content below the fixed overlay
html.Div(style=("`height: ${top_padding}px`",))

# Fixed overlay for toolbars
# Sticky toolbar overlay
with html.Div(style=css.TOOLBARS_FIXED_OVERLAY):
client.SizeObserver(
"toolbar_size",
style="position:absolute;top:0;left:0;width:100%;height:100%;pointer-events:none;",
)
toolbars.Layout(
apply_size=self.view_manager.apply_size,
zoom=self.view_manager.zoom,
Expand Down Expand Up @@ -613,22 +613,10 @@ async def _on_projection(self, projection, **_):

@change("active_tools", "available_animation_tracks")
def _on_toolbar_change(self, active_tools, **_):
# Initial estimate; client-side ResizeObserver will override with actual height
top_padding = 0
for name in active_tools:
if name == "select-slice-time":
track_count = len(self.state.available_animation_tracks or [])
rows_needed = 1
if track_count > 3:
if track_count % 3 == 0 or (track_count + 1) % 3 == 0:
rows_needed = math.ceil(track_count / 3)
elif track_count % 2 == 0:
rows_needed = track_count / 2
else:
rows_needed = math.ceil(track_count / 3)

top_padding += 70 * rows_needed
else:
top_padding += toolbars.SIZES.get(name, 0)
top_padding += toolbars.SIZES.get(name, 0)

self.state.top_padding = top_padding

Expand Down
2 changes: 1 addition & 1 deletion src/e3sm_quickview/components/css.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
)

TOOLBARS_FIXED_OVERLAY = (
"`position:fixed;top:0;width:${Math.floor(main_size?.size?.width || 0)}px;z-index:1;`",
"`position:sticky;top:0;width:${Math.floor(main_size?.size?.width || 0)}px;z-index:1;background:rgb(var(--v-theme-surface));`",
)


Expand Down
161 changes: 79 additions & 82 deletions src/e3sm_quickview/components/toolbars.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,14 @@
DENSITY = {
"adjust-layout": "compact",
"adjust-databounds": "default",
"select-slice-time": "default",
"select-slice-time": "compact",
"animation-controls": "compact",
}

SIZES = {
"adjust-layout": 49,
"adjust-databounds": 65,
"select-slice-time": 70,
"select-slice-time": 49,
"animation-controls": 49,
}

Expand Down Expand Up @@ -447,102 +447,99 @@ def __init__(self):
)
super().__init__(**style)

self.state.setdefault("expanded_slice_track", None)

with self:
with v3.VTooltip(
text=(
"slice_slider_edit ? 'Toggle to text edit' : 'Toggle to slider edit'",
),
):
with v3.Template(v_slot_activator="{ props }"):
v3.VIcon(
"mdi-tune-variant",
v_bind="props",
classes="ml-3 mr-2 opacity-50",
click="slice_slider_edit = !slice_slider_edit",
)
v3.VIcon("mdi-tune-variant", classes="ml-3 mr-2 opacity-50")

with v3.VRow(
classes="ma-0 pr-2 flex-wrap flex-grow-1",
dense=True,
v_if=("slice_slider_edit", True),
with html.Div(
classes="d-flex align-center flex-wrap flex-grow-1 ga-1 py-1 pr-2"
):
# Debug: Show animation_tracks array
# html.Div(
# "Animation Tracks: {{ JSON.stringify(available_animation_tracks) }}",
# classes="col-12",
# )
# Each track gets a column (3 per row)
with v3.VCol(
cols=("utils.quickview.cols(available_animation_tracks.length)",),
with html.Template(
v_for="(track, idx) in available_animation_tracks",
key="idx",
classes="pa-2",
):
with client.Getter(name=("track",), value_name="t_values"):
with client.Getter(
name=("track + '_idx'",), value_name="t_idx"
):
with v3.VRow(classes="ma-0 align-center", dense=True):
v3.VLabel(
"{{track}}",
classes="text-subtitle-2",
)
v3.VSpacer()
v3.VLabel(
"{{ dim_units[track] ? parseFloat(t_values[t_idx]).toFixed(2) + ' ' + dim_units[track] : 'Index value: ' + t_idx }} (k={{ t_idx }})",
classes="text-body-2",
)
v3.VSlider(
model_value=("t_idx",),
update_modelValue=(
self.on_update_slider,
"[track, $event]",
# --- Per-variable group ---
with v3.VSheet(
classes="d-flex align-center rounded px-1 ga-1",
color=(
"expanded_slice_track === track ? 'grey-lighten-3' : 'grey-lighten-4'",
),
min=0,
# max=100,#("get(track.value).length - 1",),
max=("t_values.length - 1",),
step=1,
density="compact",
hide_details=True,
)
with v3.VRow(
classes="ma-0 pl-6 pr-2 align-center ga-4",
v_if="!slice_slider_edit",
):
with v3.VCol(
v_for="(track, idx) in available_animation_tracks",
key="idx",
):
with client.Getter(name=("track",), value_name="t_values"):
with client.Getter(
name=("track + '_idx'",), value_name="t_idx"
):
with v3.VRow(classes="ma-0 align-center", dense=True):
v3.VNumberInput(
model_value=("Number(t_idx)",),
update_modelValue=(
self.on_update_slider,
"[track, Number($event)]",
),
key=("track + '_' + t_idx",),
min=[0],
max=["t_values ? t_values.length - 1 : 0"],
step=[1],
hide_details=True,
density="comfortable",
variant="plain",
):
# Toggle button with track name
v3.VBtn(
"{{ track }}",
v_tooltip_bottom="'Toggle ' + track + ' controls'",
flat=True,
control_variant="stacked",
style="max-width: 100px;",
reverse=True,
variant=(
"expanded_slice_track === track ? 'flat' : 'outlined'",
),
rounded=True,
click="expanded_slice_track = expanded_slice_track === track ? null : track",
color=(
"expanded_slice_track === track ? 'primary' : ''",
),
style=(
"'text-transform: none;' + (expanded_slice_track === track ? '' : ' background-color: white;')",
),
)
# Expanded controls
with html.Div(
v_if="expanded_slice_track === track",
classes="d-flex align-center ga-1",
style="height: 36px; overflow: visible;",
):
v3.VDivider(vertical=True, classes="mx-1")
# Text input
html.Input(
type="number",
value=("t_idx",),
min=[0],
max=["t_values ? t_values.length - 1 : 0"],
step=[1],
change=(
self.on_update_slider,
"[track, Number($event.target.value)]",
),
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;",
)
# Slider
v3.VSlider(
v_tooltip_bottom=(
"dim_units[track] ? parseFloat(t_values[t_idx]).toFixed(2) + ' ' + dim_units[track] : 'Index: ' + t_idx",
),
model_value=("t_idx",),
update_modelValue=(
self.on_update_slider,
"[track, $event]",
),
min=0,
max=("t_values.length - 1",),
step=1,
show_ticks="always",
hide_details=True,
density="compact",
style="min-width: 200px; max-width: 400px;",
)
# Index label (shown when collapsed)
v3.VLabel(
"{{track}}",
classes="text-subtitle-2 ml-2 mt-1",
v_if="expanded_slice_track !== track",
v_html="'<b>(' + t_idx + ')</b>'",
style="opacity: 1; color: rgba(0,0,0,0.87);",
)
# Value + units label
v3.VLabel(
"{{ dim_units[track] ? parseFloat(t_values[Number(t_idx)]).toFixed(2) + ' ' + dim_units[track] : 'Index value: ' + t_idx }}",
classes="text-body-2 text-no-wrap ml-2 mt-1",
v_if=(
"dim_units[track] && isNaN(Number(dim_units[track]))",
),
v_html=(
"'<i style=\\x27opacity:0.5\\x27>' + parseFloat(t_values[t_idx]).toFixed(2) + ' ' + dim_units[track] + '</i>'",
),
style="opacity: 1; color: rgba(0,0,0,0.87);",
)

def on_update_slider(self, dimension, index, *_, **__):
Expand Down
2 changes: 1 addition & 1 deletion src/e3sm_quickview/view_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -894,7 +894,7 @@ def _build_ui(self):
with v3.VCard(
variant="tonal",
style=(
"active_layout !== 'auto_layout' ? `height: calc(100% - ${top_padding}px;` : 'overflow-hidden'",
"active_layout !== 'auto_layout' ? `height: calc(100% - ${toolbar_size?.size?.height || 0}px)` : 'overflow-hidden'",
),
tile=("active_layout !== 'auto_layout'",),
raw_attrs=[f'data-field-name="{self.variable_name}"'],
Expand Down
Loading