Skip to content

Commit a243455

Browse files
author
emher
committed
Added LockableDropdown component.
1 parent 39cfdfc commit a243455

File tree

5 files changed

+311
-0
lines changed

5 files changed

+311
-0
lines changed

NAMESPACE

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ export(dccInterval)
1212
export(dccLink)
1313
export(dccLoading)
1414
export(dccLocation)
15+
export(dccLockableDropdown)
1516
export(dccLogoutButton)
1617
export(dccMarkdown)
1718
export(dccRadioItems)
Lines changed: 211 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,211 @@
1+
import PropTypes from 'prop-types';
2+
import React, {Component, lazy, Suspense} from 'react';
3+
import dropdown from '../utils/LazyLoader/dropdown';
4+
import '../components/css/custom.css';
5+
6+
const RealDropdown = lazy(dropdown);
7+
8+
/**
9+
* Dropdown is an interactive dropdown element for selecting one or more
10+
* items.
11+
* The values and labels of the dropdown items are specified in the `options`
12+
* property and the selected item(s) are specified with the `value` property.
13+
*
14+
* Use a dropdown when you have many options (more than 5) or when you are
15+
* constrained for space. Otherwise, you can use RadioItems or a Checklist,
16+
* which have the benefit of showing the users all of the items at once.
17+
*/
18+
export default class LockableDropdown extends Component {
19+
20+
render() {
21+
const {locked} = this.props;
22+
23+
return (
24+
<Suspense fallback={null}>
25+
<div className="ldd-container">
26+
<RealDropdown {...this.props} />
27+
<div className={locked? "ldd-toggle on" : "ldd-toggle"}
28+
onClick={() => {
29+
if(this.props.setProps){
30+
this.props.setProps({locked: !locked})
31+
}
32+
}}
33+
>
34+
<div className="slide">
35+
</div>
36+
</div>
37+
</div>
38+
</Suspense>
39+
);
40+
}
41+
}
42+
43+
LockableDropdown.propTypes = {
44+
/**
45+
* The ID of this component, used to identify dash components
46+
* in callbacks. The ID needs to be unique across all of the
47+
* components in an app.
48+
*/
49+
id: PropTypes.string,
50+
51+
/**
52+
* An array of options {label: [string|number], value: [string|number]},
53+
* an optional disabled field can be used for each option
54+
*/
55+
options: PropTypes.arrayOf(
56+
PropTypes.exact({
57+
/**
58+
* The dropdown's label
59+
*/
60+
label: PropTypes.oneOfType([PropTypes.string, PropTypes.number])
61+
.isRequired,
62+
63+
/**
64+
* The value of the dropdown. This value
65+
* corresponds to the items specified in the
66+
* `value` property.
67+
*/
68+
value: PropTypes.oneOfType([PropTypes.string, PropTypes.number])
69+
.isRequired,
70+
71+
/**
72+
* If true, this option is disabled and cannot be selected.
73+
*/
74+
disabled: PropTypes.bool,
75+
})
76+
),
77+
78+
/**
79+
* The value of the input. If `multi` is false (the default)
80+
* then value is just a string that corresponds to the values
81+
* provided in the `options` property. If `multi` is true, then
82+
* multiple values can be selected at once, and `value` is an
83+
* array of items with values corresponding to those in the
84+
* `options` prop.
85+
*/
86+
value: PropTypes.oneOfType([
87+
PropTypes.string,
88+
PropTypes.number,
89+
PropTypes.arrayOf(
90+
PropTypes.oneOfType([PropTypes.string, PropTypes.number])
91+
),
92+
]),
93+
94+
/**
95+
* height of each option. Can be increased when label lengths would wrap around
96+
*/
97+
optionHeight: PropTypes.number,
98+
99+
/**
100+
* className of the dropdown element
101+
*/
102+
className: PropTypes.string,
103+
104+
/**
105+
* Whether or not the dropdown is "clearable", that is, whether or
106+
* not a small "x" appears on the right of the dropdown that removes
107+
* the selected value.
108+
*/
109+
clearable: PropTypes.bool,
110+
111+
/**
112+
* Whether or not the dropdown is currently locked.
113+
*/
114+
locked: PropTypes.bool,
115+
116+
/**
117+
* If true, this dropdown is disabled and the selection cannot be changed.
118+
*/
119+
disabled: PropTypes.bool,
120+
121+
/**
122+
* If true, the user can select multiple values
123+
*/
124+
multi: PropTypes.bool,
125+
126+
/**
127+
* The grey, default text shown when no option is selected
128+
*/
129+
placeholder: PropTypes.string,
130+
131+
/**
132+
* Whether to enable the searching feature or not
133+
*/
134+
searchable: PropTypes.bool,
135+
136+
/**
137+
* The value typed in the DropDown for searching.
138+
*/
139+
search_value: PropTypes.string,
140+
141+
/**
142+
* Dash-assigned callback that gets fired when the input changes
143+
*/
144+
setProps: PropTypes.func,
145+
146+
/**
147+
* Defines CSS styles which will override styles previously set.
148+
*/
149+
style: PropTypes.object,
150+
151+
/**
152+
* Object that holds the loading state object coming from dash-renderer
153+
*/
154+
loading_state: PropTypes.shape({
155+
/**
156+
* Determines if the component is loading or not
157+
*/
158+
is_loading: PropTypes.bool,
159+
/**
160+
* Holds which property is loading
161+
*/
162+
prop_name: PropTypes.string,
163+
/**
164+
* Holds the name of the component that is loading
165+
*/
166+
component_name: PropTypes.string,
167+
}),
168+
169+
/**
170+
* Used to allow user interactions in this component to be persisted when
171+
* the component - or the page - is refreshed. If `persisted` is truthy and
172+
* hasn't changed from its previous value, a `value` that the user has
173+
* changed while using the app will keep that change, as long as
174+
* the new `value` also matches what was given originally.
175+
* Used in conjunction with `persistence_type`.
176+
*/
177+
persistence: PropTypes.oneOfType([
178+
PropTypes.bool,
179+
PropTypes.string,
180+
PropTypes.number,
181+
]),
182+
183+
/**
184+
* Properties whose user interactions will persist after refreshing the
185+
* component or the page. Since only `value` is allowed this prop can
186+
* normally be ignored.
187+
*/
188+
persisted_props: PropTypes.arrayOf(PropTypes.oneOf(['value'])),
189+
190+
/**
191+
* Where persisted user changes will be stored:
192+
* memory: only kept in memory, reset on page refresh.
193+
* local: window.localStorage, data is kept after the browser quit.
194+
* session: window.sessionStorage, data is cleared once the browser quit.
195+
*/
196+
persistence_type: PropTypes.oneOf(['local', 'session', 'memory']),
197+
};
198+
199+
LockableDropdown.defaultProps = {
200+
clearable: true,
201+
locked: false,
202+
disabled: false,
203+
multi: false,
204+
searchable: true,
205+
optionHeight: 35,
206+
persisted_props: ['value'],
207+
persistence_type: 'local',
208+
};
209+
210+
export const propTypes = LockableDropdown.propTypes;
211+
export const defaultProps = LockableDropdown.defaultProps;

