Skip to content
This repository has been archived by the owner on Aug 15, 2024. It is now read-only.

Commit

Permalink
✨ add session-level state (#29)
Browse files Browse the repository at this point in the history
* ⚡️ switch to gzipped files

* ⚡️ add functional interfaces for components

* minor fixes

* ⚡️ add session state impl

* fix issues with indexing
  • Loading branch information
renardeinside authored Apr 28, 2024
1 parent 7fb41f1 commit e9c6ba9
Show file tree
Hide file tree
Showing 16 changed files with 293 additions and 225 deletions.
36 changes: 18 additions & 18 deletions docs/landing.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
from starlette.responses import FileResponse, JSONResponse

from schorle.app import Schorle
from schorle.component import Component
from schorle.component import Component, component
from schorle.element import div, img, link, span
from schorle.icon import icon
from schorle.tags import HTMLTag
Expand Down Expand Up @@ -55,27 +55,27 @@ def render(self):
]


class LandingPage(Component):
def render(self):
with div(classes="flex flex-col justify-center items-center h-screen", style={"font-family": "Space Mono"}):
with div(classes="max-w-screen-md flex flex-col justify-center items-center space-y-4"):
img(
src="/logo",
alt="Schorle logo",
media_type="image/svg+xml",
classes="w-3/4",
)
with span(classes="text-2xl m-4 p-4 w-5/6 text-center"):
text("Pythonic Server-Driven UI Kit for building modern apps.")

with div(classes="flex flex-col space-y-4 space-x-0 md:flex-row md:space-x-4 md:space-y-0"):
for link_item in LINKS:
link_item()
@component()
def landing():
with div(classes="flex flex-col justify-center items-center h-screen", style={"font-family": "Space Mono"}):
with div(classes="max-w-screen-md flex flex-col justify-center items-center space-y-4"):
img(
src="/logo",
alt="Schorle logo",
media_type="image/svg+xml",
classes="w-3/4",
)
with span(classes="text-2xl m-4 p-4 w-5/6 text-center"):
text("Pythonic Server-Driven UI Kit for building modern apps.")

with div(classes="flex flex-col space-y-4 space-x-0 md:flex-row md:space-x-4 md:space-y-0"):
for link_item in LINKS:
link_item()


@app.get("/")
def landing_page():
return LandingPage()
return landing()


@app.backend.get("/health", response_class=JSONResponse)
Expand Down
25 changes: 9 additions & 16 deletions examples/counter.py
Original file line number Diff line number Diff line change
@@ -1,29 +1,22 @@
from pydantic import Field

from schorle.app import Schorle
from schorle.attrs import On
from schorle.component import Component
from schorle.component import component
from schorle.element import button, div
from schorle.reactive import Reactive
from schorle.text import text

app = Schorle(title="Schorle | Counter App")


class Counter(Component):
value: Reactive[int] = Field(default_factory=Reactive.factory(0))

def initialize(self):
self.value.subscribe(self.rerender)

def render(self):
with div(classes="space-x-4"):
with button(on=On("click", self.value.lazy(self.value.rx + 1)), classes="btn btn-primary"):
text("Increment")
@component(state=Reactive.factory(0))
def counter(state: Reactive[int]):
with div(classes="flex flex-col items-center"):
with button(classes="btn btn-primary", on=On("click", state.lazy(state.rx + 1))):
text("Increment")
with div(classes="text-lg font-semibold text-center m-2"):
text(f"Clicked {self.value.rx} times")
text(f"Clicked {state.rx} times")


@app.get("/")
def home_page():
return Counter()
def index():
return counter()
59 changes: 27 additions & 32 deletions examples/counter_with_loading.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
from pydantic import BaseModel, Field

from schorle.app import Schorle
from schorle.attrs import On
from schorle.component import Component
from schorle.attrs import On, when
from schorle.component import component
from schorle.element import button, div
from schorle.reactive import Reactive
from schorle.text import text
Expand All @@ -29,41 +29,36 @@ async def decrement(self):
await self.value.set(self.value.rx - 1, skip_notify=True)


class Counter(Component):
state: CounterState = Field(default_factory=CounterState)
classes: str = "m-4 w-80 h-32 flex flex-col items-center justify-center bg-base-300 rounded-xl shadow-xl"
@contextmanager
def spinner(loading: Reactive[bool]):
with div(classes=when(loading).then("loading loading-infinity loading-lg text-primary")):
with div(classes=when(loading).then("hidden")):
yield

def initialize(self):
self.state.value.subscribe(self.rerender)
self.state.loading.subscribe(self.rerender)

