dash-planet is a Dash component library that provides an interactive orbital menu system for creating engaging circular navigation interfaces. It displays content elements (satellites) in a circular orbit around a central element with smooth spring-based animations. The component offers both free and premium tiers, supporting basic orbital menus with up to 3 satellites for free, and unlimited satellites with advanced features like draggable elements, semicircle layouts, and enhanced animation controls with a premium API key.
Installation
⭐️ Star this component on GitHub! Stay up to date on new releases and browse the codebase.
pip install dash-planet
Quick Start
Create a basic interactive orbital menu with a central avatar and satellite action buttons.
# File: docs/dash_planet/introduction.py
import dash
from dash import html, Input, Output, State, callback, dcc, ALL
from dash_planet import DashPlanet
from dash_iconify import DashIconify
import json
import dash_mantine_components as dmc
import os
from dotenv import load_dotenv
from pathlib import Path
env_path = Path('.') / '.env'
load_dotenv(env_path)
API_KEY = os.getenv("API_KEY")
# Set React version
# _dash_renderer._set_react_version("18.3.1")
# Get API URL from environment or use default
# app = dash.Dash(__name__, suppress_callback_exceptions=True)
# Enable CORS for development
# app.enable_dev_tools(
# dev_tools_hot_reload=True,
# dev_tools_props_check=True,
# dev_tools_serve_dev_bundles=True,
# dev_tools_prune_errors=False
# )
# Rest of your styles remain the same...
styles = {
"container": {
"width": "100%",
"height": "100vh",
"display": "flex",
"flexDirection": "column",
"alignItems": "center",
"justifyContent": "center",
"padding": "20px",
},
"header": {
"textAlign": "center",
"marginBottom": "20px",
"width": "100%",
"maxWidth": "600px",
},
"apiInput": {
"width": "100%",
"maxWidth": "400px",
"padding": "8px",
"marginBottom": "10px",
"border": "1px solid #ccc",
"borderRadius": "4px",
},
"description": {
"fontSize": "14px",
"color": "#666",
"marginBottom": "20px",
"textAlign": "center",
},
"demoArea": {
"display": "flex",
"width": "100%",
"height": "500px", # Fixed height for demo area
"backgroundColor": "white",
"justifyContent": "center",
"alignItems": "center",
"position": "relative",
},
"planet": {
"height": "120px",
"width": "120px",
"borderRadius": "50%",
"backgroundColor": "#1976d2",
"display": "flex",
"justifyContent": "center",
"alignItems": "center",
"color": "white",
"cursor": "pointer",
"transition": "all 0.3s",
"position": "relative",
},
"satellite": {
"height": "40px",
"width": "40px",
# 'borderRadius': '50%',
# 'backgroundColor': '#ff4081',
"display": "flex",
"justifyContent": "center",
"alignItems": "center",
"color": "white",
"cursor": "pointer",
"zIndex": 1,
},
"gridColumn": {
"height": "300px",
"width": "100%",
"display": "flex",
"justifyContent": "center",
"alignItems": "center",
"position": "relative",
"padding": "20px",
"boxSizing": "border-box",
},
}
def generate_satellites(count):
"""Generate satellite elements with icons"""
icons = [
"fxemoji:email",
"noto:calendar",
"emojione-v1:bar-chart",
"emojione:gear",
"flat-color-icons:file",
"emojione-v1:open-folder",
"twemoji:heart-on-fire",
"flat-color-icons:home",
]
return [
html.Div(
[
DashIconify(
icon=icons[i % len(icons)], width=40, height=40, color="white"
)
],
style=styles["satellite"],
id={"type": "satellite", "index": i},
)
for i in range(count)
]
forms_props = dmc.GridCol(
children=[
dmc.Paper(
children=[
dmc.Stack(
[
dmc.Text("Props Control Panel", size="xl", fw=700, ta="center"),
dmc.Grid(
[
dmc.GridCol(
dmc.Stack(
[
# Physics Controls
dmc.Text(
"Physics", fw=700, size="sm", c="dimmed"
),
dmc.Group(
[
dmc.NumberInput(
id="mass-input",
label="Mass",
value=1,
min=1,
max=10,
step=0.5,
style={"width": 100},
),
dmc.NumberInput(
id="tension-input",
label="Tension",
value=200,
min=100,
max=1000,
step=50,
style={"width": 100},
),
dmc.NumberInput(
id="friction-input",
label="Friction",
value=32,
min=1,
max=50,
step=1,
style={"width": 100},
),
]
),
]
),
span={"xs": 12, "md": 6},
),
dmc.GridCol(
dmc.Stack(
[
# Orbit Controls
dmc.Text(
"Orbit",
fw=700,
size="sm",
c="dimmed",
mt="md",
),
dmc.Group(
[
dmc.NumberInput(
id="orbit-radius-input",
label="Radius",
value=80,
min=40,
max=200,
step=10,
style={"width": 100},
),
dmc.NumberInput(
id="rotation-input",
label="Rotation",
value=0,
min=0,
max=360,
step=15,
style={"width": 100},
),
]
),
]
),
span={"xs": 12, "md": 6},
),
],
grow=True,
),
dmc.Grid(
[
dmc.GridCol(
dmc.Stack(
[
dmc.Text(
"Rotation Animation (Works in production not in Docs)",
fw=700,
size="sm",
c="dimmed",
mt="md",
),
dmc.Group(
[
dmc.Switch(
id="animate-rotation-input",
label="Animate Rotation",
checked=False,
),
dmc.NumberInput(
id="rotation-speed-input",
label="Speed",
value=2,
min=0.1,
max=10,
step=0.1,
style={"width": 100},
),
]
),
]
),
span={"xs": 12, "md": 6},
),
dmc.GridCol(
dmc.Stack(
[
# Animation Controls
dmc.Text(
"Animation",
fw=700,
size="sm",
c="dimmed",
mt="md",
),
dmc.Group(
[
dmc.Switch(
id="bounce-input",
label="Bounce",
checked=True,
),
dmc.Switch(
id="hide-orbit-input",
label="Hide Orbit",
checked=True,
),
]
),
]
),
span={"xs": 12, "md": 6},
),
]
),
dmc.Grid(
[
dmc.GridCol(
# Satellite Orientation
dmc.Select(
id="satellite-orientation-input",
label="Satellite Orientation",
data=[
{"value": "DEFAULT", "label": "Default"},
{"value": "INSIDE", "label": "Inside"},
{"value": "OUTSIDE", "label": "Outside"},
{"value": "READABLE", "label": "Readable"},
],
value="DEFAULT",
style={"width": "100%"},
),
span={"xs": 12, "md": 12},
)
]
),
dcc.Interval(
id="rotation-interval",
interval=50, # 50ms = 20fps
disabled=True,
),
]
),
],
p="md",
shadow="sm",
radius="md",
withBorder=True,
style={"maxWidth": "100%", "width": "100%"},
)
],
style={
"height": "100%",
"display": "flex",
"justifyContent": "center",
"alignItems": "center",
"position": "relative",
"overflow": "auto",
},
span={"base": 12, "xl": 4},
)
# Create layout
component = dmc.Box(
[
dmc.Stack(
[
html.H1("DashPlanet Demo", style={"marginBottom": "10px"}),
html.P(
[
"Free tier includes up to 3 satellites. ",
"Enter an API key to unlock all features.",
],
style=styles["description"],
),
dmc.Group(
[
dcc.Input(
id="api-key-input",
type="text",
placeholder="Enter your API key to check if it works",
value="",
style={'display': 'none'}
),
dmc.Switch(
id="use-env-api-key",
label="Use Environment API Key",
checked=False,
),
],
justify="center",
),
html.Div(
id="api-key-status", style={"color": "#666", "marginBottom": "10px"}
),
],
justify="center",
align="center",
gap="md",
),
dmc.Space(h=20),
dmc.Grid(
children=[
forms_props,
dmc.GridCol(
dmc.Stack(
[
dmc.Space(h=50),
dmc.Box(
DashPlanet(
id="demo-planet",
centerContent=dmc.Indicator(
dmc.Avatar(
size="lg",
radius="xl",
src="https://raw.githubusercontent.com/mantinedev/mantine/master/.demo/avatars/avatar-3.png",
),
inline=True,
offset=7,
position="bottom-end",
color="red",
withBorder=True,
size=16,
),
open=True,
orbitRadius=80,
hideOrbit=True,
bounce=True,
bounceOnOpen=True,
rotation=0,
dragablePlanet=True,
dragableSatellites=True,
satelliteOrientation="DEFAULT",
children=generate_satellites(8),
mass=4,
tension=500,
friction=19,
apiKey="",
),
style={
"width": "100%",
"maxWidth": "500px",
"margin": "0 auto",
"display": "flex",
"justifyContent": "center",
"alignItems": "center",
"minHeight": "300px",
}
),
dmc.Space(h=150),
html.Div(
[
# API validation status
html.Div(id="validation-status"),
dmc.Text(
"Click a satellite to see its function",
id="action-text",
ta="center",
),
]
),
],
mt="100px",
),
span={"base": 12, "xl": 4},
style={
"height": "100%",
"display": "flex",
"justifyContent": "center",
"alignItems": "center",
"position": "relative",
"padding": "20px",
"boxSizing": "border-box",
},
),
dmc.GridCol(
dmc.Card(
[
dmc.Text("Features", size="xl", fw=700, mb="md"),
dmc.Stack(
[
dmc.Group(
[
DashIconify(icon="mdi:check", width=20),
dmc.Text("Free Tier: Up to 3 satellites"),
],
gap="xs",
),
dmc.Group(
[
DashIconify(icon="mdi:star", width=20),
dmc.Text("Premium: Unlimited satellites"),
],
gap="xs",
),
dmc.Group(
[
DashIconify(icon="fxemoji:crescentmoon", width=20),
dmc.Text("Premium: Semicircle Menu layout"),
],
gap="xs",
),
dmc.Group(
[
DashIconify(icon="mdi:animation", width=20),
dmc.Text("Premium: Enhanced animation controls"),
],
gap="xs",
),
dmc.Group(
[
DashIconify(icon="fluent-emoji:sparkling-heart", width=20),
dmc.Text("Supports independent Dash Components development"),
],
gap="xs",
),
dmc.Divider(),
dmc.HoverCard(
withArrow=True,
width=200,
shadow="md",
children=[
dmc.HoverCardTarget(
dmc.Group(
[
DashIconify(icon="cib:buy-me-a-coffee", width=20),
dmc.Anchor(
"Buy a DashPlanet API key",
href="https://plotly.pro/product/prod_SY2xOUihEmOKda",
target="_blank",
size="md",
),
],
gap="xs",
)
),
dmc.HoverCardDropdown(
dmc.Image(
radius="md",
src="/assets/images/tippy.png",
)
),
],
),
],
gap="sm",
),
],
p="xl",
shadow="sm",
radius="md",
withBorder=True,
style={"width": "100%"}
),
style={
"display": "flex",
"justifyContent": "center",
"alignItems": "center",
"position": "relative",
"padding": "20px",
"boxSizing": "border-box",
},
span={"base": 12, "xl": 4},
),
],
gutter="xl",
),
]
)
# Update callbacks
@callback(
[
Output("demo-planet", "apiKey"),
Output("api-key-input", "disabled"),
Output("api-key-status", "children"),
Output("api-key-status", "style"),
],
[
Input("api-key-input", "value"),
Input("use-env-api-key", "checked")
],
prevent_initial_call=True,
)
def update_api_key(api_key, use_env_key):
"""Update API key based on input or environment variable"""
if use_env_key:
return API_KEY, True, "Demo using a paid API key", {"color": "#4CAF50"}
if not api_key:
return None, False, "Using free tier", {"color": "#666"}
return api_key, False, f"Using API key: {api_key[:8]}...", {"color": "#4CAF50"}
@callback(
Output("demo-planet", "open"),
Input("demo-planet", "n_clicks"),
prevent_initial_call=True,
)
def toggle_planet(n_clicks):
"""Toggle planet open/closed state"""
if n_clicks is None:
return dash.no_update
return n_clicks % 2 == 1
# Add this callback to handle satellite clicks
@callback(
Output("action-text", "children"),
Input({"type": "satellite", "index": ALL}, "n_clicks"),
prevent_initial_call=True,
)
def handle_satellite_click(clicks):
ctx = dash.callback_context
if not ctx.triggered:
return "Click a satellite to see its function"
triggered_id = ctx.triggered[0]["prop_id"].split(".")[0]
satellite_index = json.loads(triggered_id)["index"]
# Map indices to actions
actions = [
"Compose new email",
"Open calendar",
"View analytics",
"Open settings",
"New document",
"Browse files",
"Favorite item",
"Return home",
]
return f"Selected: {actions[satellite_index % len(actions)]}"
# Add these callbacks after your existing callbacks:
@callback(
Output("demo-planet", "mass"),
Input("mass-input", "value"),
prevent_initial_call=True,
)
def update_mass(value):
return value
@callback(
Output("demo-planet", "tension"),
Input("tension-input", "value"),
prevent_initial_call=True,
)
def update_tension(value):
return value
@callback(
Output("demo-planet", "friction"),
Input("friction-input", "value"),
prevent_initial_call=True,
)
def update_friction(value):
return value
@callback(
Output("demo-planet", "orbitRadius"),
Input("orbit-radius-input", "value"),
prevent_initial_call=True,
)
def update_orbit_radius(value):
return value
@callback(
Output("demo-planet", "rotation"),
Input("rotation-input", "value"),
prevent_initial_call=True,
)
def update_rotation(value):
return value
@callback(
Output("demo-planet", "bounce"),
Input("bounce-input", "checked"),
prevent_initial_call=True,
)
def update_bounce(checked):
return checked
@callback(
Output("demo-planet", "hideOrbit"),
Input("hide-orbit-input", "checked"),
prevent_initial_call=True,
)
def update_hide_orbit(checked):
return checked
@callback(
Output("demo-planet", "satelliteOrientation"),
Input("satellite-orientation-input", "value"),
prevent_initial_call=True,
)
def update_satellite_orientation(value):
return value
@callback(
[Output("rotation-interval", "disabled"), Output("rotation-input", "disabled")],
Input("animate-rotation-input", "checked"),
)
def toggle_animation(animate):
return not animate, animate
# Callback to update rotation based on the interval
@callback(
Output("demo-planet", "rotation", allow_duplicate=True),
[Input("rotation-interval", "n_intervals"), Input("rotation-speed-input", "value")],
State("demo-planet", "rotation"),
prevent_initial_call=True,
)
def update_rotation(n_intervals, speed, current_rotation):
if current_rotation is None:
current_rotation = 0
# Calculate new rotation angle
new_rotation = (current_rotation + speed) % 360
return new_rotation
# Callback to handle manual rotation input
@callback(
Output("rotation-input", "disabled", allow_duplicate=True),
Input("animate-rotation-input", "checked"),
prevent_initial_call=True,
)
def toggle_rotation_input(animate):
return animate
Basic Code Example
Here's a minimal example to get started with DashPlanet:
from dash import Dash
from dash_planet import DashPlanet
import dash_mantine_components as dmc
from dash_iconify import DashIconify
app = Dash(__name__)
app.layout = DashPlanet(
id='my-planet',
centerContent=dmc.Avatar(
size="lg",
radius="xl",
src="path/to/avatar.png"
),
children=[
dmc.ActionIcon(
DashIconify(icon="clarity:settings-line", width=20, height=20),
size="lg",
variant="filled",
id="action-icon-1",
),
dmc.ActionIcon(
DashIconify(icon="mdi:email", width=20, height=20),
size="lg",
variant="filled",
id="action-icon-2",
),
dmc.ActionIcon(
DashIconify(icon="mdi:bell", width=20, height=20),
size="lg",
variant="filled",
id="action-icon-3",
),
],
orbitRadius=80,
rotation=0,
)
if __name__ == '__main__':
app.run_server(debug=True)
Semicircle Menu Layout
Premium feature that displays satellites in a semicircle layout instead of full circular orbit. Perfect for creating arc-shaped menus and navigation bars.
# File: docs/dash_planet/semicircle_example.py
import dash
from dash import html, Input, Output, State, callback, dcc
from dash_planet import DashPlanet
from dash_iconify import DashIconify
import dash_mantine_components as dmc
import os
from dotenv import load_dotenv
from pathlib import Path
env_path = Path('.') / '.env'
load_dotenv(env_path)
API_URL = os.getenv("API_URL")
API_KEY = os.getenv("API_KEY")
styles = {
'root': {
'display': 'flex',
'flex': '1',
'width': '100%',
'justifyContent': 'center',
'alignItems': 'center',
'flexDirection': 'column',
'position': 'relative',
'gap': '20px'
},
'satellite': {
'height': '40px',
'width': '40px',
'display': 'flex',
'justifyContent': 'center',
'alignItems': 'center',
'color': 'white',
'cursor': 'pointer',
'zIndex': 1
}
}
def generate_satellites(count, empty_divs=5):
"""Generate satellite elements with icons"""
icons = [
"fxemoji:email",
"noto:calendar",
"emojione-v1:bar-chart",
"emojione:gear",
"flat-color-icons:file",
"emojione-v1:open-folder",
"twemoji:heart-on-fire",
"flat-color-icons:home",
]
empty_div_style = {
'width': '40px',
'height': '40px',
'transition': 'transform 0.3s ease-in-out'
}
return [
html.Div([
DashIconify(icon=icons[i % len(icons)], width=40, height=40, color="white")
], style=styles['satellite'], id={'type': 'satellite', 'index': i})
for i in range(count)
] + [html.Div(style=empty_div_style) for _ in range(empty_divs)]
component = dmc.Box([
dcc.Input(
id='api-key-input',
type='text',
value='O3iEIQMkVzbbdgs-ZSfBotNt3WoLhqGjID0fMrhuN64',
style={'display': 'none'}
),
dmc.Grid([
# Add form controls
dmc.GridCol([
dmc.Paper([
dmc.Text("Menu Semicircle Controls", size="lg", fw=500, ta="center", mb="md"),
dmc.NumberInput(
id="menu-planet-empty-divs-input",
label="Number of Empty Divs",
value=5,
min=0,
max=10,
step=1,
mb="sm"
),
dmc.NumberInput(
id="menu-planet-rotation-input",
label="Rotation (degrees)",
value=0,
min=0,
max=360,
step=45,
mb="sm"
),
dmc.NumberInput(
id="menu-planet-orbit-radius-input",
label="Orbit Radius",
value=80,
min=40,
max=200,
step=10,
mb="sm"
),
], p="md", shadow="sm", radius="md", withBorder=True, style={"width": "100%"})
], span={'base': 12, 'lg': 5}),
dmc.GridCol([
dmc.Box(
DashPlanet(
id='menu-planet',
centerContent=dmc.Indicator(
dmc.Avatar(
size="lg",
radius="xl",
src="https://raw.githubusercontent.com/mantinedev/mantine/master/.demo/avatars/avatar-3.png",
),
inline=True,
offset=7,
position="bottom-end",
color="red",
withBorder=True,
size=16,
),
open=True,
orbitRadius=80,
hideOrbit=True,
bounce=True,
bounceOnOpen=True,
rotation=90,
dragablePlanet=True,
dragableSatellites=True,
satelliteOrientation='DEFAULT',
children=generate_satellites(3),
mass=4,
tension=500,
friction=19,
apiKey=API_KEY
),
style={
"width": "100%",
"maxWidth": "500px",
"margin": "0 auto",
"display": "flex",
"justifyContent": "center",
"alignItems": "center",
"minHeight": "300px",
}
)
], style={
'display': 'flex',
'flex': '1',
'width': '100%',
'justifyContent': 'center',
'alignItems': 'center',
'flexDirection': 'column',
'position': 'relative',
'gap': '20px',
'padding': '20px',
'boxSizing': 'border-box'
}, span={'base': 12, 'lg': 7}),
], gutter="md")
])
@callback(
Output('menu-planet', 'children'),
Output('menu-planet', 'rotation'),
Input('menu-planet-empty-divs-input', 'value'),
Input('menu-planet-rotation-input', 'value')
)
def update_satellites(empty_divs, rotation):
return generate_satellites(3, empty_divs), rotation
@callback(
Output("menu-planet", "orbitRadius"),
Input("menu-planet-orbit-radius-input", "value"),
prevent_initial_call=True,
)
def update_orbit_radius(value):
return value
Free vs Premium Features
DashPlanet offers both free and premium tiers to suit different project needs.
| Free Tier | Premium Features |
|---|---|
| ✓ Up to 3 satellite elements in orbit | 🌟 Unlimited satellite elements |
| ✓ Basic orbital animation | 🌙 Semicircle Menu layout |
| ✓ Customizable orbit radius and rotation | ⚡ Enhanced animation controls |
| ✓ Click-to-toggle functionality | 💎 Draggable satellites and center element |
| 🎯 Bounce animations with directional control | |
| 🔄 Advanced satellite orientation options |
Get Premium Access: Buy DashPlanet API Key
To use premium features, provide your API key:
DashPlanet(
apiKey="your-api-key-here",
# Premium features now available
dragableSatellites=True,
bounce=True,
bounceDirection="TOP",
)
Working with Callbacks
Toggle Orbital Menu:
Control the visibility of satellite elements using the open property and n_clicks callback.
from dash import Input, Output, callback
@callback(
Output("my-planet", "open"),
Input("my-planet", "n_clicks")
)
def toggle_planet(n_clicks):
"""Toggle satellite visibility on center element click"""
if n_clicks is None:
return False
return n_clicks % 2 == 1
Dynamic Rotation Control:
Update the orbital rotation angle dynamically based on user input.
@callback(
Output("my-planet", "rotation"),
Input("rotation-slider", "value")
)
def update_rotation(value):
"""Rotate the entire orbital system"""
return value
Satellite Click Handling:
Respond to clicks on individual satellite elements by giving each satellite a unique ID.
@callback(
Output("output-div", "children"),
Input("satellite-1", "n_clicks"),
Input("satellite-2", "n_clicks"),
Input("satellite-3", "n_clicks"),
)
def handle_satellite_clicks(n1, n2, n3):
"""Handle clicks on different satellites"""
ctx = callback_context
if not ctx.triggered:
return "Click a satellite"
button_id = ctx.triggered[0]["prop_id"].split(".")[0]
return f"Clicked: {button_id}"
Customizing Satellite Elements
Satellites can be any valid Dash component, allowing for rich, interactive menu items.
Using Icons:
from dash_iconify import DashIconify
from dash import html
satellites = [
html.Div([
DashIconify(
icon="mdi:email",
width=40,
height=40,
color="#3b82f6"
)
], style={'width': '40px', 'height': '40px'})
for _ in range(3)
]
Using Mantine Components:
import dash_mantine_components as dmc
satellites = [
dmc.ActionIcon(
dmc.ThemeIcon(DashIconify(icon="mdi:home")),
size="lg",
variant="filled",
color="blue",
),
dmc.ActionIcon(
dmc.ThemeIcon(DashIconify(icon="mdi:settings")),
size="lg",
variant="filled",
color="green",
),
]
Animation Controls
Fine-tune the spring physics animation using mass, tension, and friction properties.
DashPlanet(
mass=4, # Higher mass = slower, heavier animation
tension=500, # Higher tension = stiffer spring, faster animation
friction=19, # Higher friction = more damping, less bounce
)
Animation Parameter Guide:
mass: Controls the "weight" of the animation (default: 1)- Lower values (0.5-1): Light, quick animations
- Higher values (2-5): Heavy, slower animations
tension: Controls spring stiffness (default: 500)- Lower values (100-300): Looser, more elastic
- Higher values (500-1000): Tighter, snappier
friction: Controls damping/resistance (default: 17)- Lower values (5-15): More bouncy, oscillating
- Higher values (20-30): Smoother, more damped
Styling and Appearance
Component Styling:
Apply custom styles to the container using the style prop:
DashPlanet(
style={
'backgroundColor': '#f8f9fa',
'borderRadius': '50%',
'boxShadow': '0 4px 6px rgba(0, 0, 0, 0.1)',
'padding': '20px',
}
)
Hiding the Orbit Line:
Toggle the visibility of the orbital path line:
DashPlanet(
hideOrbit=True, # Hide the circular orbit line
)
Satellite Orientation (Premium):
Control how satellites rotate as they orbit:
DEFAULT: No rotation, satellites maintain upright positionINSIDE: Satellites face toward the centerOUTSIDE: Satellites face away from the centerREADABLE: Satellites rotate to remain readable (top half upright, bottom half inverted)
DashPlanet(
apiKey="your-key",
satelliteOrientation="READABLE",
)
Browser Support
DashPlanet is compatible with modern browsers that support CSS transforms and React Spring animations:
- Chrome (latest)
- Firefox (latest)
- Safari (latest)
- Edge (latest)
Minimum Requirements:
dash≥ 2.0.0react≥ 18.3.1
Component Properties
| Property | Type | Default | Description |
|---|---|---|---|
id | string | Required | Unique identifier for the component used in Dash callbacks. |
centerContent | node | None | Content displayed in the center of the orbit (e.g., avatar, button, icon). |
children | node | None | Satellite elements to be displayed in orbit around the center. Can be any valid Dash components. |
open | bool | False | Controls visibility of satellite elements. Set to True to show satellites, False to hide. |
orbitRadius | number | 120 | Radius of the orbit in pixels. Determines how far satellites are from the center. |
rotation | number | 0 | Initial rotation angle of the orbital system in degrees. Use to offset starting positions. |
hideOrbit | bool | False | If True, hides the circular orbit line. Satellites still orbit, but the path is invisible. |
mass | number | 1 | Mass parameter for spring physics animation. Higher values create heavier, slower animations. |
tension | number | 500 | Spring tension parameter. Higher values create stiffer, faster animations. |
friction | number | 17 | Spring friction parameter. Higher values create more damping and reduce bounce. |
style | dict | {} | Standard CSS styles to apply to the component container. |
className | string | None | CSS class name to apply to the component container. |
n_clicks | number | 0 | Number of times the center element has been clicked. Updates automatically on click. |
apiKey | string | None | API key for unlocking premium features. Purchase at plotly.pro |
dragablePlanet | bool | False | (Premium) Enable dragging of the center element. Requires valid apiKey. |
dragableSatellites | bool | False | (Premium) Enable dragging of satellite elements. Requires valid apiKey. |
bounce | bool | False | (Premium) Enable bounce animation effect. Requires valid apiKey. |
bounceDirection | string | "TOP" | (Premium) Direction of bounce animation. Options: "TOP", "BOTTOM", "LEFT", "RIGHT". Requires apiKey. |
satelliteOrientation | string | "DEFAULT" | (Premium) How satellites rotate in orbit. Options: "DEFAULT", "INSIDE", "OUTSIDE", "READABLE". Requires apiKey. |
setProps | func | (Dash Internal) | Callback function to update component properties. |
loading_state | object | (Dash Internal) | Object describing the loading state of the component or its props. |
Note: Premium features marked with (Premium) require a valid API key. Free tier is limited to 3 satellite elements maximum.
Contributing
Contributions to dash-planet are welcome! Please refer to the project's issues on GitHub for any feature requests or bug reports.
License
This project is licensed under the MIT License.