Skip to content

Commit

Permalink
Support named slots in Element.move (#3319)
Browse files Browse the repository at this point in the history
* add optional parameter target_slot to Element.move

* add demo "Move elements to slots"

* add test test_move_slots

* code review

---------

Co-authored-by: Falko Schindler <falko@zauberzeug.com>
  • Loading branch information
NiklasNeugebauer and falkoschindler committed Jul 9, 2024
1 parent 61d9710 commit 30bcf66
Show file tree
Hide file tree
Showing 3 changed files with 63 additions and 4 deletions.
21 changes: 17 additions & 4 deletions nicegui/element.py
Original file line number Diff line number Diff line change
Expand Up @@ -498,19 +498,32 @@ def clear(self) -> None:
slot.children.clear()
self.update()

def move(self, target_container: Optional[Element] = None, target_index: int = -1) -> None:
def move(self,
target_container: Optional[Element] = None,
target_index: int = -1, *,
target_slot: Optional[str] = None) -> None:
"""Move the element to another container.
:param target_container: container to move the element to (default: the parent container)
:param target_index: index within the target slot (default: append to the end)
:param target_slot: slot within the target container (default: default slot)
"""
assert self.parent_slot is not None
self.parent_slot.children.remove(self)
self.parent_slot.parent.update()
target_container = target_container or self.parent_slot.parent
target_index = target_index if target_index >= 0 else len(target_container.default_slot.children)
target_container.default_slot.children.insert(target_index, self)
self.parent_slot = target_container.default_slot

if target_slot is None:
self.parent_slot = target_container.default_slot
elif target_slot in target_container.slots:
self.parent_slot = target_container.slots[target_slot]
else:
raise ValueError(f'Slot "{target_slot}" does not exist in the target container. '
f'Add it first using `add_slot("{target_slot}")`.')

target_index = target_index if target_index >= 0 else len(self.parent_slot.children)
self.parent_slot.children.insert(target_index, self)

target_container.update()

def remove(self, element: Union[Element, int]) -> None:
Expand Down
33 changes: 33 additions & 0 deletions tests/test_element.py
Original file line number Diff line number Diff line change
Expand Up @@ -134,17 +134,50 @@ def test_move(screen: Screen):

screen.open('/')
assert screen.find('A').location['y'] < screen.find('X').location['y'] < screen.find('B').location['y']

screen.click('Move X to B')
screen.wait(0.5)
assert screen.find('A').location['y'] < screen.find('B').location['y'] < screen.find('X').location['y']

screen.click('Move X to A')
screen.wait(0.5)
assert screen.find('A').location['y'] < screen.find('X').location['y'] < screen.find('B').location['y']

screen.click('Move X to top')
screen.wait(0.5)
assert screen.find('X').location['y'] < screen.find('A').location['y'] < screen.find('B').location['y']


def test_move_slots(screen: Screen):
with ui.expansion(value=True) as a:
with a.add_slot('header'):
ui.label('A')
x = ui.label('X')

with ui.expansion(value=True) as b:
with b.add_slot('header'):
ui.label('B')

ui.button('Move X to header', on_click=lambda: x.move(target_slot='header'))
ui.button('Move X to B', on_click=lambda: x.move(b))
ui.button('Move X to top', on_click=lambda: x.move(target_index=0))

screen.open('/')
assert screen.find('A').location['y'] < screen.find('X').location['y'], 'X is in A.default'

screen.click('Move X to header')
screen.wait(0.5)
assert screen.find('A').location['y'] == screen.find('X').location['y'], 'X is in A.header'

screen.click('Move X to top')
screen.wait(0.5)
assert screen.find('A').location['y'] < screen.find('X').location['y'], 'X is in A.default'

screen.click('Move X to B')
screen.wait(0.5)
assert screen.find('B').location['y'] < screen.find('X').location['y'], 'X is in B.default'


def test_xss(screen: Screen):
ui.label('</script><script>alert(1)</script>')
ui.label('<b>Bold 1</b>, `code`, copy&paste, multi\nline')
Expand Down
13 changes: 13 additions & 0 deletions website/documentation/content/element_documentation.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,19 @@ def move_elements() -> None:
ui.button('Move X to top', on_click=lambda: x.move(target_index=0))


@doc.demo('Move elements to slots', '''
This demo shows how to move elements between slots within an element.
''')
def move_elements_to_slots() -> None:
with ui.card() as card:
name = ui.input('Name', value='Paul')
name.add_slot('append')
icon = ui.icon('face')

ui.button('Move into input', on_click=lambda: icon.move(name, target_slot='append'))
ui.button('Move out of input', on_click=lambda: icon.move(card))


@doc.demo('Default props', '''
You can set default props for all elements of a certain class.
This way you can avoid repeating the same props over and over again.
Expand Down

0 comments on commit 30bcf66

Please sign in to comment.