Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: replace mode for UI manager #374

Merged
merged 15 commits into from
Apr 16, 2024
Merged
Show file tree
Hide file tree
Changes from 10 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
39 changes: 39 additions & 0 deletions docs/docs/backend-driven-ui.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,28 @@ with ui.find("column-container"):
```
If the component couldn't be found, the method raises a `RuntimeError`.

### `refresh_with` method

You can use the `ui.refresh_with(component_id: str)` method to replace children of an existing component (referenced by its ID):
```python
with ui.refresh_with("my-page"):
# Previously existing children are cleared
ui.Header({"text": "Hello New World!"})
with ui.ColumnContainer():
with ui.Column():
ui.Text({"text": "Nobody here for now..."})
```

This method also allows to clear children of a component:
```python
with ui.refresh_with("my-page"):
# Empties the page
pass
```

This method works only with CMCs exclusively and doesn't allow to modify BMCs, which will raise an `UIError`.
mmikita95 marked this conversation as resolved.
Show resolved Hide resolved
As well as with `find` method, it also raises a `RuntimeError` if it failes to find a referenced component.
mmikita95 marked this conversation as resolved.
Show resolved Hide resolved

### Component methods

UI manager contains methods linked to each frontend component. For example, in previous code snippets we provide a `ui.Text` method, which is used for creating [Text components](https://www.streamsync.cloud/component-list.html#text).
Expand Down Expand Up @@ -218,3 +240,20 @@ with ui.find(id="cmc-column-1"):
# to the retrieved Column
ui.Text({"text": 'Hello World again!'}, id="hello-world-2")
```

This will result in a Column component having two children Text components. To replace or clear the children, use [`refresh_with` method](#refresh_with-method):

```python
with ui.Column(id="cmc-column-1"):
ui.Text({"text": 'Hello World!'}, id="hello-world-1")

...

with ui.refresh_with(id="cmc-column-1"):
# The following component is going to replace
# previously existing children of the retrieved Column
ui.Text(
{"text": 'To Hello World, or not to Hello World?'},
id="hello-world-new"
)
```
4 changes: 4 additions & 0 deletions src/streamsync/core_ui.py
Original file line number Diff line number Diff line change
Expand Up @@ -164,9 +164,13 @@ def clear_children(self, component_id: str) -> None:
try:
self.delete_component(child.id)
except UIError:
continue
mmikita95 marked this conversation as resolved.
Show resolved Hide resolved
# TODO reintroduce as a warning
"""
raise UIError("Failed to clear children of component " +
f"with ID '{component_id}': {child.id} " +
"is a builder-managed component.")
"""

def get_direct_descendents(self, parent_id: str) -> List[Component]:
base_children = self.base_component_tree.get_direct_descendents(parent_id)
Expand Down
35 changes: 35 additions & 0 deletions src/streamsync/ui_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,41 @@ def find(component_id: str) \
raise RuntimeError(f"Component {component_id} not found")
return component

@staticmethod
def refresh_with(component_id: str):
"""
Clears the existing children of a container component and sets it up to
accept new components. This method is designed to refresh the specified
container with new content specified in the subsequent block.

:param component_id: The unique identifier of the container component
to be refreshed.
:type component_id: str
mmikita95 marked this conversation as resolved.
Show resolved Hide resolved
:raises RuntimeError: If no component with the specified ID is found
in the current session's component tree.

.. note:: Upon invocation, this method clears all children of the
specified container component to prepare for new content. If no new
components are added within the context block, the container will
simply be emptied.

**Example**:
>>> with ui.refresh_with(id="my-container"):
>>> ui.Text({"text": "New content"}, id="new-content-1")
>>> ui.Button({"text": "Click me"}, id="new-button-1")

This method can also be used to clear existing children without adding
new components:
>>> with ui.refresh_with(id="my-container"):
>>> pass
"""
component = StreamsyncUI.find(component_id)

# Clear the children of the specified component.
current_component_tree().clear_children(component_id)

return component

@staticmethod
def create_container_component(component_type: str, **kwargs) -> Component:
component_tree = current_component_tree()
Expand Down
Loading