Skip to content

Commit 894fdd7

Browse files
committed
Add plotly cloud placeholder button
1 parent a537e41 commit 894fdd7

File tree

12 files changed

+265
-17
lines changed

12 files changed

+265
-17
lines changed
Lines changed: 1 addition & 0 deletions
Loading
Lines changed: 1 addition & 3 deletions
Loading
Lines changed: 1 addition & 3 deletions
Loading

dash/dash-renderer/src/components/error/menu/DebugMenu.css

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,7 @@
112112
}
113113

114114
.dash-debug-menu:hover {
115-
background-color: #108de4;
115+
background-color: #7f4bc4;
116116
}
117117

118118
.dash-debug-menu__outer {
@@ -208,13 +208,12 @@
208208
align-items: center;
209209
transition: background-color 0.2s;
210210
font-family: Verdana, sans-serif !important;
211-
font-weight: bold;
212-
color: black;
211+
color: #7f4bc4;
213212
}
214213

215214
.dash-debug-menu__button.dash-debug-menu__button--selected {
216215
color: #7f4bc4;
217-
box-shadow: 0 2px #0071c2;
216+
box-shadow: 0 2px #7f4bc4;
218217
}
219218
.dash-debug-menu__button.dash-debug-menu__button--selected:hover {
220219
color: #5806c4;
@@ -253,3 +252,9 @@
253252
font-size: 14px;
254253
margin-left: 3px;
255254
}
255+
.dash-debug-menu__icon {
256+
color: #9ca3af;
257+
}
258+
.dash-debug-menu__button:hover .dash-debug-menu__icon {
259+
color: #5806c4;
260+
}

dash/dash-renderer/src/components/error/menu/DebugMenu.react.js

Lines changed: 26 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import React, {useEffect, useState} from 'react';
22
import PropTypes from 'prop-types';
33
import {concat} from 'ramda';
4+
import {useSelector} from 'react-redux';
45

56
import './DebugMenu.css';
67

@@ -14,7 +15,8 @@ import {VersionInfo} from './VersionInfo.react';
1415
import {CallbackGraphContainer} from '../CallbackGraph/CallbackGraphContainer.react';
1516
import {FrontEndErrorContainer} from '../FrontEnd/FrontEndErrorContainer.react';
1617
import ExternalWrapper from '../../../wrapper/ExternalWrapper';
17-
import {useSelector} from 'react-redux';
18+
import PlotlyCloud from './PlotlyCloud';
19+
import {DevtoolProvider, useDevtool} from './DevtoolContext';
1820

