feat: implement new grid creation with inline rename in DataGridsManager
- Add new_grid() method to create empty DataGrid under selected folder/leaf parent - Generate unique sheet names (Sheet1, Sheet2, ...) with _generate_unique_sheet_name() - Auto-select and open new node in edit mode for immediate renaming - Fix TreeView to cancel edit mode when selecting any node - Wire "New grid" icon to new_grid() instead of clear_tree() - Add 14 unit tests covering new_grid() scenarios and TreeView behavior
This commit is contained in:
@@ -50,7 +50,7 @@ class Commands(BaseCommands):
|
||||
return Command("NewGrid",
|
||||
"New grid",
|
||||
self._owner,
|
||||
self._owner.new_grid)
|
||||
self._owner.new_grid).htmx(target=f"#{self._owner._tree.get_id()}")
|
||||
|
||||
def open_from_excel(self, tab_id, file_upload):
|
||||
return Command("OpenFromExcel",
|
||||
@@ -104,6 +104,62 @@ class DataGridsManager(SingleInstance, DatagridMetadataProvider):
|
||||
file_upload.set_on_ok(self.commands.open_from_excel(tab_id, file_upload))
|
||||
return self._tabs_manager.show_tab(tab_id)
|
||||
|
||||
def new_grid(self):
|
||||
selected_id = self._tree.get_selected_id()
|
||||
|
||||
if selected_id is None:
|
||||
parent_id = self._tree.ensure_path("Untitled")
|
||||
else:
|
||||
node = self._tree._state.items[selected_id]
|
||||
if node.type == "folder":
|
||||
parent_id = selected_id
|
||||
else: # leaf
|
||||
parent_id = node.parent
|
||||
|
||||
namespace = self._tree._state.items[parent_id].label
|
||||
name = self._generate_unique_sheet_name(parent_id)
|
||||
|
||||
dg_conf = DatagridConf(namespace=namespace, name=name)
|
||||
dg = DataGrid(self, conf=dg_conf, save_state=True)
|
||||
dg.init_from_dataframe(pd.DataFrame())
|
||||
self._registry.put(namespace, name, dg.get_id())
|
||||
|
||||
tab_id = self._tabs_manager.create_tab(name, dg)
|
||||
document = DocumentDefinition(
|
||||
document_id=str(uuid.uuid4()),
|
||||
namespace=namespace,
|
||||
name=name,
|
||||
type="excel",
|
||||
tab_id=tab_id,
|
||||
datagrid_id=dg.get_id()
|
||||
)
|
||||
self._state.elements = self._state.elements + [document]
|
||||
|
||||
tree_node = TreeNode(
|
||||
id=document.document_id,
|
||||
label=name,
|
||||
type="excel",
|
||||
parent=parent_id,
|
||||
bag=document.document_id
|
||||
)
|
||||
self._tree.add_node(tree_node, parent_id=parent_id)
|
||||
|
||||
if parent_id not in self._tree._state.opened:
|
||||
self._tree._state.opened.append(parent_id)
|
||||
|
||||
self._tree._state.selected = document.document_id
|
||||
self._tree._start_rename(document.document_id)
|
||||
|
||||
return self._tree, self._tabs_manager.show_tab(tab_id)
|
||||
|
||||
def _generate_unique_sheet_name(self, parent_id: str) -> str:
|
||||
children = self._tree._state.items[parent_id].children
|
||||
existing_labels = {self._tree._state.items[c].label for c in children}
|
||||
n = 1
|
||||
while f"Sheet{n}" in existing_labels:
|
||||
n += 1
|
||||
return f"Sheet{n}"
|
||||
|
||||
def open_from_excel(self, tab_id, file_upload: FileUpload):
|
||||
excel_content = file_upload.get_content()
|
||||
df = pd.read_excel(BytesIO(excel_content), file_upload.get_sheet_name())
|
||||
@@ -257,7 +313,7 @@ class DataGridsManager(SingleInstance, DatagridMetadataProvider):
|
||||
def mk_main_icons(self):
|
||||
return Div(
|
||||
mk.icon(folder_open20_regular, tooltip="Upload from source", command=self.commands.upload_from_source()),
|
||||
mk.icon(table_add20_regular, tooltip="New grid", command=self.commands.clear_tree()),
|
||||
mk.icon(table_add20_regular, tooltip="New grid", command=self.commands.new_grid()),
|
||||
cls="flex"
|
||||
)
|
||||
|
||||
|
||||
@@ -184,10 +184,10 @@ class TreeView(MultipleInstance):
|
||||
self._state = TreeViewState(self)
|
||||
self.conf = conf or TreeViewConf()
|
||||
self.commands = Commands(self)
|
||||
|
||||
|
||||
if items:
|
||||
self._state.items = items
|
||||
|
||||
|
||||
if self.conf.icons:
|
||||
self._state.icon_config = self.conf.icons
|
||||
|
||||
@@ -350,6 +350,7 @@ class TreeView(MultipleInstance):
|
||||
|
||||
def _save_rename(self, node_id: str, node_label: str):
|
||||
"""Save renamed node with new label."""
|
||||
logger.debug(f"_save_rename {node_id=}, {node_label=}")
|
||||
if node_id not in self._state.items:
|
||||
raise ValueError(f"Node {node_id} does not exist")
|
||||
|
||||
@@ -393,7 +394,9 @@ class TreeView(MultipleInstance):
|
||||
"""Select a node."""
|
||||
if node_id not in self._state.items:
|
||||
raise ValueError(f"Node {node_id} does not exist")
|
||||
|
||||
|
||||
# Cancel edit mode when selecting
|
||||
self._state.editing = None
|
||||
self._state.selected = node_id
|
||||
return self
|
||||
|
||||
@@ -401,11 +404,11 @@ class TreeView(MultipleInstance):
|
||||
"""Render action buttons for a node (visible on hover)."""
|
||||
is_leaf = len(self._state.items[node_id].children) == 0
|
||||
conf = self.conf
|
||||
|
||||
add_visible = conf.add_leaf if is_leaf else conf.add_node
|
||||
edit_visible = conf.edit_leaf if is_leaf else conf.edit_node
|
||||
|
||||
add_visible = conf.add_leaf if is_leaf else conf.add_node
|
||||
edit_visible = conf.edit_leaf if is_leaf else conf.edit_node
|
||||
delete_visible = conf.delete_leaf if is_leaf else conf.delete_node
|
||||
|
||||
|
||||
buttons = []
|
||||
if add_visible:
|
||||
buttons.append(mk.icon(add_circle20_regular, command=self.commands.add_child(node_id)))
|
||||
@@ -413,7 +416,7 @@ class TreeView(MultipleInstance):
|
||||
buttons.append(mk.icon(edit20_regular, command=self.commands.start_rename(node_id)))
|
||||
if delete_visible:
|
||||
buttons.append(mk.icon(delete20_regular, command=self.commands.delete_node(node_id)))
|
||||
|
||||
|
||||
return Div(*buttons, cls="mf-treenode-actions")
|
||||
|
||||
def _render_node(self, node_id: str, level: int = 0):
|
||||
|
||||
Reference in New Issue
Block a user