-
-
Notifications
You must be signed in to change notification settings - Fork 473
/
accordion.py
123 lines (100 loc) · 4.45 KB
/
accordion.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
import param
from bokeh.models import Column as BkColumn, CustomJS
from .base import NamedListPanel
from .card import Card
class Accordion(NamedListPanel):
active_header_background = param.String(default='#ccc', doc="""
Color for currently active headers.""")
active = param.List(default=[], doc="""
List of indexes of active cards.""")
header_color = param.String(doc="""
A valid CSS color to apply to the expand button.""")
header_background = param.String(default=None, doc="""
A valid CSS color for the header background.""")
toggle = param.Boolean(default=False, doc="""
Whether to toggle between active cards or allow multiple cards""")
_bokeh_model = BkColumn
_rename = {'active': None, 'active_header_background': None,
'header_background': None, 'objects': 'children',
'dynamic': None, 'toggle': None, 'header_color': None}
_toggle = """
for (var child of accordion.children) {
if ((child.id !== cb_obj.id) && (child.collapsed == cb_obj.collapsed) && !cb_obj.collapsed) {
child.collapsed = !cb_obj.collapsed
}
}
"""
_synced_properties = [
'active_header_background', 'header_background', 'width',
'sizing_mode', 'width_policy', 'height_policy', 'header_color'
]
def __init__(self, *objects, **params):
super(Accordion, self).__init__(*objects, **params)
self.param.watch(self._update_active, ['active'])
self.param.watch(self._update_cards, self._synced_properties)
def _get_objects(self, model, old_objects, doc, root, comm=None):
"""
Returns new child models for the layout while reusing unchanged
models and cleaning up any dropped objects.
"""
from panel.pane.base import RerenderError, panel
new_models = []
if len(self._names) != len(self):
raise ValueError('Accordion names do not match objects, ensure '
'that the Tabs.objects are not modified '
'directly. Found %d names, expected %d.' %
(len(self._names), len(self)))
for i, (name, pane) in enumerate(zip(self._names, self)):
pane = panel(pane, name=name)
self.objects[i] = pane
for obj in old_objects:
if obj not in self.objects:
self._panels[id(obj)]._cleanup(root)
params = {k: v for k, v in self.param.get_param_values()
if k in self._synced_properties}
ref = root.ref['id']
current_objects = list(self)
for i, (name, pane) in enumerate(zip(self._names, self)):
params.update(self._apply_style(i))
if id(pane) in self._panels:
card = self._panels[id(pane)]
else:
card = Card(
pane, title=name, css_classes=['accordion'],
header_css_classes=['accordion-header']
)
self._panels[id(pane)] = card
card.param.set_param(**params)
if ref in card._models:
panel = card._models[ref][0]
else:
try:
panel = card._get_model(doc, root, model, comm)
if self.toggle:
cb = CustomJS(args={'accordion': model}, code=self._toggle)
panel.js_on_change('collapsed', cb)
except RerenderError:
return self._get_objects(model, current_objects[:i], doc, root, comm)
new_models.append(panel)
self._update_cards()
return new_models
def _cleanup(self, root):
for panel in self._panels.values():
panel._cleanup(root)
super(Accordion, self)._cleanup(root)
def _apply_style(self, i):
if i == 0:
margin = (5, 5, 0, 5)
elif i == (len(self)-1):
margin = (0, 5, 5, 5)
else:
margin = (0, 5, 0, 5)
return dict(margin=margin, collapsed = i not in self.active)
def _update_active(self, *events):
for i, pane in enumerate(self.objects):
self._panels[id(pane)].collapsed = i not in self.active
def _update_cards(self, *events):
params = {k: v for k, v in self.param.get_param_values()
if k in self._synced_properties}
for panel in self._panels.values():
panel.param.set_param(**params)