holoviz / panel
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().__init__(*objects, **params)
47 7
        self._updating_active = False
48 7
        self.param.watch(self._update_active, ['active'])
49 7
        self.param.watch(self._update_cards, self._synced_properties)
50

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

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

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

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

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

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

118 7
    def _set_active(self, *events):
119 7
        if self._updating_active:
120 7
            return
121 7
        active = []
122 7
        self._updating_active = True
123 7
        try:
124 7
            for i, pane in enumerate(self.objects):
125 7
                if id(pane) not in self._panels:
126 7
                    continue
127 7
                elif not self._panels[id(pane)].collapsed:
128 7
                    active.append(i)
129 7
            self.active = active
130
        finally:
131 7
            self._updating_active = False
132

133 7
    def _update_active(self, *events):
134 7
        if self._updating_active:
135 7
            return
136 7
        self._updating_active = True
137 7
        try:
138 7
            for i, pane in enumerate(self.objects):
139 7
                if id(pane) not in self._panels:
140 0
                    continue
141 7
                self._panels[id(pane)].collapsed = i not in self.active
142
        finally:
143 7
            self._updating_active = False
144

145 7
    def _update_cards(self, *events):
146 7
        params = {k: v for k, v in self.param.get_param_values()
147
                  if k in self._synced_properties}
148 7
        for panel in self._panels.values():
149 7
            panel.param.set_param(**params)

Read our documentation on viewing source code .

Loading