src/components/css/custom.css

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
/**
2+
* Styles related to lockable drop down
3+
*/
4+
5+
.ldd-container{
6+
display: grid;
7+
grid-template-columns: 1fr 0;
8+
width: 100%;
9+
margin-bottom: -5px;
10+
}
11+
12+
.ldd-toggle {
13+
background-color: #FFF;
14+
border-radius: 60px;
15+
#box-shadow: 0 1px 1px 0 rgba(255,255,255,.4), 0 1px 0 0 rgba(0,0,0,0.10) inset;
16+
cursor: pointer;
17+
width: 38px;
18+
height: 85%;
19+
overflow: hidden;
20+
position: relative;
21+
top: 5%;
22+
left:-40px;
23+
margin:auto;
24+
transition: all .25s linear;
25+
}
26+
27+
.ldd-toggle .slide {
28+
font-size: 8px;
29+
line-height: 28px;
30+
text-align: center;
31+
position: absolute;
32+
top: 0px;
33+
left: 0px;
34+
transition: all 0.3s cubic-bezier(0.43, 1.3, 0.86, 1);
35+
}
36+
37+
.ldd-toggle .slide span{
38+
text-shadow: 0 1px 1px rgba(255,255,255,.7), 0 0 1px rgba(0,0,0,.3);
39+
}
40+
41+
.ldd-toggle .slide:before,
42+
.ldd-toggle .slide:after {
43+
color: #FFF;
44+
content: "\f023";
45+
font-family: fontAwesome;
46+
font-size: 18px;
47+
font-weight: 400;
48+
text-shadow: 0 -1px 1px rgba(0, 0, 0, 0.25);
49+
-webkit-font-smoothing: antialiased;
50+
position: absolute;
51+
}
52+
53+
.ldd-toggle .slide:before {
54+
right: -25px;
55+
color: #2a2b2c;
56+
opacity: 0.2;
57+
}
58+
59+
.ldd-toggle .slide:after {
60+
content: "\f09c";
61+
left: -45px;
62+
color: #2a2b2c;
63+
opacity: 0.2;
64+
}
65+
66+
.ldd-toggle.on {
67+
background: #FFF;
68+
}
69+
70+
.ldd-toggle.on .slide {
71+
left: 55px;
72+
color: #00d100;
73+
}

src/index.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
import ConfirmDialog from './components/ConfirmDialog.react';
33
import ConfirmDialogProvider from './components/ConfirmDialogProvider.react';
44
import Dropdown from './components/Dropdown.react';
5+
import LockableDropdown from './components/LockableDropdown.react';
56
import Input from './components/Input.react';
67
import Graph from './components/Graph.react';
78
import RangeSlider from './components/RangeSlider.react';
@@ -30,6 +31,7 @@ export {
3031
ConfirmDialog,
3132
ConfirmDialogProvider,
3233
Dropdown,
34+
LockableDropdown,
3335
Graph,
3436
Input,
3537
RadioItems,

usage.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import dash
2+
import dash_core_components as dcc
3+
import dash_html_components as html
4+
from dash.dependencies import Output, Input
5+
6+
external_stylesheets = ['https://codepen.io/chriddyp/pen/bWLwgP.css',
7+
"https://maxcdn.bootstrapcdn.com/font-awesome/4.2.0/css/font-awesome.min.css"]
8+
options = [{"label": "1", "value": "1"}, {"label": "2", "value": "2"}]
9+
10+
app = dash.Dash(__name__, external_stylesheets=external_stylesheets)
11+
app.layout = html.Div(children=[
12+
dcc.LockableDropdown(options=options, id="dd_master"),
13+
html.Br(),
14+
dcc.Dropdown(options=options, id="dd_slave")
15+
])
16+
17+
18+
@app.callback(Output("dd_slave", "value"), [Input("dd_master", "value"), Input("dd_master", "locked")])
19+
def update_dd_slave(dd_master_value, dd_master_locked):
20+
return dd_master_value if dd_master_locked else None
21+
22+
23+
if __name__ == '__main__':
24+
app.run_server(debug=True)

0 commit comments

Comments
 (0)