Skip to content

Commit bd582d8

Browse files
committed
add and delete sections
1 parent 2b618a5 commit bd582d8

File tree

2 files changed

+139
-12
lines changed

2 files changed

+139
-12
lines changed

src/fourc_webviewer/fourc_webserver.py

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -324,6 +324,7 @@ def init_general_sections_state_and_server_vars(self):
324324

325325
# loop through input file sections
326326
self.state.general_sections = {}
327+
self.state.add_section = ""
327328
for section_name, section_data in self._server_vars[
328329
"fourc_yaml_content"
329330
].sections.items():
@@ -942,6 +943,99 @@ def change_export_fourc_yaml_path(self, export_fourc_yaml_path, **kwargs):
942943
#################################################
943944
# SELECTION CHANGES #################################
944945
################################################
946+
@controller.set("click_delete_section_button")
947+
def click_delete_section_button(self, **kwargs):
948+
"""Deletes the currently selected section; if it was the last
949+
subsection, delete the main too."""
950+
if self.state.selected_section_name in self.state.json_schema.get(
951+
"required", []
952+
):
953+
return
954+
955+
cur_main = self.state.selected_main_section_name
956+
cur_section = self.state.selected_section_name
957+
if not cur_main or not cur_section:
958+
return
959+
960+
general_sections = dict(self.state.general_sections or {})
961+
sections_names = {
962+
k: dict(v) for k, v in (self.state.section_names or {}).items()
963+
}
964+
965+
# delete the subsection's data
966+
del general_sections[cur_main][cur_section]
967+
self.state.general_sections = general_sections
968+
969+
# rebuild subsections list (new list ref -> reactive)
970+
subs_before = sections_names[cur_main]["subsections"]
971+
new_subs = [s for s in subs_before if s != cur_section]
972+
sections_names[cur_main] = {**sections_names[cur_main], "subsections": new_subs}
973+
974+
if cur_section == cur_main:
975+
# last one -> delete the main group immediately
976+
sections_names.pop(cur_main, None)
977+
general_sections.pop(cur_main, None)
978+
self.state.section_names = sections_names
979+
self.state.general_sections = general_sections
980+
981+
# choose a new valid selection
982+
new_main = next(iter(sections_names.keys()), "")
983+
self.state.selected_main_section_name = new_main
984+
self.state.selected_section_name = (
985+
sections_names[new_main]["subsections"][0] if new_main else ""
986+
)
987+
return
988+
989+
self.state.section_names = sections_names
990+
self.state.selected_main_section_name = cur_main
991+
self.state.selected_section_name = new_subs[0]
992+
993+
@change("add_section")
994+
def change_add_section(self, **kwargs):
995+
"""Reaction to section selection."""
996+
add_section = self.state.add_section
997+
main_section_name = add_section.split("/")[0]
998+
999+
if add_section not in self.state.json_schema.get("properties", {}):
1000+
return
1001+
1002+
general_sections = dict(self.state.general_sections or {})
1003+
section_names = {
1004+
k: dict(v) for k, v in (self.state.section_names or {}).items()
1005+
}
1006+
1007+
# Ensure main buckets exist
1008+
if main_section_name not in section_names:
1009+
section_names[main_section_name] = {
1010+
"subsections": [main_section_name],
1011+
"content_mode": self.state.all_content_modes["general_section"],
1012+
}
1013+
if main_section_name not in general_sections:
1014+
general_sections[main_section_name] = {main_section_name: {}}
1015+
1016+
# Store data under main -> sub
1017+
if add_section not in general_sections[main_section_name]:
1018+
general_sections[main_section_name][add_section] = {}
1019+
1020+
# Replace subsections list with a NEW list object
1021+
subs = section_names[main_section_name]["subsections"]
1022+
if add_section not in subs:
1023+
subs = subs + [add_section] # new list ref
1024+
section_names[main_section_name] = {
1025+
**section_names[main_section_name], # keep content_mode
1026+
"subsections": subs, # new list ref
1027+
}
1028+
1029+
# Commit (new references -> reactive)
1030+
self.state.general_sections = general_sections
1031+
self.state.section_names = section_names
1032+
1033+
# Set a valid selection so VSelect updates
1034+
self.state.selected_main_section_name = main_section_name
1035+
self.state.selected_section_name = add_section
1036+
1037+
self.state.add_section = ""
1038+
9451039
@change("selected_main_section_name")
9461040
def change_selected_main_section_name(self, selected_main_section_name, **kwargs):
9471041
"""Reaction to change of state.selected_main_section_name."""

