@@ -17,6 +17,7 @@ var Widgets = (function () {
1717 case "text" : return renderText ( config , bodyEl ) ;
1818 case "timeline" : return renderTimeline ( config , bodyEl ) ;
1919 case "rawaxes" : return renderRawAxes ( config , bodyEl ) ;
20+ case "group" : return renderGroup ( config , bodyEl ) ;
2021 default :
2122 bodyEl . textContent = "Unknown widget type: " + type ;
2223 }
@@ -229,6 +230,100 @@ var Widgets = (function () {
229230 '</div>' ;
230231 }
231232
233+ /* --- Group --------------------------------------------- */
234+ function renderGroup ( config , container ) {
235+ var mode = config . mode || 'panel' ;
236+ var label = config . label || '' ;
237+
238+ // Header
239+ if ( label ) {
240+ var header = document . createElement ( 'div' ) ;
241+ header . className = 'widget-group-header' ;
242+ header . textContent = label ;
243+
244+ if ( mode === 'collapsible' ) {
245+ var toggle = document . createElement ( 'span' ) ;
246+ toggle . className = 'widget-group-toggle' ;
247+ toggle . textContent = config . collapsed ? '>' : 'v' ;
248+ header . insertBefore ( toggle , header . firstChild ) ;
249+ header . style . cursor = 'pointer' ;
250+ header . addEventListener ( 'click' , function ( ) {
251+ var content = container . querySelector ( '.widget-group-content' ) ;
252+ var isCollapsed = content . style . display === 'none' ;
253+ content . style . display = isCollapsed ? 'grid' : 'none' ;
254+ toggle . textContent = isCollapsed ? 'v' : '>' ;
255+ } ) ;
256+ }
257+
258+ if ( mode === 'tabbed' && config . tabs && config . tabs . length > 0 ) {
259+ var tabBar = document . createElement ( 'div' ) ;
260+ tabBar . className = 'widget-group-tabbar' ;
261+ config . tabs . forEach ( function ( tab , idx ) {
262+ var tabBtn = document . createElement ( 'button' ) ;
263+ tabBtn . className = 'widget-group-tab' ;
264+ if ( tab . name === config . activeTab ) {
265+ tabBtn . classList . add ( 'active' ) ;
266+ }
267+ tabBtn . textContent = tab . name ;
268+ tabBtn . addEventListener ( 'click' , function ( ) {
269+ var panels = container . querySelectorAll ( '.widget-group-tabpanel' ) ;
270+ panels . forEach ( function ( p ) { p . style . display = 'none' ; } ) ;
271+ panels [ idx ] . style . display = 'grid' ;
272+ tabBar . querySelectorAll ( '.widget-group-tab' ) . forEach ( function ( b ) {
273+ b . classList . remove ( 'active' ) ;
274+ } ) ;
275+ tabBtn . classList . add ( 'active' ) ;
276+ } ) ;
277+ tabBar . appendChild ( tabBtn ) ;
278+ } ) ;
279+ header . appendChild ( tabBar ) ;
280+ }
281+
282+ container . appendChild ( header ) ;
283+ }
284+
285+ // Content
286+ if ( mode === 'tabbed' && config . tabs ) {
287+ config . tabs . forEach ( function ( tab , idx ) {
288+ var tabPanel = document . createElement ( 'div' ) ;
289+ tabPanel . className = 'widget-group-tabpanel widget-group-content' ;
290+ tabPanel . style . display = ( tab . name === config . activeTab ) ? 'grid' : 'none' ;
291+ tabPanel . style . gridTemplateColumns = 'repeat(auto-fit, minmax(200px, 1fr))' ;
292+ tabPanel . style . gap = '8px' ;
293+ tabPanel . style . padding = '8px' ;
294+
295+ ( tab . widgets || [ ] ) . forEach ( function ( wCfg ) {
296+ var wEl = document . createElement ( 'div' ) ;
297+ wEl . className = 'widget' ;
298+ var wBody = document . createElement ( 'div' ) ;
299+ wBody . className = 'widget-body' ;
300+ wEl . appendChild ( wBody ) ;
301+ render ( wCfg , wBody ) ;
302+ tabPanel . appendChild ( wEl ) ;
303+ } ) ;
304+ container . appendChild ( tabPanel ) ;
305+ } ) ;
306+ } else {
307+ var content = document . createElement ( 'div' ) ;
308+ content . className = 'widget-group-content' ;
309+ content . style . display = config . collapsed ? 'none' : 'grid' ;
310+ content . style . gridTemplateColumns = 'repeat(auto-fit, minmax(200px, 1fr))' ;
311+ content . style . gap = '8px' ;
312+ content . style . padding = '8px' ;
313+
314+ ( config . children || [ ] ) . forEach ( function ( childCfg ) {
315+ var wEl = document . createElement ( 'div' ) ;
316+ wEl . className = 'widget' ;
317+ var wBody = document . createElement ( 'div' ) ;
318+ wBody . className = 'widget-body' ;
319+ wEl . appendChild ( wBody ) ;
320+ render ( childCfg , wBody ) ;
321+ content . appendChild ( wEl ) ;
322+ } ) ;
323+ container . appendChild ( content ) ;
324+ }
325+ }
326+
232327 /* --- helpers ------------------------------------------- */
233328 function formatNumber ( v ) {
234329 if ( v == null ) return "--" ;
@@ -239,5 +334,5 @@ var Widgets = (function () {
239334 return v . toFixed ( 2 ) ;
240335 }
241336
242- return { render : render } ;
337+ return { render : render , renderGroup : renderGroup } ;
243338} ) ( ) ;
0 commit comments