1921
const classes = (base, variant, variant2) =>
2022
`${base} ${base}--${variant}` + (variant2 ? ` ${base}--${variant2}` : '');
@@ -75,6 +77,7 @@ const MenuContent = ({
7577
return (
7678
<div className='dash-debug-menu__content'>
7779
{custom && <>{custom.left}</>}
80+
{!config.plotly_cloud_installed ? <PlotlyCloud /> : null}
7881
<button
7982
onClick={toggleErrors}
8083
className={
@@ -84,7 +87,12 @@ const MenuContent = ({
8487
}
8588
id='dash-debug-menu__errors-button'
8689
>
87-
<ErrorIcon className='dash-debug-menu__icon' />
90+
<ErrorIcon
91+
className='dash-debug-menu__icon'
92+
width={16}
93+
height={16}
94+
fill='currentColor'
95+
/>
8896
Errors
8997
{errCount > 0 ? (
9098
<span className='test-devtools-error-count dash-debug-menu__error-count'>
@@ -101,7 +109,12 @@ const MenuContent = ({
101109
}
102110
id='dash-debug-menu__callback-graph-button'
103111
>
104-
<GraphIcon className='dash-debug-menu__icon' />
112+
<GraphIcon
113+
className='dash-debug-menu__icon'
114+
width={16}
115+
height={16}
116+
fill='currentColor'
117+
/>
105118
Callbacks
106119
</button>
107120
<div className='dash-debug-menu__divider' />
@@ -122,8 +135,8 @@ const MenuContent = ({
122135
);
123136
};
124137

125-
const DebugMenu = ({error, hotReload, config, children}) => {
126-
const [popup, setPopup] = useState('errors');
138+
const Debug = ({error, hotReload, config, children}) => {
139+
const {popup, setPopup} = useDevtool();
127140
const [collapsed, setCollapsed] = useState(isCollapsed);
128141

129142
const errCount = error.frontEnd.length + error.backEnd.length;
@@ -207,6 +220,14 @@ const DebugMenu = ({error, hotReload, config, children}) => {
207220
);
208221
};
209222

223+
const DebugMenu = ({children, ...props}) => {
224+
return (
225+
<DevtoolProvider>
226+
<Debug {...props}>{children}</Debug>
227+
</DevtoolProvider>
228+
);
229+
};
230+
210231
DebugMenu.propTypes = {
211232
children: PropTypes.object,
212233
error: PropTypes.object,
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import React, {useContext, useMemo, useState} from 'react';
2+
3+
export const DevtoolContext = React.createContext({});
4+
5+
export const DevtoolProvider = ({children}) => {
6+
const [popup, setPopup] = useState('');
7+
8+
return (
9+
<DevtoolContext.Provider
10+
value={{
11+
popup,
12+
setPopup
13+
}}
14+
>
15+
{children}
16+
</DevtoolContext.Provider>
17+
);
18+
};
19+
20+
export const useDevtool = () => {
21+
return useContext(DevtoolContext);
22+
};
23+
24+
export const useDevtoolMenuButtonClassName = popupName => {
25+
const {popup} = useDevtool();
26+
27+
const className = useMemo(() => {
28+
const base = 'dash-debug-menu__button';
29+
if (popup === popupName) {
30+
return base + ' dash-debug-menu__button--selected';
31+
}
32+
return base;
33+
}, [popup]);
34+
35+
return className;
36+
};
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
/*
2+
Same as in plotly-cloud but without the publish
3+
in the name to avoid future conflicts if changed
4+
upstream.
5+
*/
6+
.plotly-cloud-modal-overlay {
7+
position: absolute;
8+
bottom: 100%;
9+
left: -1px;
10+
z-index: 10000;
11+
}
12+
13+
.plotly-cloud-modal-content {
14+
border-radius: 8px;
15+
width: 522px;
16+
background: white;
17+
border: 1px solid #d1d5db;
18+
border-radius: 4px 4px 0 0;
19+
box-shadow: 0 -4px 6px rgba(0, 0, 0, 0.08);
20+
overflow: hidden;
21+
}
22+
23+
.plotly-cloud-modal-header {
24+
display: flex;
25+
justify-content: space-between;
26+
align-items: center;
27+
padding: 16px 20px;
28+
border-bottom: 1px solid #e5e5e5;
29+
background: #f9fafb;
30+
}
31+
32+
.plotly-cloud-modal-header h3 {
33+
margin: 0;
34+
color: #333;
35+
}
36+
37+
.plotly-cloud-modal-close {
38+
background: none;
39+
border: none;
40+
font-size: 24px;
41+
cursor: pointer;
42+
color: #666;
43+
padding: 0;
44+
width: 30px;
45+
height: 30px;
46+
display: flex;
47+
align-items: center;
48+
justify-content: center;
49+
}
50+
51+
.plotly-cloud-modal-close:hover {
52+
color: #333;
53+
}
54+
55+
.plotly-cloud-modal-body {
56+
padding: 20px;
57+
color: black !important;
58+
font-weight: 100;
59+
}
60+
button.plotly-cloud-modal-button {
61+
display: inline-flex;
62+
align-items: center;
63+
gap: 6px;
64+
padding: 6px 10px !important;
65+
border-radius: 4px !important;
66+
font-size: 12px !important;
67+
font-weight: 600 !important;
68+
cursor: pointer;
69+
border: 1px solid transparent;
70+
background: #8b5cf6;
71+
color: white;
72+
border-color: #7c3aed;
73+
}
74+
75+
.plotly-cloud-modal-button:disabled {
76+
cursor: not-allowed;
77+
}
78+
.plotly-cloud-copy-install {
79+
display: flex;
80+
align-items: center;
81+
justify-content: space-between;
82+
gap: 10px;
83+
padding: 8px 10px;
84+
background: #f9fafb;
85+
border: 1px solid #e5e7eb;
86+
border-radius: 6px;
87+
margin-top: 8px;
88+
}
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
import React, {useMemo} from 'react';
2+
3+
import {useDevtool, useDevtoolMenuButtonClassName} from './DevtoolContext';
4+
5+
import './PlotlyCloud.css';
6+
import CloudSlashIcon from '../icons/CloudSlashIcon.svg';
7+
8+
const CLOUD_POPUP = 'cloud';
9+
10+
const PlotlyCloud = () => {
11+
const {popup, setPopup} = useDevtool();
12+
13+
const className = useDevtoolMenuButtonClassName(CLOUD_POPUP);
14+
15+
const isOpen = useMemo(() => popup === CLOUD_POPUP, [popup]);
16+
17+
const onClick = () => {
18+
if (popup === CLOUD_POPUP) {
19+
setPopup('');
20+
} else {
21+
setPopup(CLOUD_POPUP);
22+
}
23+
};
24+
25+
const onCopy = () => {
26+
navigator.clipboard.writeText('pip install "dash[cloud]"');
27+
};
28+
29+
return (
30+
<>
31+
<button className={className} onClick={onClick}>
32+
<CloudSlashIcon
33+
className='dash-debug-menu__icon'
34+
width={16}
35+
height={16}
36+
fill='currentColor'
37+
/>{' '}
38+
Plotly Cloud
39+
</button>
40+
{isOpen ? (
41+
<div className='plotly-cloud-modal-overlay'>
42+
<div className='plotly-cloud-modal-content'>
43+
<div className='plotly-cloud-modal-header'>
44+
<h3 key='modal-title'>Plotly Cloud</h3>
45+
<button
46+
key='modal-close'
47+
className='plotly-cloud-modal-close'
48+
onClick={() => setPopup('')}
49+
>
50+
×
51+
</button>
52+
</div>
53+
<div
54+
key='modal-body'
55+
className='plotly-cloud-modal-body'
56+
>
57+
<div>
58+
Install the extension to publish to Plotly
59+
Cloud.
60+
</div>
61+
<div className='plotly-cloud-copy-install'>
62+
<span>{'pip install "dash[cloud]"'}</span>
63+
<button
64+
onClick={onCopy}
65+
className='plotly-cloud-modal-button'
66+
>
67+
Copy
68+
</button>
69+
</div>
70+
</div>
71+
</div>
72+
</div>
73+
) : null}
74+
</>
75+
);
76+
};
77+
78+
export default PlotlyCloud;

dash/dash-renderer/src/dashApi.ts

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,11 @@ import {getPath} from './actions/paths';
44
import {getStores} from './utils/stores';
55
import ExternalWrapper from './wrapper/ExternalWrapper';
66
import {stringifyId} from './actions/dependencies';
7+
import {
8+
DevtoolContext,
9+
useDevtool,
10+
useDevtoolMenuButtonClassName
11+
} from './components/error/menu/DevtoolContext';
712

813
/**
914
* Get the dash props from a component path or id.
@@ -34,5 +39,10 @@ function getLayout(componentPathOrId: string[] | string): any {
3439
DashContext,
3540
useDashContext,
3641
getLayout,
37-
stringifyId
42+
stringifyId,
43+
devtool: {
44+
DevtoolContext,
45+
useDevtool,
46+
useDevtoolMenuButtonClassName
47+
}
3848
};

dash/dash.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -639,6 +639,8 @@ def __init__( # pylint: disable=too-many-statements
639639
)
640640
self.setup_startup_routes()
641641

642+
self._plotly_cloud = None
643+
642644
def _setup_hooks(self):
643645
# pylint: disable=import-outside-toplevel,protected-access
644646
from ._hooks import HooksManager
@@ -926,6 +928,16 @@ def _config(self):
926928
"ddk_version": ddk_version,
927929
"plotly_version": plotly_version,
928930
}
931+
if self._plotly_cloud is None:
932+
try:
933+
# pylint: disable=C0415,W0611
934+
import plotly_cloud # noqa: F401
935+
936+
self._plotly_cloud = True
937+
except ImportError:
938+
self._plotly_cloud = False
939+
940+
config["plotly_cloud_installed"] = self._plotly_cloud
929941
if not self.config.serve_locally:
930942
config["plotlyjs_url"] = self._plotlyjs_url
931943
if self._dev_tools.hot_reload:

0 commit comments

Comments
 (0)