-
|
I am trying to use @wavesurfer/react component in a Reflex app. Otherwise component wrapping seems straightforward but I do not understand what I should do e.g. to attach a Reflex button Here is the sample code I am playing with: import reflex as rx
class WaveSurfer(rx.NoSSRComponent):
library = "@wavesurfer/react"
tag = "WavesurferPlayer"
is_default = True
url: rx.Var[str]
drag_to_seek: rx.Var[bool] = True
media_controls: rx.Var[bool] = False
on_ready: rx.EventHandler[lambda surfer: [surfer]]
on_decode: rx.EventHandler[lambda surfer: [surfer]]
on_play: rx.EventHandler[lambda surfer: [surfer]]
on_pause: rx.EventHandler[lambda surfer: [surfer]]
@classmethod
def create(cls, *children, **props):
return rx.box(super().create(*children, **props), width="100%")
wavesurfer = WaveSurfer.create
@rx.page(route="/")
def index() -> rx.Component:
return rx.container(
rx.vstack(
wavesurfer(
url="https://www.mfiles.co.uk/mp3-downloads/brahms-st-anthony-chorale-theme-two-pianos.mp3",
# ws is the player instance the react component creates
on_ready=lambda ws: rx.console_log(f"{ws}"),
on_decode=rx.console_log("Decode"),
on_play=rx.console_log("Play"),
on_pause=rx.console_log("Pause"),
),
rx.button(
rx.icon("play"),
# this should make a call to ws.play()
on_click=rx.console_log("Click"),
),
),
justify="center",
min_height="85vh",
)
app = rx.App() |
Beta Was this translation helpful? Give feedback.
Replies: 1 comment
-
|
in this case, you need to save the instance of the wavesurfer as a global ref, so the functions can be called imperatively from other event triggers. here is a partially working example, although getting the status (isReady/isPlaying) doesn't seem to work and i didn't have time to figure out why. """
1. Use the hook interface inside a normal div with MemoizationLeaf to create a component with a local wavesurfer variable.
2. Automatically generate an ID for the div if one is not provided so each instance can be handled separately.
3. Store the resulting wavesurfer instance in a global ref to allow other components to access it.
4. Expose a helper API to call methods on the wavesurfer instance from other components like an event handler.
"""
import reflex as rx
from reflex.components.component import MemoizationLeaf
from reflex.components.el.elements.typography import Div
from reflex.event import EventSpec, no_args_event_spec
USE_WAVESURFER = rx.vars.FunctionStringVar.create(
"useWavesurfer",
_var_data=rx.vars.VarData(
imports={
"@wavesurfer/react": ["useWavesurfer"],
},
position=rx.constants.Hooks.HookPosition.POST_TRIGGER,
)
)
class WaveSurfer(Div, MemoizationLeaf):
url: rx.Var[str]
drag_to_seek: rx.Var[bool] = rx.Var.create(True)
media_controls: rx.Var[bool] = rx.Var.create(False)
on_ready: rx.EventHandler[no_args_event_spec]
on_decode: rx.EventHandler[no_args_event_spec]
on_play: rx.EventHandler[no_args_event_spec]
on_pause: rx.EventHandler[no_args_event_spec]
def _exclude_props(self):
# These props are used to initialize wavesurfer and should not be passed to the div.
return super()._exclude_props() + [
"url", "drag_to_seek", "media_controls",
"on_ready", "on_decode", "on_play", "on_pause",
]
@classmethod
def create(cls, *children, **props) -> "WaveSurfer":
id = props.pop("id", rx.vars.get_unique_variable_name())
return super().create(
*children,
id=id,
width=props.pop("width", "100%"),
**props,
)
def add_hooks(self) -> list[str | rx.Var]:
if (ref_name := self.get_ref()) is None:
raise ValueError("WaveSurfer component must have a ref (id) to initialize wavesurfer.")
wavesurfer_options = rx.Var.create(
{
"container": rx.Var(ref_name),
"url": self.url,
"dragToSeek": self.drag_to_seek,
"mediaControls": self.media_controls,
"onReady": self.event_triggers["on_ready"],
"onDecode": self.event_triggers["on_decode"],
"onPlay": self.event_triggers["on_play"],
"onPause": self.event_triggers["on_pause"],
}
)
return [
rx.Var(f"refs['wavesurfer_{self.id}'] = {USE_WAVESURFER(wavesurfer_options)}")
]
def _ws_instance(self) -> rx.vars.ObjectVar:
return rx.vars.ObjectVar(
_js_expr=f"refs['wavesurfer_{self.id}']?.wavesurfer",
_var_type=dict,
_var_data=rx.Var(self.id)._as_ref()._get_all_var_data(),
)
def play(self) -> EventSpec:
return rx.call_function(
rx.vars.function.ArgsFunctionOperation.create(
args_names=(),
return_expr=self._ws_instance().play.to(rx.vars.FunctionVar)(),
)
)
def playPause(self) -> EventSpec:
return rx.call_function(
rx.vars.function.ArgsFunctionOperation.create(
args_names=(),
return_expr=self._ws_instance().playPause.to(rx.vars.FunctionVar)(),
)
)
wavesurfer = WaveSurfer.create
@rx.page(route="/")
def index() -> rx.Component:
ws = wavesurfer(
url="https://www.mfiles.co.uk/mp3-downloads/brahms-st-anthony-chorale-theme-two-pianos.mp3",
on_ready=rx.console_log("READY"),
on_decode=rx.console_log("Decode"),
on_play=rx.console_log("Play"),
on_pause=rx.console_log("Pause"),
)
return rx.container(
rx.vstack(
ws,
rx.button(
rx.icon("Play"),
on_click=ws.playPause(),
),
),
justify="center",
min_height="85vh",
)
app = rx.App()
app.add_page(index) |
Beta Was this translation helpful? Give feedback.
in this case, you need to save the instance of the wavesurfer as a global ref, so the functions can be called imperatively from other event triggers. here is a partially working example, although getting the status (isReady/isPlaying) doesn't seem to work and i didn't have time to figure out why.