Skip to content

Commit 60b6483

Browse files
committed
add remove sections
1 parent 2b618a5 commit 60b6483

File tree

2 files changed

+139
-12
lines changed

2 files changed

+139
-12
lines changed

src/fourc_webviewer/fourc_webserver.py

Lines changed: 92 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,97 @@ 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 = copy.deepcopy(self.state.general_sections) or {}
961+
sections_names = copy.deepcopy(self.state.section_names) or {}
962+
963+
# delete the subsection's data
964+
del general_sections[cur_main][cur_section]
965+
self.state.general_sections = general_sections
966+
967+
# rebuild subsections list (new list ref -> reactive)
968+
subs_before = sections_names[cur_main]["subsections"]
969+
new_subs = [s for s in subs_before if s != cur_section]
970+
sections_names[cur_main] = {**sections_names[cur_main], "subsections": new_subs}
971+
972+
if cur_section == cur_main:
973+
# last one -> delete the main group immediately
974+
sections_names.pop(cur_main, None)
975+
general_sections.pop(cur_main, None)
976+
self.state.section_names = sections_names
977+
self.state.general_sections = general_sections
978+
979+
# choose a new valid selection
980+
new_main = next(iter(sections_names.keys()), "")
981+
self.state.selected_main_section_name = new_main
982+
self.state.selected_section_name = (
983+
sections_names[new_main]["subsections"][0]
984+
if new_main and sections_names[new_main]["subsections"]
985+
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] if new_subs else ""
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] or ""
998+
999+
if add_section not in self.state.json_schema.get("properties", {}):
1000+
return
1001+
1002+
general_sections = copy.deepcopy(self.state.general_sections) or {}
1003+
section_names = copy.deepcopy(self.state.section_names) or {}
1004+
1005+
# Ensure main buckets exist
1006+
if main_section_name not in section_names:
1007+
section_names[main_section_name] = {
1008+
"subsections": [main_section_name],
1009+
"content_mode": self.state.all_content_modes["general_section"],
1010+
}
1011+
if main_section_name not in general_sections:
1012+
general_sections[main_section_name] = {main_section_name: {}}
1013+
1014+
# Store data under main -> sub
1015+
if add_section not in general_sections[main_section_name]:
1016+
general_sections[main_section_name][add_section] = {}
1017+
1018+
# Replace subsections list with a NEW list object
1019+
subs = section_names[main_section_name]["subsections"]
1020+
if add_section not in subs:
1021+
subs = subs + [add_section] # new list ref
1022+
section_names[main_section_name] = {
1023+
**section_names[main_section_name], # keep content_mode
1024+
"subsections": subs, # new list ref
1025+
}
1026+
1027+
# Commit (new references -> reactive)
1028+
self.state.general_sections = general_sections
1029+
self.state.section_names = section_names
1030+
1031+
# Set a valid selection so VSelect updates
1032+
self.state.selected_main_section_name = main_section_name
1033+
self.state.selected_section_name = add_section
1034+
1035+
self.state.add_section = ""
1036+
9451037
@change("selected_main_section_name")
9461038
def change_selected_main_section_name(self, selected_main_section_name, **kwargs):
9471039
"""Reaction to change of state.selected_main_section_name."""

src/fourc_webviewer/gui_utils.py

Lines changed: 47 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,39 @@ 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=(
604+
"Object.keys(json_schema['properties']).filter(key => !new Set(['MATERIALS', 'TITLE', 'CLONING MATERIAL MAP', 'RESULT DESCRIPTION']).has(key) && !(['DESIGN', 'TOPOLOGY', 'ELEMENTS', 'NODE', 'FUNCT', 'GEOMETRY'].some(n => key.includes(n))))",
605+
),
606+
dense=True,
607+
solo=True,
608+
filterable=True,
609+
classes="ma-0 flex-grow-0",
610+
style="width: 200px;",
611+
)
612+
613+
579614
def _prop_value_table():
580615
"""Table (property - value) layout (for general sections)."""
581616
with vuetify.VTable(
@@ -1246,24 +1281,24 @@ def create_gui(server, render_window):
12461281
with layout.drawer as drawer:
12471282
drawer.width = 800
12481283
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-
12601284
# Further elements with conditional rendering (see above)
1285+
_top_row(server)
12611286
_sections_dropdown()
12621287
_prop_value_table()
12631288
_materials_panel()
12641289
_functions_panel(server)
12651290
_design_conditions_panel()
12661291
_result_description_panel()
1292+
vuetify.VBtn(
1293+
text="DELETE SECTION",
1294+
classes="mx-auto d-block mt-10",
1295+
outlined=True,
1296+
color="red",
1297+
v_if=(
1298+
"!json_schema['required'].includes(selected_section_name) && Object.keys(general_sections).includes(selected_main_section_name)",
1299+
),
1300+
click=server.controller.click_delete_section_button,
1301+
)
12671302
with html.Div(classes="flex-column justify-start"):
12681303
vuetify.VCard(
12691304
title="No input file content available",

0 commit comments

Comments
 (0)