mirror of
https://github.com/rio-labs/rio.git
synced 2025-12-21 12:59:32 -06:00
several small fixes
This commit is contained in:
@@ -96,7 +96,7 @@ Additions:
|
||||
`rio.Component`.
|
||||
- Page rework
|
||||
- Add `rio.Redirect`
|
||||
- TODO: Automatic page scan
|
||||
- Still missing automatic page scan
|
||||
- New experimental `rio.FilePickerArea` component
|
||||
|
||||
## 0.9.2
|
||||
|
||||
@@ -1,371 +1,371 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import collections.abc
|
||||
import sys
|
||||
import typing as t
|
||||
|
||||
import rio
|
||||
|
||||
from .. import global_state
|
||||
|
||||
__all__ = ["List", "Dict", "Set"]
|
||||
|
||||
|
||||
T = t.TypeVar("T")
|
||||
|
||||
|
||||
def identity(x: T) -> T:
|
||||
return x
|
||||
|
||||
|
||||
class ObservableContainer:
|
||||
def __init__(self):
|
||||
self._affected_sessions: set[rio.Session] = set()
|
||||
|
||||
def _mark_as_accessed(self):
|
||||
if global_state.currently_building_session is None:
|
||||
return
|
||||
|
||||
global_state.accessed_objects.add(self)
|
||||
self._affected_sessions.add(global_state.currently_building_session)
|
||||
|
||||
def _mark_as_changed(self) -> None:
|
||||
for session in self._affected_sessions:
|
||||
session._changed_objects.add(self)
|
||||
session._refresh_required_event.set()
|
||||
|
||||
|
||||
class List(ObservableContainer, collections.abc.MutableSequence[T]):
|
||||
"""
|
||||
A `list`-like object that automatically rebuilds components whenever its
|
||||
content changes.
|
||||
|
||||
## Examples
|
||||
|
||||
```python
|
||||
class ElementAdder(rio.Component):
|
||||
list: rio.List[str]
|
||||
|
||||
def build(self):
|
||||
return rio.Button(
|
||||
"add an element",
|
||||
on_press=lambda: self.list.append('foo'),
|
||||
)
|
||||
|
||||
class Display(rio.Component):
|
||||
list: rio.List[str]
|
||||
|
||||
def build(self):
|
||||
return rio.Text("\n".join(self.list))
|
||||
|
||||
class ListDemo(rio.Component):
|
||||
list: rio.List[str] = rio.List()
|
||||
|
||||
def build(self):
|
||||
return rio.Column(
|
||||
ElementAdder(self.list),
|
||||
Display(self.list),
|
||||
)
|
||||
```
|
||||
|
||||
Here you can see how the `Display` component automatically updates whenever
|
||||
the `ElementAdder` component appends a new element to the `List`. This would
|
||||
be much trickier to do with a regular `list`.
|
||||
|
||||
## Metadata
|
||||
|
||||
`experimental`: True
|
||||
"""
|
||||
|
||||
def __init__(self, items: t.Iterable[T] = (), /):
|
||||
super().__init__()
|
||||
|
||||
self._items = list(items)
|
||||
|
||||
def insert(self, index: int, value: T) -> None:
|
||||
self._items.insert(index, value)
|
||||
self._mark_as_changed()
|
||||
|
||||
def append(self, value: T) -> None:
|
||||
self._items.append(value)
|
||||
self._mark_as_changed()
|
||||
|
||||
def extend(self, values: t.Iterable[T]) -> None:
|
||||
self._items.extend(values)
|
||||
self._mark_as_changed()
|
||||
|
||||
def remove(self, value: T) -> None:
|
||||
self._items.remove(value)
|
||||
self._mark_as_changed()
|
||||
|
||||
def clear(self) -> None:
|
||||
self._items.clear()
|
||||
self._mark_as_changed()
|
||||
|
||||
def pop(self, index: int | None = None, /) -> T:
|
||||
self._mark_as_accessed()
|
||||
|
||||
if index is None:
|
||||
item = self._items.pop()
|
||||
else:
|
||||
item = self._items.pop(index)
|
||||
|
||||
self._mark_as_changed()
|
||||
|
||||
return item
|
||||
|
||||
def reverse(self) -> None:
|
||||
self._items.reverse()
|
||||
self._mark_as_changed()
|
||||
|
||||
def copy(self) -> List[T]:
|
||||
self._mark_as_accessed()
|
||||
return List(self)
|
||||
|
||||
def __delitem__(self, index: int | slice) -> None:
|
||||
del self._items[index]
|
||||
self._mark_as_changed()
|
||||
|
||||
def __add__(self, other: t.Iterable[T], /) -> List[T]:
|
||||
result = List(self)
|
||||
result += other
|
||||
return result
|
||||
|
||||
def __iadd__(self, other: t.Iterable[T]) -> List[T]:
|
||||
self.extend(other)
|
||||
return self
|
||||
|
||||
def index(self, value: T, start: int = 0, stop: int = sys.maxsize) -> int:
|
||||
self._mark_as_accessed()
|
||||
return self._items.index(value, start, stop)
|
||||
|
||||
def count(self, value: T) -> int:
|
||||
self._mark_as_accessed()
|
||||
return self._items.count(value)
|
||||
|
||||
@t.overload
|
||||
def __getitem__(self, index: int) -> T: ...
|
||||
|
||||
@t.overload
|
||||
def __getitem__(self, index: slice) -> List[T]: ...
|
||||
|
||||
def __getitem__(self, index: int | slice) -> T | List[T]:
|
||||
self._mark_as_accessed()
|
||||
|
||||
if isinstance(index, slice):
|
||||
return List(self._items[index])
|
||||
else:
|
||||
return self._items[index]
|
||||
|
||||
def __len__(self) -> int:
|
||||
self._mark_as_accessed()
|
||||
return len(self._items)
|
||||
|
||||
def __iter__(self) -> t.Iterator[T]:
|
||||
# TODO: Technically, no data has been accessed yet. The correct behavior
|
||||
# would be to return a custom iterator that tracks access in its
|
||||
# `__next__` method.
|
||||
self._mark_as_accessed()
|
||||
return iter(self._items)
|
||||
|
||||
def __contains__(self, value: object) -> bool:
|
||||
self._mark_as_accessed()
|
||||
return value in self._items
|
||||
|
||||
# These function signatures are a PITA. Screw the boilerplate, just inherit
|
||||
# the signature
|
||||
if t.TYPE_CHECKING:
|
||||
__setitem__ = collections.abc.MutableSequence.__setitem__
|
||||
else:
|
||||
|
||||
def sort(self, *args, **kwargs) -> None:
|
||||
self._items.sort(*args, **kwargs)
|
||||
self._mark_as_changed()
|
||||
|
||||
def __setitem__(self, index_or_slice, value) -> None:
|
||||
self._items[index_or_slice] = value
|
||||
self._mark_as_changed()
|
||||
|
||||
|
||||
K = t.TypeVar("K")
|
||||
V = t.TypeVar("V")
|
||||
|
||||
|
||||
class Dict(ObservableContainer, collections.abc.MutableMapping[K, V]):
|
||||
"""
|
||||
A `dict`-like object that automatically rebuilds components whenever its
|
||||
content changes.
|
||||
|
||||
## Examples
|
||||
|
||||
```python
|
||||
class ElementAdder(rio.Component):
|
||||
dict: rio.Dict[int, str]
|
||||
|
||||
def _add_element(self):
|
||||
self.dict[len(self.dict)] = 'foo'
|
||||
|
||||
def build(self):
|
||||
return rio.Button(
|
||||
"add an element",
|
||||
on_press=self._add_element,
|
||||
)
|
||||
|
||||
class Display(rio.Component):
|
||||
dict: rio.Dict[int, str]
|
||||
|
||||
def build(self):
|
||||
return rio.Text("\n".join(f"{k}: {v}" for k, v in self.dict.items()))
|
||||
|
||||
class DictDemo(rio.Component):
|
||||
dict: rio.Dict[int, str] = rio.Dict()
|
||||
|
||||
def build(self):
|
||||
return rio.Column(
|
||||
ElementAdder(self.dict),
|
||||
Display(self.dict),
|
||||
)
|
||||
```
|
||||
|
||||
Here you can see how the `Display` component automatically updates whenever
|
||||
the `ElementAdder` component adds a new element to the `Dict`. This would be
|
||||
much trickier to do with a regular `dict`.
|
||||
|
||||
## Metadata
|
||||
|
||||
`experimental`: True
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
__items: t.Mapping[K, V] | t.Iterable[tuple[K, V]] = (),
|
||||
/,
|
||||
**kwargs: V,
|
||||
):
|
||||
super().__init__()
|
||||
|
||||
self._items = dict(__items, **kwargs)
|
||||
|
||||
def __setitem__(self, key: K, value: V, /) -> None:
|
||||
self._items[key] = value
|
||||
self._mark_as_changed()
|
||||
|
||||
def __delitem__(self, key: K, /) -> None:
|
||||
del self._items[key]
|
||||
self._mark_as_changed()
|
||||
|
||||
def __getitem__(self, key: K, /) -> V:
|
||||
self._mark_as_accessed()
|
||||
return self._items[key]
|
||||
|
||||
def __iter__(self) -> t.Iterator[K]:
|
||||
self._mark_as_accessed()
|
||||
return iter(self._items)
|
||||
|
||||
def __len__(self) -> int:
|
||||
self._mark_as_accessed()
|
||||
return len(self._items)
|
||||
|
||||
def __contains__(self, key: object, /) -> bool:
|
||||
self._mark_as_accessed()
|
||||
return key in self._items
|
||||
|
||||
def popitem(self) -> tuple[K, V]:
|
||||
self._mark_as_accessed()
|
||||
|
||||
item = self._items.popitem()
|
||||
self._mark_as_changed()
|
||||
|
||||
return item
|
||||
|
||||
# These function signatures are a PITA. Screw the boilerplate, just inherit
|
||||
# the signature
|
||||
if not t.TYPE_CHECKING:
|
||||
|
||||
def update(self, *args, **kwargs) -> None:
|
||||
self._items.update(*args, **kwargs)
|
||||
self._mark_as_changed()
|
||||
|
||||
def pop(self, *args, **kwargs):
|
||||
self._mark_as_accessed()
|
||||
|
||||
value = self._items.pop(*args, **kwargs)
|
||||
self._mark_as_changed()
|
||||
|
||||
return value
|
||||
|
||||
|
||||
class Set(ObservableContainer, collections.abc.MutableSet[T]):
|
||||
"""
|
||||
A `set`-like object that automatically rebuilds components whenever its
|
||||
content changes.
|
||||
|
||||
## Examples
|
||||
|
||||
```python
|
||||
class ElementAdder(rio.Component):
|
||||
set: rio.Set[str]
|
||||
|
||||
def build(self):
|
||||
return rio.Button(
|
||||
"add an element",
|
||||
on_press=lambda: self.set.add(len(self.set)),
|
||||
)
|
||||
|
||||
class Display(rio.Component):
|
||||
set: rio.Set[str]
|
||||
|
||||
def build(self):
|
||||
return rio.Text("\n".join(self.set))
|
||||
|
||||
class SetDemo(rio.Component):
|
||||
set: rio.Set[str] = rio.Set()
|
||||
|
||||
def build(self):
|
||||
return rio.Column(
|
||||
ElementAdder(self.set),
|
||||
Display(self.set),
|
||||
)
|
||||
```
|
||||
|
||||
Here you can see how the `Display` component automatically updates whenever
|
||||
the `ElementAdder` component adds a new element to the `Set`. This would be
|
||||
much trickier to do with a regular `set`.
|
||||
|
||||
## Metadata
|
||||
|
||||
`experimental`: True
|
||||
"""
|
||||
|
||||
def __init__(self, items: t.Iterable[T] = (), /):
|
||||
super().__init__()
|
||||
|
||||
self._items = set(items)
|
||||
|
||||
def __iter__(self) -> t.Iterator[T]:
|
||||
self._mark_as_accessed()
|
||||
return iter(self._items)
|
||||
|
||||
def __len__(self) -> int:
|
||||
self._mark_as_accessed()
|
||||
return len(self._items)
|
||||
|
||||
def __contains__(self, value: object) -> bool:
|
||||
self._mark_as_accessed()
|
||||
return value in self._items
|
||||
|
||||
def add(self, value: T) -> None:
|
||||
self._items.add(value)
|
||||
self._mark_as_changed()
|
||||
|
||||
def update(self, values: t.Iterable[T]) -> None:
|
||||
self._items.update(values)
|
||||
self._mark_as_changed()
|
||||
|
||||
def discard(self, value: T) -> None:
|
||||
self._items.discard(value)
|
||||
self._mark_as_changed()
|
||||
|
||||
def clear(self) -> None:
|
||||
self._items.clear()
|
||||
self._mark_as_changed()
|
||||
from __future__ import annotations
|
||||
|
||||
import collections.abc
|
||||
import sys
|
||||
import typing as t
|
||||
|
||||
import rio
|
||||
|
||||
from .. import global_state
|
||||
|
||||
__all__ = ["List", "Dict", "Set"]
|
||||
|
||||
|
||||
T = t.TypeVar("T")
|
||||
|
||||
|
||||
def identity(x: T) -> T:
|
||||
return x
|
||||
|
||||
|
||||
class ObservableContainer:
|
||||
def __init__(self):
|
||||
self._affected_sessions: set[rio.Session] = set()
|
||||
|
||||
def _mark_as_accessed(self):
|
||||
if global_state.currently_building_session is None:
|
||||
return
|
||||
|
||||
global_state.accessed_objects.add(self)
|
||||
self._affected_sessions.add(global_state.currently_building_session)
|
||||
|
||||
def _mark_as_changed(self) -> None:
|
||||
for session in self._affected_sessions:
|
||||
session._changed_objects.add(self)
|
||||
session._refresh_required_event.set()
|
||||
|
||||
|
||||
class List(ObservableContainer, collections.abc.MutableSequence[T]):
|
||||
"""
|
||||
A `list`-like object that automatically rebuilds components whenever its
|
||||
content changes.
|
||||
|
||||
## Examples
|
||||
|
||||
```python
|
||||
class ElementAdder(rio.Component):
|
||||
list: rio.List[str]
|
||||
|
||||
def build(self):
|
||||
return rio.Button(
|
||||
"add an element",
|
||||
on_press=lambda: self.list.append("foo"),
|
||||
)
|
||||
|
||||
class Display(rio.Component):
|
||||
list: rio.List[str]
|
||||
|
||||
def build(self):
|
||||
return rio.Text("\\n".join(self.list))
|
||||
|
||||
class ListDemo(rio.Component):
|
||||
list: rio.List[str] = rio.List()
|
||||
|
||||
def build(self):
|
||||
return rio.Column(
|
||||
ElementAdder(self.list),
|
||||
Display(self.list),
|
||||
)
|
||||
```
|
||||
|
||||
Here you can see how the `Display` component automatically updates whenever
|
||||
the `ElementAdder` component appends a new element to the `List`. This would
|
||||
be much trickier to do with a regular `list`.
|
||||
|
||||
## Metadata
|
||||
|
||||
`experimental`: True
|
||||
"""
|
||||
|
||||
def __init__(self, items: t.Iterable[T] = (), /):
|
||||
super().__init__()
|
||||
|
||||
self._items = list(items)
|
||||
|
||||
def insert(self, index: int, value: T) -> None:
|
||||
self._items.insert(index, value)
|
||||
self._mark_as_changed()
|
||||
|
||||
def append(self, value: T) -> None:
|
||||
self._items.append(value)
|
||||
self._mark_as_changed()
|
||||
|
||||
def extend(self, values: t.Iterable[T]) -> None:
|
||||
self._items.extend(values)
|
||||
self._mark_as_changed()
|
||||
|
||||
def remove(self, value: T) -> None:
|
||||
self._items.remove(value)
|
||||
self._mark_as_changed()
|
||||
|
||||
def clear(self) -> None:
|
||||
self._items.clear()
|
||||
self._mark_as_changed()
|
||||
|
||||
def pop(self, index: int | None = None, /) -> T:
|
||||
self._mark_as_accessed()
|
||||
|
||||
if index is None:
|
||||
item = self._items.pop()
|
||||
else:
|
||||
item = self._items.pop(index)
|
||||
|
||||
self._mark_as_changed()
|
||||
|
||||
return item
|
||||
|
||||
def reverse(self) -> None:
|
||||
self._items.reverse()
|
||||
self._mark_as_changed()
|
||||
|
||||
def copy(self) -> List[T]:
|
||||
self._mark_as_accessed()
|
||||
return List(self)
|
||||
|
||||
def __delitem__(self, index: int | slice) -> None:
|
||||
del self._items[index]
|
||||
self._mark_as_changed()
|
||||
|
||||
def __add__(self, other: t.Iterable[T], /) -> List[T]:
|
||||
result = List(self)
|
||||
result += other
|
||||
return result
|
||||
|
||||
def __iadd__(self, other: t.Iterable[T]) -> List[T]:
|
||||
self.extend(other)
|
||||
return self
|
||||
|
||||
def index(self, value: T, start: int = 0, stop: int = sys.maxsize) -> int:
|
||||
self._mark_as_accessed()
|
||||
return self._items.index(value, start, stop)
|
||||
|
||||
def count(self, value: T) -> int:
|
||||
self._mark_as_accessed()
|
||||
return self._items.count(value)
|
||||
|
||||
@t.overload
|
||||
def __getitem__(self, index: int) -> T: ...
|
||||
|
||||
@t.overload
|
||||
def __getitem__(self, index: slice) -> List[T]: ...
|
||||
|
||||
def __getitem__(self, index: int | slice) -> T | List[T]:
|
||||
self._mark_as_accessed()
|
||||
|
||||
if isinstance(index, slice):
|
||||
return List(self._items[index])
|
||||
else:
|
||||
return self._items[index]
|
||||
|
||||
def __len__(self) -> int:
|
||||
self._mark_as_accessed()
|
||||
return len(self._items)
|
||||
|
||||
def __iter__(self) -> t.Iterator[T]:
|
||||
# TODO: Technically, no data has been accessed yet. The correct behavior
|
||||
# would be to return a custom iterator that tracks access in its
|
||||
# `__next__` method.
|
||||
self._mark_as_accessed()
|
||||
return iter(self._items)
|
||||
|
||||
def __contains__(self, value: object) -> bool:
|
||||
self._mark_as_accessed()
|
||||
return value in self._items
|
||||
|
||||
# These function signatures are a PITA. Screw the boilerplate, just inherit
|
||||
# the signature
|
||||
if t.TYPE_CHECKING:
|
||||
__setitem__ = collections.abc.MutableSequence.__setitem__
|
||||
else:
|
||||
|
||||
def sort(self, *args, **kwargs) -> None:
|
||||
self._items.sort(*args, **kwargs)
|
||||
self._mark_as_changed()
|
||||
|
||||
def __setitem__(self, index_or_slice, value) -> None:
|
||||
self._items[index_or_slice] = value
|
||||
self._mark_as_changed()
|
||||
|
||||
|
||||
K = t.TypeVar("K")
|
||||
V = t.TypeVar("V")
|
||||
|
||||
|
||||
class Dict(ObservableContainer, collections.abc.MutableMapping[K, V]):
|
||||
"""
|
||||
A `dict`-like object that automatically rebuilds components whenever its
|
||||
content changes.
|
||||
|
||||
## Examples
|
||||
|
||||
```python
|
||||
class ElementAdder(rio.Component):
|
||||
dict: rio.Dict[int, str]
|
||||
|
||||
def _add_element(self):
|
||||
self.dict[len(self.dict)] = "foo"
|
||||
|
||||
def build(self):
|
||||
return rio.Button(
|
||||
"add an element",
|
||||
on_press=self._add_element,
|
||||
)
|
||||
|
||||
class Display(rio.Component):
|
||||
dict: rio.Dict[int, str]
|
||||
|
||||
def build(self):
|
||||
return rio.Text("\\n".join(f"{k}: {v}" for k, v in self.dict.items()))
|
||||
|
||||
class DictDemo(rio.Component):
|
||||
dict: rio.Dict[int, str] = rio.Dict()
|
||||
|
||||
def build(self):
|
||||
return rio.Column(
|
||||
ElementAdder(self.dict),
|
||||
Display(self.dict),
|
||||
)
|
||||
```
|
||||
|
||||
Here you can see how the `Display` component automatically updates whenever
|
||||
the `ElementAdder` component adds a new element to the `Dict`. This would be
|
||||
much trickier to do with a regular `dict`.
|
||||
|
||||
## Metadata
|
||||
|
||||
`experimental`: True
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
__items: t.Mapping[K, V] | t.Iterable[tuple[K, V]] = (),
|
||||
/,
|
||||
**kwargs: V,
|
||||
):
|
||||
super().__init__()
|
||||
|
||||
self._items = dict(__items, **kwargs)
|
||||
|
||||
def __setitem__(self, key: K, value: V, /) -> None:
|
||||
self._items[key] = value
|
||||
self._mark_as_changed()
|
||||
|
||||
def __delitem__(self, key: K, /) -> None:
|
||||
del self._items[key]
|
||||
self._mark_as_changed()
|
||||
|
||||
def __getitem__(self, key: K, /) -> V:
|
||||
self._mark_as_accessed()
|
||||
return self._items[key]
|
||||
|
||||
def __iter__(self) -> t.Iterator[K]:
|
||||
self._mark_as_accessed()
|
||||
return iter(self._items)
|
||||
|
||||
def __len__(self) -> int:
|
||||
self._mark_as_accessed()
|
||||
return len(self._items)
|
||||
|
||||
def __contains__(self, key: object, /) -> bool:
|
||||
self._mark_as_accessed()
|
||||
return key in self._items
|
||||
|
||||
def popitem(self) -> tuple[K, V]:
|
||||
self._mark_as_accessed()
|
||||
|
||||
item = self._items.popitem()
|
||||
self._mark_as_changed()
|
||||
|
||||
return item
|
||||
|
||||
# These function signatures are a PITA. Screw the boilerplate, just inherit
|
||||
# the signature
|
||||
if not t.TYPE_CHECKING:
|
||||
|
||||
def update(self, *args, **kwargs) -> None:
|
||||
self._items.update(*args, **kwargs)
|
||||
self._mark_as_changed()
|
||||
|
||||
def pop(self, *args, **kwargs):
|
||||
self._mark_as_accessed()
|
||||
|
||||
value = self._items.pop(*args, **kwargs)
|
||||
self._mark_as_changed()
|
||||
|
||||
return value
|
||||
|
||||
|
||||
class Set(ObservableContainer, collections.abc.MutableSet[T]):
|
||||
"""
|
||||
A `set`-like object that automatically rebuilds components whenever its
|
||||
content changes.
|
||||
|
||||
## Examples
|
||||
|
||||
```python
|
||||
class ElementAdder(rio.Component):
|
||||
set: rio.Set[str]
|
||||
|
||||
def build(self):
|
||||
return rio.Button(
|
||||
"add an element",
|
||||
on_press=lambda: self.set.add(len(self.set)),
|
||||
)
|
||||
|
||||
class Display(rio.Component):
|
||||
set: rio.Set[str]
|
||||
|
||||
def build(self):
|
||||
return rio.Text("\\n".join(self.set))
|
||||
|
||||
class SetDemo(rio.Component):
|
||||
set: rio.Set[str] = rio.Set()
|
||||
|
||||
def build(self):
|
||||
return rio.Column(
|
||||
ElementAdder(self.set),
|
||||
Display(self.set),
|
||||
)
|
||||
```
|
||||
|
||||
Here you can see how the `Display` component automatically updates whenever
|
||||
the `ElementAdder` component adds a new element to the `Set`. This would be
|
||||
much trickier to do with a regular `set`.
|
||||
|
||||
## Metadata
|
||||
|
||||
`experimental`: True
|
||||
"""
|
||||
|
||||
def __init__(self, items: t.Iterable[T] = (), /):
|
||||
super().__init__()
|
||||
|
||||
self._items = set(items)
|
||||
|
||||
def __iter__(self) -> t.Iterator[T]:
|
||||
self._mark_as_accessed()
|
||||
return iter(self._items)
|
||||
|
||||
def __len__(self) -> int:
|
||||
self._mark_as_accessed()
|
||||
return len(self._items)
|
||||
|
||||
def __contains__(self, value: object) -> bool:
|
||||
self._mark_as_accessed()
|
||||
return value in self._items
|
||||
|
||||
def add(self, value: T) -> None:
|
||||
self._items.add(value)
|
||||
self._mark_as_changed()
|
||||
|
||||
def update(self, values: t.Iterable[T]) -> None:
|
||||
self._items.update(values)
|
||||
self._mark_as_changed()
|
||||
|
||||
def discard(self, value: T) -> None:
|
||||
self._items.discard(value)
|
||||
self._mark_as_changed()
|
||||
|
||||
def clear(self) -> None:
|
||||
self._items.clear()
|
||||
self._mark_as_changed()
|
||||
|
||||
@@ -1936,6 +1936,16 @@ a.remove();
|
||||
|
||||
@property
|
||||
def theme(self) -> theme.Theme:
|
||||
"""
|
||||
The theme currently used by this session.
|
||||
|
||||
If you've passed both a light and dark theme to your app, this will be
|
||||
whichever one is actually being used by the client. You can also assign
|
||||
a new theme to this property to change the theme for this session.
|
||||
|
||||
Note that changing the theme will only affect this specific session, not
|
||||
the entire app.
|
||||
"""
|
||||
return self._theme
|
||||
|
||||
@theme.setter
|
||||
@@ -3121,6 +3131,7 @@ a.remove();
|
||||
properties_to_serialize: IdentityDefaultDict[object, set[str]],
|
||||
):
|
||||
components_to_build = set[rio.Component]()
|
||||
permanent_component_level_cache: dict[rio.Component, int] = {}
|
||||
|
||||
while True:
|
||||
# Update the properties_to_serialize
|
||||
@@ -3135,19 +3146,8 @@ a.remove();
|
||||
self._changed_items.clear()
|
||||
self._refresh_required_event.clear()
|
||||
|
||||
# We need to build parents before children, but some components
|
||||
# haven't had their `_weak_parent_` set yet, so we don't know who
|
||||
# their parent is. We need to find the topmost components and build
|
||||
# them.
|
||||
|
||||
# TODO: This is not entirely correct, because during the build
|
||||
# process, new components can be instantiated or the level of an
|
||||
# existing component can change. The correct solution would be to
|
||||
# process one component, then call `_collect_components_to_build()`
|
||||
# again, and sort again. TODO: I think this *is* correct now that we
|
||||
# use parents instead of builders
|
||||
|
||||
component_level_cache = {}
|
||||
# Find the topmost components and build them
|
||||
component_level_cache = permanent_component_level_cache.copy()
|
||||
components_by_level = collections.defaultdict[
|
||||
int, list[rio.Component]
|
||||
](list)
|
||||
@@ -3185,6 +3185,9 @@ a.remove();
|
||||
|
||||
yield from components_to_build_in_this_iteration
|
||||
|
||||
for component in components_to_build_in_this_iteration:
|
||||
permanent_component_level_cache[component] = level_to_build
|
||||
|
||||
async def _refresh(self) -> None:
|
||||
"""
|
||||
Make sure the session state is up to date. Specifically:
|
||||
@@ -4066,9 +4069,9 @@ def find_components_for_reconciliation(
|
||||
|
||||
yield old_component, new_component
|
||||
|
||||
# Compare the children, but make sure to preserve the topology.
|
||||
# Can't just use `iter_direct_children` here, since that would
|
||||
# discard topological information.
|
||||
# Compare the children, but make sure to preserve the topology. Can't
|
||||
# just use `iter_direct_children` here, since that would discard
|
||||
# topological information.
|
||||
#
|
||||
# Also, in this context, "children" means *only* "components stored in
|
||||
# attributes", *not* "tree children". Reconciliation is about component
|
||||
|
||||
@@ -177,7 +177,7 @@ class BrowserClient(BaseClient):
|
||||
marker.style.top = `{y}px`;
|
||||
marker.style.transform = 'translate(-50%, -50%)';
|
||||
document.body.appendChild(marker);
|
||||
|
||||
|
||||
setTimeout(() => {{
|
||||
marker.remove();
|
||||
}}, {sleep} * 1000);
|
||||
|
||||
@@ -58,10 +58,10 @@ async def test_3rd_party_elements_dont_affect_layout(
|
||||
)
|
||||
|
||||
assert user_content_height == pytest.approx(
|
||||
await client.get_window_height()
|
||||
await client.get_window_height(), abs=0.1
|
||||
)
|
||||
|
||||
dev_tools_sidebar_width = await client.get_dev_tools_sidebar_width()
|
||||
assert user_content_width == pytest.approx(
|
||||
await client.get_window_width() - dev_tools_sidebar_width
|
||||
await client.get_window_width() - dev_tools_sidebar_width, abs=0.1
|
||||
)
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
"target": "es2022",
|
||||
"module": "es2022",
|
||||
"lib": ["es2021", "dom", "dom.iterable"],
|
||||
"strict": false, // TODO: fix and enable strict checking
|
||||
"strict": true,
|
||||
"moduleResolution": "node",
|
||||
"skipLibCheck": true,
|
||||
"useDefineForClassFields": false
|
||||
|
||||
Reference in New Issue
Block a user