@@ -32,6 +32,56 @@ class BootstrapPanelHelper extends Helper {
3232
3333 public $ current = NULL ;
3434
35+ /* Protected attributes used to generate ID for collapsible panels. */
36+ protected $ _panelCount = 0 ;
37+ protected $ _bodyId = null ;
38+ protected $ _headId = null ;
39+
40+ /* Default value for "collapsible" option. */
41+ protected $ _defaultCollapsible = false ;
42+
43+ /* Protected attribute used to generate group ID. */
44+ protected $ _groupCount = 0 ;
45+ protected $ _groupId = false ;
46+
47+ protected $ _groupPanelCount = 0 ;
48+ protected $ _groupPanelOpen = 0 ;
49+
50+ protected $ _lastPanelClosed = true ;
51+ protected $ _autoCloseOnCreate = false ;
52+
53+ protected $ _collapsible = false ;
54+
55+ public function startGroup ($ options = []) {
56+ $ options += [
57+ 'class ' => '' ,
58+ 'role ' => 'tablist ' ,
59+ 'aria-multiselectable ' => true ,
60+ 'id ' => 'panelGroup- ' .(++$ this ->_groupCount ),
61+ 'collapsible ' => true ,
62+ 'open ' => 0
63+ ];
64+ $ this ->_defaultCollapsible = $ options ['collapsible ' ];
65+ $ this ->_autoCloseOnCreate = true ;
66+ $ this ->_lastPanelClosed = true ;
67+ $ this ->_groupPanelCount = -1 ;
68+ $ this ->_groupPanelOpen = $ options ['open ' ];
69+ $ this ->_groupId = $ options ['id ' ];
70+ $ options = $ this ->addClass ($ options , 'panel-group ' );
71+ $ class = $ options ['class ' ];
72+ unset($ options ['class ' ], $ options ['open ' ], $ options ['collapsible ' ]);
73+ return $ this ->Html ->div ($ class , null , $ options );
74+ }
75+
76+ public function endGroup () {
77+ $ this ->_defaultCollapsible = false ;
78+ $ out = '' ;
79+ if (!$ this ->_lastPanelClosed ) {
80+ $ out = $ this ->end ();
81+ }
82+ return $ out .'</div> ' ;
83+ }
84+
3585 /**
3686 *
3787 * Create a Twitter Bootstrap like panel.
@@ -45,28 +95,52 @@ class BootstrapPanelHelper extends Helper {
4595 public function create ($ title = null , $ options = []) {
4696
4797 if (is_array ($ title )) {
48- $ options = $ title ;
98+ $ options = $ title ;
4999 }
50100
51- $ nobody = $ this ->_extractOption ('no-body ' , $ options , false );
52- unset ($ options ['no-body ' ]);
53- $ type = $ this ->_extractOption ('type ' , $ options , 'default ' );
54- unset ($ options ['type ' ]);
101+ $ options += [
102+ 'no-body ' => false ,
103+ 'type ' => 'default ' ,
104+ 'collapsible ' => $ this ->_defaultCollapsible
105+ ];
106+
107+ $ nobody = $ options ['no-body ' ];
108+ $ type = $ options ['type ' ];
109+ $ this ->_collapsible = $ options ['collapsible ' ];
110+ unset ($ options ['no-body ' ], $ options ['collapsible ' ], $ options ['type ' ]);
55111
56112 $ options = $ this ->addClass ($ options , ['panel ' , 'panel- ' .$ type ]);
113+
114+ if ($ this ->_collapsible ) {
115+ $ this ->_headId = 'heading- ' .($ this ->_panelCount );
116+ $ this ->_bodyId = 'collapse- ' .($ this ->_panelCount );
117+ $ this ->_panelCount ++;
118+ }
119+
57120 $ class = $ options ['class ' ];
58121 unset ($ options ['class ' ]);
59122
60- $ res = $ this ->Html ->div ($ class , null , $ options );
123+ $ out = '' ;
124+
125+ if ($ this ->_autoCloseOnCreate && !$ this ->_lastPanelClosed ) {
126+ $ out .= $ this ->end ();
127+ }
128+ $ this ->_lastPanelClosed = false ;
129+
130+ /* Increment panel counter for the current group. */
131+ $ this ->_groupPanelCount ++;
132+
133+ $ out .= $ this ->Html ->div ($ class , null , $ options );
61134 if (is_string ($ title ) && $ title ) {
62- $ res .= $ this ->_createHeader ($ title , [
135+ $ out .= $ this ->_createHeader ($ title , [
63136 'title ' => isset ($ options ['title ' ]) ? $ options ['title ' ] : true
64137 ]) ;
65138 if (!$ nobody ) {
66- $ res .= $ this ->_startPart ('body ' );
139+ $ out .= $ this ->_startPart ('body ' );
67140 }
68141 }
69- return $ res ;
142+
143+ return $ out ;
70144 }
71145
72146 /**
@@ -79,11 +153,9 @@ public function create($title = null, $options = []) {
79153 *
80154 **/
81155 public function end ($ title = null , $ options = []) {
156+ $ this ->_lastPanelClosed = true ;
82157 $ res = '' ;
83- if ($ this ->current != null ) {
84- $ this ->current = null ;
85- $ res .= $ this ->_endPart ();
86- }
158+ $ res .= $ this ->_cleanCurrent ();
87159 if ($ title !== null ) {
88160 $ res .= $ this ->footer ($ title , $ options ) ;
89161 }
@@ -92,36 +164,64 @@ public function end ($title = null, $options = []) {
92164 }
93165
94166 protected function _cleanCurrent () {
167+ $ res = '' ;
95168 if ($ this ->current ) {
169+ $ res = $ this ->_endPart ();
96170 $ this ->current = NULL ;
97- return $ this ->_endPart ();
98171 }
99- return '' ;
172+ return $ res ;
100173 }
101174
102175 protected function _createHeader ($ title , $ options = [], $ titleOptions = []) {
103176 if (empty ($ titleOptions )) {
104177 $ titleOptions = $ options ['title ' ] ;
105178 }
106179 unset ($ options ['title ' ]);
180+ $ options = $ this ->addClass ($ options , 'panel-heading ' );
181+ $ class = $ options ['class ' ];
182+ unset ($ options ['class ' ]);
183+ if ($ this ->_collapsible ) {
184+ $ options += [
185+ 'role ' => 'tab ' ,
186+ 'id ' => $ this ->_headId
187+ ];
188+ $ this ->_headId = $ options ['id ' ];
189+ $ title = $ this ->Html ->link ($ title , '# ' .$ this ->_bodyId , [
190+ 'data-toggle ' => 'collapse ' ,
191+ 'data-parent ' => $ this ->_groupId ? '# ' .$ this ->_groupId : false ,
192+ 'aria-expanded ' => true ,
193+ 'aria-controls ' => '# ' .$ this ->_bodyId
194+ ]);
195+ }
107196 if ($ titleOptions !== false ) {
108197 if (!is_array ($ titleOptions )) {
109198 $ titleOptions = [];
110199 }
200+ $ titleOptions += ['tag ' => 'h4 ' ];
111201 $ titleOptions = $ this ->addClass ($ titleOptions , 'panel-title ' );
112- $ title = $ titleOptions ? $ this ->Html ->tag ('h3 ' , $ title , $ titleOptions ) : $ title ;
202+ $ tag = $ titleOptions ['tag ' ];
203+ unset($ titleOptions ['tag ' ]);
204+ $ title = $ titleOptions ? $ this ->Html ->tag ($ tag , $ title , $ titleOptions ) : $ title ;
113205 }
114- $ options = $ this ->addClass ($ options , 'panel-heading ' );
115- $ class = $ options ['class ' ];
116- unset ($ options ['class ' ]);
117206 return $ this ->_cleanCurrent ().$ this ->Html ->div ($ class , $ title , $ options );
118207 }
119208
120209 protected function _createBody ($ text , $ options = []) {
121210 $ options = $ this ->addClass ($ options , 'panel-body ' );
122211 $ class = $ options ['class ' ];
123212 unset ($ options ['class ' ]);
124- return $ this ->_cleanCurrent ().$ this ->Html ->div ($ class , $ text , $ options ) ;
213+ $ body = $ this ->Html ->div ($ class , $ text , $ options );
214+ if ($ this ->_collapsible ) {
215+ $ open = ((is_int ($ this ->_groupPanelOpen )
216+ && $ this ->_groupPanelOpen == $ this ->_groupPanelCount )
217+ || $ this ->_groupPanelOpen == $ this ->_bodyId ) ? ' in ' : '' ;
218+ $ body = $ this ->Html ->div ('panel-collapse collapse ' .$ open , $ body , [
219+ 'role ' => 'tabpanel ' ,
220+ 'aria-labelledby ' => $ this ->_headId ,
221+ 'id ' => $ this ->_bodyId
222+ ]);
223+ }
224+ return $ this ->_cleanCurrent ().$ body ;
125225 }
126226
127227 protected function _createFooter ($ text = null , $ options = []) {
@@ -137,12 +237,25 @@ protected function _startPart ($part, $options = []) {
137237 $ res = $ this ->_endPart () ;
138238 }
139239 $ this ->current = $ part ;
240+ if ($ this ->_collapsible && $ this ->current == 'body ' ) {
241+ $ open = ((is_int ($ this ->_groupPanelOpen )
242+ && $ this ->_groupPanelOpen === $ this ->_groupPanelCount )
243+ || $ this ->_groupPanelOpen === $ this ->_bodyId ) ? ' in ' : '' ;
244+ $ res .= $ this ->Html ->div ('panel-collapse collapse ' .$ open , null , [
245+ 'role ' => 'tabpanel ' ,
246+ 'aria-labelledby ' => $ this ->_headId ,
247+ 'id ' => $ this ->_bodyId
248+ ]);
249+ }
140250 return $ res .$ this ->Html ->div ('panel- ' .$ part .' ' .$ this ->_extractOption ('class ' ,
141251 $ options , '' ),
142252 null , $ options ) ;
143253 }
144254
145255 protected function _endPart () {
256+ if ($ this ->_collapsible && $ this ->current == 'body ' ) {
257+ return '</div></div> ' ;
258+ }
146259 return '</div> ' ;
147260 }
148261
0 commit comments