1 7
import param
2

3 7
from bokeh.models import Column as BkColumn, CustomJS
4

5 7
from .base import NamedListPanel
6 7
from .card import Card
7

8

9 7
class Accordion(NamedListPanel):
10
    
11 7
    active_header_background = param.String(default='#ccc', doc="""
12
        Color for currently active headers.""")
13

14 7
    active = param.List(default=[], doc="""
15
        List of indexes of active cards.""")
16

17 7
    header_color = param.String(doc="""
18
        A valid CSS color to apply to the expand button.""")
19

20 7
    header_background = param.String(doc="""
21
        A valid CSS color for the header background.""")
22

23 7
    toggle = param.Boolean(default=False, doc="""
24
        Whether to toggle between active cards or allow multiple cards""")
25

26 7
    _bokeh_model = BkColumn
27
    
28 7
    _rename = {'active': None, 'active_header_background': None,
29
               'header_background': None, 'objects': 'children',
30
               'dynamic': None, 'toggle': None, 'header_color': None}
31

32 7
    _toggle = """
33
    for (var child of accordion.children) {
34
      if ((child.id !== cb_obj.id) && (child.collapsed == cb_obj.collapsed) && !cb_obj.collapsed) {
35
        child.collapsed = !cb_obj.collapsed
36
      }
37
    }
38
    """
39

40 7
    _synced_properties = [
41
        'active_header_background', 'header_background', 'width',
42
        'sizing_mode', 'width_policy', 'height_policy', 'header_color'
43
    ]
44

45 7
    def __init__(self, *objects, **params):
46 7
        super(Accordion, self).__init__(*objects, **params)
47 7
        self.param.watch(self._update_active, ['active'])
48 7
        self.param.watch(self._update_cards, self._synced_properties)
49

50 7
    def _get_objects(self, model, old_objects, doc, root, comm=None):
51
        """
52
        Returns new child models for the layout while reusing unchanged
53
        models and cleaning up any dropped objects.
54
        """
55 7
        from panel.pane.base import RerenderError, panel
56 7
        new_models = []
57 7
        if len(self._names) != len(self):
58 0
            raise ValueError('Accordion names do not match objects, ensure '
59
                             'that the Tabs.objects are not modified '
60
                             'directly. Found %d names, expected %d.' %
61
                             (len(self._names), len(self)))
62 7
        for i, (name, pane) in enumerate(zip(self._names, self)):
63 7
            pane = panel(pane, name=name)
64 7
            self.objects[i] = pane
65

66 7
        for obj in old_objects:
67 0
            if obj not in self.objects:
68 0
                self._panels[id(obj)]._cleanup(root)
69

70 7
        params = {k: v for k, v in self.param.get_param_values()
71
                  if k in self._synced_properties}
72

73 7
        ref = root.ref['id']
74 7
        current_objects = list(self)
75 7
        for i, (name, pane) in enumerate(zip(self._names, self)):
76 7
            params.update(self._apply_style(i))
77 7
            if id(pane) in self._panels:
78 0
                card = self._panels[id(pane)]
79
            else:
80 7
                card = Card(
81
                    pane, title=name, css_classes=['accordion'],
82
                    header_css_classes=['accordion-header'],
83
                    margin=self.margin
84
                )
85 7
                self._panels[id(pane)] = card
86 7
            card.param.set_param(**params)
87 7
            if ref in card._models:
88 0
                panel = card._models[ref][0]
89
            else:
90 7
                try:
91 7
                    panel = card._get_model(doc, root, model, comm)
92 7
                    if self.toggle:
93 0
                        cb = CustomJS(args={'accordion': model}, code=self._toggle)
94 0
                        panel.js_on_change('collapsed', cb)
95 0
                except RerenderError:
96 0
                    return self._get_objects(model, current_objects[:i], doc, root, comm)
97
            
98 7
            new_models.append(panel)
99 7
        self._update_cards()
100 7
        self._update_active()
101 7
        return new_models
102

103 7
    def _cleanup(self, root):
104 7
        for panel in self._panels.values():
105 7
            panel._cleanup(root)
106 7
        super(Accordion, self)._cleanup(root)
107

108 7
    def _apply_style(self, i):
109 7
        if i == 0:
110 7
            margin = (5, 5, 0, 5)
111 7
        elif i == (len(self)-1):
112 7
            margin = (0, 5, 5, 5)
113
        else:
114 0
            margin = (0, 5, 0, 5)
115 7
        return dict(margin=margin, collapsed = i not in self.active)
116

117 7
    def _update_active(self, *events):
118 7
        for i, pane in enumerate(self.objects):
119 7
            if id(pane) not in self._panels:
120 0
                continue
121 7
            self._panels[id(pane)].collapsed = i not in self.active
122

123 7
    def _update_cards(self, *events):
124 7
        params = {k: v for k, v in self.param.get_param_values()
125
                  if k in self._synced_properties}
126 7
        for panel in self._panels.values():
127 7
            panel.param.set_param(**params)

Read our documentation on viewing source code .

Loading