@contextmanager
def spinner(self, *, loading: bool):
with div(classes="loading loading-infinity loading-lg text-primary" if loading else ""):
with div(classes="hidden" if loading else ""):
yield
@component(
classes="m-4 w-80 h-32 flex flex-col items-center justify-center bg-base-300 rounded-xl shadow-xl",
state=lambda: CounterState(),
)
def counter_with_loading(state: CounterState):
with spinner(state.loading):
with div(classes="space-x-4"):
with button(on=On("click", state.increment), classes="btn btn-primary"):
text("Increment")
with button(
on=On("click", state.decrement),
classes=["btn btn-secondary", when(state.value.rx == 0).then("btn-disabled")],
):
text("Decrement")
with div(classes="text-lg font-semibold text-center m-2"):
text(f"Clicked {state.value.rx} times")

def render(self):
with self.spinner(self.state.loading.rx):
with div(classes="space-x-4"):
with button(on=On("click", self.state.increment), classes="btn btn-primary"):
text("Increment")
with button(
on=On("click", self.state.decrement),
classes="btn btn-secondary" if self.state.value.rx > 0 else "btn btn-secondary btn-disabled",
):
text("Decrement")
with div(classes="text-lg font-semibold text-center m-2"):
text(f"Clicked {self.state.value.rx} times")


class HomePage(Component):
classes: str = "flex flex-col items-center justify-center h-screen"

def render(self):
Counter()
@component(classes="flex flex-col items-center justify-center h-screen")
def index_view():
counter_with_loading()


@app.get("/")
def home():
return HomePage()
return index_view()
86 changes: 35 additions & 51 deletions examples/io.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
from contextlib import contextmanager

from pydantic import Field

from schorle.app import Schorle
from schorle.attrs import Bind, On
from schorle.component import Component
from schorle.component import component
from schorle.element import button, div, h2, input_
from schorle.reactive import Reactive
from schorle.text import text
Expand All @@ -13,61 +11,47 @@
app = Schorle(title="Schorle | IO Examples", theme=Theme.DARK)


class SimpleInput(Component):
current: Reactive[str] = Field(default_factory=Reactive.factory(""))

def initialize(self):
self.current.subscribe(self.rerender)

def render(self):
input_(
type="text",
placeholder="Enter your name",
bind=Bind("value", self.current),
classes="input input-primary w-full",
)
with div(classes="text-center m-2"):
text(f"Hello, {self.current.rx}!") if self.current.rx else text("Hello, stranger!")


class Clearable(Component):
current: Reactive[str] = Field(default_factory=Reactive.factory(""))
classes: str = "flex justify-around"
@component(state=Reactive.factory(""))
def simple_input(state: Reactive[str]):
input_(
type="text",
placeholder="Enter your name",
bind=Bind("value", state),
classes="input input-primary w-full",
)
with div(classes="text-center m-2"):
text(f"Hello, {state.rx}!") if state.rx else text("Hello, stranger!")

def initialize(self):
self.current.subscribe(self.rerender)

def render(self):
input_(
type="text",
placeholder="Enter something here",
bind=Bind("value", self.current),
classes="input input-primary",
)
with button(on=On("click", self.current.lazy("")), classes="btn btn-primary"):
text("Clear")
@component(state=Reactive.factory(""), classes="flex justify-around")
def clearable_input(state: Reactive[str]):
input_(
type="text",
placeholder="Enter something here",
bind=Bind("value", state),
classes="input input-primary",
)
with button(on=On("click", state.lazy("")), classes="btn btn-primary"):
text("Clear")


class Examples(Component):
tag: str = "main"
classes: str = "flex flex-col items-center justify-center h-screen space-y-4"
@contextmanager
def card(title: str):
with div(classes="card w-96 bg-base-300 shadow-xl"):
with div(classes="card-body"):
with h2(classes="card-title"):
text(title)
yield

@staticmethod
@contextmanager
def card(title: str):
with div(classes="card w-96 bg-base-300 shadow-xl"):
with div(classes="card-body"):
with h2(classes="card-title"):
text(title)
yield

def render(self):
with self.card("Simple Input"):
SimpleInput()
with self.card("Clearable Input"):
Clearable()
@component(tag="main", classes="flex flex-col items-center justify-center h-screen space-y-4")
def examples_view():
with card("Simple Input"):
simple_input()
with card("Clearable Input"):
clearable_input()


@app.get("/")
def home():
return Examples()
return examples_view()
Loading

0 comments on commit e9c6ba9

Please sign in to comment.