src/fourc_webviewer/gui_utils.py

Lines changed: 45 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -249,7 +249,9 @@ def _sections_dropdown():
249249
items=("Object.keys(section_names)",),
250250
)
251251
vuetify.VSelect(
252-
v_if=("section_names[selected_main_section_name]['subsections'].length>1"),
252+
v_if=(
253+
"selected_main_section_name!=selected_section_name || section_names[selected_main_section_name]['subsections'].length>1",
254+
),
253255
v_model=("selected_section_name",),
254256
items=("section_names[selected_main_section_name]['subsections']",),
255257
)
@@ -576,6 +578,37 @@ def _functions_panel(server):
576578
)
577579

578580

581+
def _top_row(server):
582+
"""Top row layout (edit mode switch and add section)."""
583+
# EDIT MODE switch
584+
with html.Div(classes="d-flex align-center flex-nowrap w-100", style="gap: 12px;"):
585+
vuetify.VSwitch(
586+
v_model=("edit_mode", "all_edit_modes['view_mode']"),
587+
label=("edit_mode", "VIEW"),
588+
true_value=("all_edit_modes['edit_mode']",),
589+
false_value=("all_edit_modes['view_mode']",),
590+
color="primary",
591+
inset=True,
592+
classes="ma-0",
593+
)
594+
# add sections on the right
595+
with html.Div(
596+
classes="d-inline-flex align-center ml-auto",
597+
style="gap: 8px;",
598+
v_if="edit_mode == all_edit_modes['edit_mode']",
599+
):
600+
html.Span("Add Section:", classes="text-h6 font-weight-medium mr-3")
601+
vuetify.VAutocomplete(
602+
v_model=("add_section",),
603+
items=("Object.keys(json_schema['properties'])",),
604+
dense=True,
605+
solo=True,
606+
filterable=True,
607+
classes="ma-0 flex-grow-0",
608+
style="width: 200px;",
609+
)
610+
611+
579612
def _prop_value_table():
580613
"""Table (property - value) layout (for general sections)."""
581614
with vuetify.VTable(
@@ -1246,24 +1279,24 @@ def create_gui(server, render_window):
12461279
with layout.drawer as drawer:
12471280
drawer.width = 800
12481281
with html.Div(v_if=("vtu_path != ''",)):
1249-
# EDIT MODE switch
1250-
vuetify.VSwitch(
1251-
v_model=("edit_mode", "all_edit_modes['view_mode']"),
1252-
label=("edit_mode", "VIEW"),
1253-
true_value=("all_edit_modes['edit_mode']",),
1254-
false_value=("all_edit_modes['view_mode']",),
1255-
color="primary",
1256-
inset=True,
1257-
classes="ml-5",
1258-
)
1259-
12601282
# Further elements with conditional rendering (see above)
1283+
_top_row(server)
12611284
_sections_dropdown()
12621285
_prop_value_table()
12631286
_materials_panel()
12641287
_functions_panel(server)
12651288
_design_conditions_panel()
12661289
_result_description_panel()
1290+
vuetify.VBtn(
1291+
text="DELETE SECTION",
1292+
classes="ml-8",
1293+
outlined=True,
1294+
color="red",
1295+
v_if=(
1296+
"!json_schema['required'].includes(selected_section_name) && Object.keys(general_sections).includes(selected_main_section_name)",
1297+
),
1298+
click=server.controller.click_delete_section_button,
1299+
)
12671300
with html.Div(classes="flex-column justify-start"):
12681301
vuetify.VCard(
12691302
title="No input file content available",

0 commit comments

Comments
 (0)