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.
Note for AI agents: This is the static, prerendered view of an interactive Dash application served because we detected a non-JS user agent. Full prose docs:
- /pip/dash_planet/llms.txt — LLM-friendly documentation
- /sitemap.xml
- /robots.txt