reflex-react-joyride 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,81 @@
1
+ """reflex-react-joyride: guided product tours for Reflex, powered by react-joyride."""
2
+
3
+ from importlib.metadata import PackageNotFoundError
4
+ from importlib.metadata import version as _package_version
5
+
6
+ from . import actions
7
+ from .actions import (
8
+ close,
9
+ get_state,
10
+ go_to,
11
+ next_step,
12
+ open_tooltip,
13
+ prev_step,
14
+ replay,
15
+ reset,
16
+ skip,
17
+ start,
18
+ stop,
19
+ )
20
+ from .constants import (
21
+ ACTIONS,
22
+ EVENTS,
23
+ FINISHED_STATUSES,
24
+ JOYRIDE_VERSION,
25
+ LIFECYCLE,
26
+ ORIGIN,
27
+ PLACEMENTS,
28
+ PORTAL_ELEMENT_ID,
29
+ STATUS,
30
+ )
31
+ from .events import JoyrideEvent, TourState
32
+ from .joyride import Joyride, joyride
33
+ from .props import (
34
+ FloatingOptions,
35
+ Locale,
36
+ SpotlightPadding,
37
+ Step,
38
+ TourOptions,
39
+ TourStyles,
40
+ )
41
+
42
+ # pyproject.toml is the single source of truth for the version; read it back
43
+ # from the installed metadata so the two can never drift.
44
+ try:
45
+ __version__ = _package_version("reflex-react-joyride")
46
+ except PackageNotFoundError: # pragma: no cover - source tree without an install
47
+ __version__ = "0.0.0.dev0"
48
+
49
+ __all__ = [
50
+ "ACTIONS",
51
+ "EVENTS",
52
+ "FINISHED_STATUSES",
53
+ "JOYRIDE_VERSION",
54
+ "LIFECYCLE",
55
+ "ORIGIN",
56
+ "PLACEMENTS",
57
+ "PORTAL_ELEMENT_ID",
58
+ "STATUS",
59
+ "FloatingOptions",
60
+ "Joyride",
61
+ "JoyrideEvent",
62
+ "Locale",
63
+ "SpotlightPadding",
64
+ "Step",
65
+ "TourOptions",
66
+ "TourState",
67
+ "TourStyles",
68
+ "actions",
69
+ "close",
70
+ "get_state",
71
+ "go_to",
72
+ "joyride",
73
+ "next_step",
74
+ "open_tooltip",
75
+ "prev_step",
76
+ "replay",
77
+ "reset",
78
+ "skip",
79
+ "start",
80
+ "stop",
81
+ ]
@@ -0,0 +1,104 @@
1
+ """Drive a running tour from Python.
2
+
3
+ Every ``joyride(...)`` created with a ``tour_id`` registers react-joyride's
4
+ ``controls`` object in the browser under ``window.__reflexJoyride[tour_id]``.
5
+ The helpers below return ``rx.call_script`` events, so they can be used as
6
+ triggers directly or returned from an event handler::
7
+
8
+ rx.button("Start the tour", on_click=rjr.start("main"))
9
+ rx.button("Next", on_click=rjr.next_step("main"))
10
+
11
+ They work whether the tour runs uncontrolled or in controlled mode, and they are
12
+ no-ops (with a console warning) if no tour with that id is mounted.
13
+ """
14
+
15
+ from __future__ import annotations
16
+
17
+ import json
18
+ from typing import Any
19
+
20
+ import reflex as rx
21
+ from reflex.event import EventSpec
22
+
23
+
24
+ def _call(tour_id: str, method: str, *args: Any, callback: Any = None) -> EventSpec:
25
+ """Call a method of the controls object registered under ``tour_id``."""
26
+ js_args = ", ".join(json.dumps(arg) for arg in args)
27
+ script = (
28
+ f"(() => {{ const api = window.__reflexJoyride?.[{json.dumps(tour_id)}];"
29
+ f" if (!api) {{ console.warn('reflex-react-joyride: no tour with id', {json.dumps(tour_id)}); return null; }}"
30
+ f" return api.{method}({js_args}); }})()"
31
+ )
32
+ if callback is not None:
33
+ return rx.call_script(script, callback=callback)
34
+ return rx.call_script(script)
35
+
36
+
37
+ def start(tour_id: str, index: int | None = None) -> EventSpec:
38
+ """Start (or restart) the tour, optionally at a given step index."""
39
+ return _call(tour_id, "start") if index is None else _call(tour_id, "start", index)
40
+
41
+
42
+ def stop(tour_id: str, advance: bool = False) -> EventSpec:
43
+ """Stop the tour. With ``advance=True`` it moves to the next step first."""
44
+ return _call(tour_id, "stop", advance)
45
+
46
+
47
+ def next_step(tour_id: str) -> EventSpec:
48
+ """Advance to the next step."""
49
+ return _call(tour_id, "next")
50
+
51
+
52
+ def prev_step(tour_id: str) -> EventSpec:
53
+ """Go back to the previous step."""
54
+ return _call(tour_id, "prev")
55
+
56
+
57
+ def go_to(tour_id: str, index: int) -> EventSpec:
58
+ """Jump to a specific step index (zero-based)."""
59
+ return _call(tour_id, "go", index)
60
+
61
+
62
+ def close(tour_id: str) -> EventSpec:
63
+ """Close the current step, advancing to the next one."""
64
+ return _call(tour_id, "close")
65
+
66
+
67
+ def skip(tour_id: str) -> EventSpec:
68
+ """End the tour, marking it as skipped."""
69
+ return _call(tour_id, "skip")
70
+
71
+
72
+ def reset(tour_id: str, restart: bool = False) -> EventSpec:
73
+ """Reset the tour to the first step; ``restart=True`` also starts it again."""
74
+ return _call(tour_id, "reset", restart)
75
+
76
+
77
+ def replay(tour_id: str) -> EventSpec:
78
+ """Replay the current step, re-running its hooks and events."""
79
+ return _call(tour_id, "replay")
80
+
81
+
82
+ def open_tooltip(tour_id: str) -> EventSpec:
83
+ """Open the tooltip of the current step (skipping its beacon)."""
84
+ return _call(tour_id, "open")
85
+
86
+
87
+ def get_state(tour_id: str, callback: Any) -> EventSpec:
88
+ """Send the current tour state to ``callback``, an event handler taking a dict."""
89
+ return _call(tour_id, "info", callback=callback)
90
+
91
+
92
+ __all__ = [
93
+ "close",
94
+ "get_state",
95
+ "go_to",
96
+ "next_step",
97
+ "open_tooltip",
98
+ "prev_step",
99
+ "replay",
100
+ "reset",
101
+ "skip",
102
+ "start",
103
+ "stop",
104
+ ]
@@ -0,0 +1,188 @@
1
+ """Constants mirroring the literals exported by react-joyride v3.
2
+
3
+ The values are the exact strings react-joyride emits, so they can be compared
4
+ directly against the payload received by ``on_event`` and friends::
5
+
6
+ from reflex_react_joyride import EVENTS, STATUS
7
+
8
+ @rx.event
9
+ def tour_event(self, event: dict):
10
+ if event["type"] == EVENTS.TOUR_END:
11
+ self.finished = event["status"] == STATUS.FINISHED
12
+ """
13
+
14
+ from __future__ import annotations
15
+
16
+ from typing import Final, Literal
17
+
18
+ #: npm version of react-joyride this component wraps.
19
+ JOYRIDE_VERSION: Final = "3.2.0"
20
+
21
+ #: The id of the portal element react-joyride renders into.
22
+ PORTAL_ELEMENT_ID: Final = "react-joyride-portal"
23
+
24
+
25
+ class ACTIONS:
26
+ """The action that triggered a state update (``event["action"]``)."""
27
+
28
+ INIT: Final = "init"
29
+ START: Final = "start"
30
+ STOP: Final = "stop"
31
+ RESET: Final = "reset"
32
+ PREV: Final = "prev"
33
+ NEXT: Final = "next"
34
+ GO: Final = "go"
35
+ CLOSE: Final = "close"
36
+ SKIP: Final = "skip"
37
+ REPLAY: Final = "replay"
38
+ UPDATE: Final = "update"
39
+ COMPLETE: Final = "complete"
40
+
41
+
42
+ class EVENTS:
43
+ """The type of the event (``event["type"]``)."""
44
+
45
+ TOUR_START: Final = "tour:start"
46
+ STEP_BEFORE_HOOK: Final = "step:before_hook"
47
+ STEP_BEFORE: Final = "step:before"
48
+ SCROLL_START: Final = "scroll:start"
49
+ SCROLL_END: Final = "scroll:end"
50
+ BEACON: Final = "beacon"
51
+ TOOLTIP: Final = "tooltip"
52
+ STEP_AFTER: Final = "step:after"
53
+ STEP_AFTER_HOOK: Final = "step:after_hook"
54
+ TOUR_END: Final = "tour:end"
55
+ TOUR_STATUS: Final = "tour:status"
56
+ TARGET_NOT_FOUND: Final = "error:target_not_found"
57
+ ERROR: Final = "error"
58
+
59
+
60
+ class LIFECYCLE:
61
+ """The rendering phase of the current step (``event["lifecycle"]``)."""
62
+
63
+ INIT: Final = "init"
64
+ READY: Final = "ready"
65
+ BEACON_BEFORE: Final = "beacon_before"
66
+ BEACON: Final = "beacon"
67
+ TOOLTIP_BEFORE: Final = "tooltip_before"
68
+ TOOLTIP: Final = "tooltip"
69
+ COMPLETE: Final = "complete"
70
+
71
+
72
+ class ORIGIN:
73
+ """The UI element that triggered the action (``event["origin"]``)."""
74
+
75
+ BUTTON_BACK: Final = "button_back"
76
+ BUTTON_CLOSE: Final = "button_close"
77
+ BUTTON_PRIMARY: Final = "button_primary"
78
+ BUTTON_SKIP: Final = "button_skip"
79
+ KEYBOARD: Final = "keyboard"
80
+ OVERLAY: Final = "overlay"
81
+
82
+
83
+ class STATUS:
84
+ """The tour's current status (``event["status"]``)."""
85
+
86
+ IDLE: Final = "idle"
87
+ READY: Final = "ready"
88
+ WAITING: Final = "waiting"
89
+ RUNNING: Final = "running"
90
+ PAUSED: Final = "paused"
91
+ SKIPPED: Final = "skipped"
92
+ FINISHED: Final = "finished"
93
+
94
+
95
+ #: Statuses that mean the tour is over, one way or another.
96
+ FINISHED_STATUSES: Final = (STATUS.FINISHED, STATUS.SKIPPED)
97
+
98
+ ActionType = Literal[
99
+ "init", "start", "stop", "reset", "prev", "next", "go", "close", "skip", "replay", "update", "complete"
100
+ ]
101
+ EventType = Literal[
102
+ "tour:start",
103
+ "step:before_hook",
104
+ "step:before",
105
+ "scroll:start",
106
+ "scroll:end",
107
+ "beacon",
108
+ "tooltip",
109
+ "step:after",
110
+ "step:after_hook",
111
+ "tour:end",
112
+ "tour:status",
113
+ "error:target_not_found",
114
+ "error",
115
+ ]
116
+ LifecycleType = Literal["init", "ready", "beacon_before", "beacon", "tooltip_before", "tooltip", "complete"]
117
+ OriginType = Literal["button_back", "button_close", "button_primary", "button_skip", "keyboard", "overlay"]
118
+ StatusType = Literal["idle", "ready", "waiting", "running", "paused", "skipped", "finished"]
119
+
120
+ #: Placements accepted by the beacon and the tooltip.
121
+ Placement = Literal[
122
+ "top",
123
+ "top-start",
124
+ "top-end",
125
+ "bottom",
126
+ "bottom-start",
127
+ "bottom-end",
128
+ "left",
129
+ "left-start",
130
+ "left-end",
131
+ "right",
132
+ "right-start",
133
+ "right-end",
134
+ ]
135
+
136
+ #: ``placement`` also accepts ``auto`` (let floating-ui decide) and ``center``
137
+ #: (a modal-like step with no arrow, useful for a welcome step).
138
+ StepPlacement = Literal[
139
+ "top",
140
+ "top-start",
141
+ "top-end",
142
+ "bottom",
143
+ "bottom-start",
144
+ "bottom-end",
145
+ "left",
146
+ "left-start",
147
+ "left-end",
148
+ "right",
149
+ "right-start",
150
+ "right-end",
151
+ "auto",
152
+ "center",
153
+ ]
154
+
155
+ #: The buttons rendered in the tooltip footer, in order.
156
+ ButtonType = Literal["back", "close", "primary", "skip"]
157
+
158
+ #: What opens the tooltip when the beacon is shown.
159
+ BeaconTrigger = Literal["click", "hover"]
160
+
161
+ #: What the close button does.
162
+ CloseButtonAction = Literal["close", "skip", "replay"]
163
+
164
+ #: What the ESC key does (``False`` disables it).
165
+ DismissKeyAction = Literal["close", "next", "replay"]
166
+
167
+ #: What a click on the overlay does (``False`` disables it).
168
+ OverlayClickAction = Literal["close", "next", "replay"]
169
+
170
+ #: Positioning strategy used by floating-ui.
171
+ Strategy = Literal["absolute", "fixed"]
172
+
173
+ PLACEMENTS: Final = (
174
+ "top",
175
+ "top-start",
176
+ "top-end",
177
+ "bottom",
178
+ "bottom-start",
179
+ "bottom-end",
180
+ "left",
181
+ "left-start",
182
+ "left-end",
183
+ "right",
184
+ "right-start",
185
+ "right-end",
186
+ "auto",
187
+ "center",
188
+ )
@@ -0,0 +1,73 @@
1
+ """The payloads delivered to the component's event triggers.
2
+
3
+ react-joyride's own ``onEvent`` callback receives the merged step object, which
4
+ carries React nodes and functions and cannot cross the wire. The JSX wrapper
5
+ therefore reduces it to the flat, JSON-safe :class:`JoyrideEvent` below, with
6
+ ``snake_case`` keys so it reads naturally from Python::
7
+
8
+ @rx.event
9
+ def on_tour_event(self, event: rjr.JoyrideEvent):
10
+ self.log.append(f"{event['type']} -> step {event['index'] + 1}")
11
+ """
12
+
13
+ from __future__ import annotations
14
+
15
+ from typing import Any, TypedDict
16
+
17
+
18
+ class JoyrideEvent(TypedDict):
19
+ """The payload of every event trigger.
20
+
21
+ It is a plain ``dict`` at runtime, so annotate handlers with ``dict`` if you
22
+ prefer: ``def handler(self, event: dict)``.
23
+ """
24
+
25
+ # The event that fired, e.g. "tour:start" or "step:after". See EVENTS.
26
+ type: str
27
+ # The action that triggered it, e.g. "next", "prev", "close". See ACTIONS.
28
+ action: str
29
+ # The index of the current step, zero-based.
30
+ index: int
31
+ # The total number of steps.
32
+ size: int
33
+ # The tour status: idle, ready, waiting, running, paused, skipped, finished.
34
+ status: str
35
+ # The step's rendering phase: init, ready, beacon, tooltip, complete, ...
36
+ lifecycle: str
37
+ # Which control triggered the action ("button_primary", "overlay", ...), or None.
38
+ origin: str | None
39
+ # Whether the tour is running in controlled mode (``step_index`` is set).
40
+ controlled: bool
41
+ # Whether the tour is waiting for a target to appear or a hook to resolve.
42
+ waiting: bool
43
+ # Whether the page is being scrolled to the target right now.
44
+ scrolling: bool
45
+ # ``index == size - 1``.
46
+ is_last_step: bool
47
+ # The current step's ``id``, if it has one.
48
+ step_id: str | None
49
+ # The current step's ``target`` when it is a CSS selector.
50
+ step_target: str | None
51
+ # The current step's ``title`` when it is a plain string.
52
+ step_title: str | None
53
+ # The current step's ``data``, passed through untouched.
54
+ step_data: Any | None
55
+ # The error message for "error" events, otherwise None.
56
+ error: str | None
57
+
58
+
59
+ class TourState(TypedDict):
60
+ """The tour state returned by :func:`reflex_react_joyride.actions.get_state`."""
61
+
62
+ action: str
63
+ controlled: bool
64
+ index: int
65
+ lifecycle: str
66
+ origin: str | None
67
+ scrolling: bool
68
+ size: int
69
+ status: str
70
+ waiting: bool
71
+
72
+
73
+ __all__ = ["JoyrideEvent", "TourState"]