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