|
| 1 | +%% GroupWidget — All Modes Demo |
| 2 | +% Demonstrates GroupWidget as a container for child widgets, covering all |
| 3 | +% three grouping modes: panel, collapsible, and tabbed. |
| 4 | +% |
| 5 | +% NOTE: GroupWidget is defined in the Phase A implementation plan |
| 6 | +% (docs/superpowers/plans/2026-03-18-dashboard-groupwidget-phase-a.md). |
| 7 | +% This example script will run correctly once GroupWidget is implemented |
| 8 | +% and registered in DashboardEngine. Until then it documents the |
| 9 | +% intended API; uncommenting is the only change needed after Phase A lands. |
| 10 | +% |
| 11 | +% GroupWidget Properties: |
| 12 | +% Mode — 'panel' (default) | 'collapsible' | 'tabbed'. |
| 13 | +% Label — header title string shown in the group header bar. |
| 14 | +% Children — cell array of DashboardWidget (panel / collapsible modes). |
| 15 | +% Tabs — cell array of structs with fields 'name' and 'widgets' |
| 16 | +% (tabbed mode only). |
| 17 | +% ActiveTab — name of the initially visible tab (tabbed mode). |
| 18 | +% Collapsed — initial collapsed state (collapsible mode, default false). |
| 19 | +% ChildAutoFlow — auto-arrange children left-to-right (default true). |
| 20 | +% ChildColumns — column count of the child sub-grid (default 24). |
| 21 | +% Position — [col row width height] on the 24-column grid. |
| 22 | +% |
| 23 | +% Usage: |
| 24 | +% example_widget_group |
| 25 | + |
| 26 | +close all force; |
| 27 | +clear functions; |
| 28 | +projectRoot = fileparts(fileparts(mfilename('fullpath'))); |
| 29 | +run(fullfile(projectRoot, 'install.m')); |
| 30 | + |
| 31 | +%% 1. Create sensors |
| 32 | +rng(42); |
| 33 | +N = 5000; |
| 34 | +t = linspace(0, 86400, N); % 24 hours |
| 35 | + |
| 36 | +sTemp = Sensor('T-401', 'Name', 'Temperature'); |
| 37 | +sTemp.Units = [char(176) 'F']; |
| 38 | +sTemp.X = t; |
| 39 | +sTemp.Y = 72 + 4*sin(2*pi*t/3600) + randn(1,N)*1.2; |
| 40 | +sTemp.Y(end) = 79; % near warning level |
| 41 | +sTemp.addThresholdRule(struct(), 78, ... |
| 42 | + 'Direction', 'upper', 'Label', 'Hi Warn', ... |
| 43 | + 'Color', [1 0.8 0], 'LineStyle', '--'); |
| 44 | +sTemp.addThresholdRule(struct(), 85, ... |
| 45 | + 'Direction', 'upper', 'Label', 'Hi Alarm', ... |
| 46 | + 'Color', [1 0.2 0.2], 'LineStyle', '-'); |
| 47 | +sTemp.resolve(); |
| 48 | + |
| 49 | +sPress = Sensor('P-201', 'Name', 'Pressure'); |
| 50 | +sPress.Units = 'psi'; |
| 51 | +sPress.X = t; |
| 52 | +sPress.Y = 55 + 8*sin(2*pi*t/7200) + randn(1,N)*1.5; |
| 53 | +sPress.addThresholdRule(struct(), 68, ... |
| 54 | + 'Direction', 'upper', 'Label', 'Hi Warn', ... |
| 55 | + 'Color', [1 0.8 0], 'LineStyle', '--'); |
| 56 | +sPress.resolve(); |
| 57 | + |
| 58 | +sFlow = Sensor('F-301', 'Name', 'Flow Rate'); |
| 59 | +sFlow.Units = 'L/min'; |
| 60 | +sFlow.X = t; |
| 61 | +sFlow.Y = max(0, 120 + 10*sin(2*pi*t/1800) + randn(1,N)*4); |
| 62 | +sFlow.resolve(); |
| 63 | + |
| 64 | +sVib = Sensor('V-501', 'Name', 'Vibration RMS'); |
| 65 | +sVib.Units = 'mm/s'; |
| 66 | +sVib.X = t; |
| 67 | +sVib.Y = max(0.1, 1.5 + 0.4*sin(2*pi*t/5400) + randn(1,N)*0.2); |
| 68 | +sVib.resolve(); |
| 69 | + |
| 70 | +%% 2. Build dashboard |
| 71 | +d = DashboardEngine('Group Widget Demo'); |
| 72 | +d.Theme = 'light'; |
| 73 | + |
| 74 | +%% --- Mode 1: Panel group (default) --- |
| 75 | +% A panel group acts as a titled container. Children auto-flow left to |
| 76 | +% right inside the group boundaries. Equivalent to placing widgets in a |
| 77 | +% named section of the dashboard. |
| 78 | +% |
| 79 | +% g1 = GroupWidget('Label', 'Motor Health', 'Mode', 'panel', ... |
| 80 | +% 'Position', [1 1 24 4]); |
| 81 | +% g1.addChild(NumberWidget('Sensor', sTemp)); |
| 82 | +% g1.addChild(NumberWidget('Sensor', sPress)); |
| 83 | +% g1.addChild(GaugeWidget('Sensor', sTemp, 'Style', 'arc')); |
| 84 | +% g1.addChild(GaugeWidget('Sensor', sPress, 'Style', 'arc')); |
| 85 | +% d.addWidget('group', 'Label', 'Motor Health', 'Mode', 'panel', ... |
| 86 | +% 'Position', [1 1 24 4], ... |
| 87 | +% 'Children', {NumberWidget('Sensor', sTemp), ... |
| 88 | +% NumberWidget('Sensor', sPress), ... |
| 89 | +% GaugeWidget('Sensor', sTemp, 'Style', 'arc'), ... |
| 90 | +% GaugeWidget('Sensor', sPress, 'Style', 'arc')}); |
| 91 | + |
| 92 | +%% --- Mode 2: Collapsible group --- |
| 93 | +% A collapsible group renders a clickable header that hides/shows children. |
| 94 | +% Collapse() reduces Position(4) to 1 grid row; DashboardLayout.reflow() |
| 95 | +% compacts widgets below it automatically. |
| 96 | +% |
| 97 | +% d.addWidget('group', 'Label', 'Vibration Details', 'Mode', 'collapsible', ... |
| 98 | +% 'Position', [1 5 24 6], ... |
| 99 | +% 'Collapsed', false, ... |
| 100 | +% 'Children', {FastSenseWidget('Sensor', sVib)}); |
| 101 | + |
| 102 | +%% --- Mode 3: Tabbed group --- |
| 103 | +% A tabbed group shows one tab at a time. Each tab gets its own named |
| 104 | +% panel. Tab buttons appear in the header bar. ActiveTab sets which tab |
| 105 | +% is visible on first render. |
| 106 | +% |
| 107 | +% tab1 = struct('name', 'Overview', 'widgets', {{ ... |
| 108 | +% NumberWidget('Sensor', sTemp), ... |
| 109 | +% NumberWidget('Sensor', sPress), ... |
| 110 | +% StatusWidget('Sensor', sTemp) ... |
| 111 | +% }}); |
| 112 | +% tab2 = struct('name', 'Trends', 'widgets', {{ ... |
| 113 | +% FastSenseWidget('Sensor', sTemp), ... |
| 114 | +% FastSenseWidget('Sensor', sFlow) ... |
| 115 | +% }}); |
| 116 | +% d.addWidget('group', 'Label', 'Process Monitor', 'Mode', 'tabbed', ... |
| 117 | +% 'Position', [1 11 24 8], ... |
| 118 | +% 'Tabs', {tab1, tab2}, ... |
| 119 | +% 'ActiveTab', 'Overview'); |
| 120 | + |
| 121 | +%% Fallback: render standalone widgets that mirror GroupWidget children |
| 122 | +% This section produces a working dashboard today and will be replaced once |
| 123 | +% GroupWidget lands. |
| 124 | + |
| 125 | +% Panel group equivalent — KPI row + gauge row |
| 126 | +d.addWidget('number', 'Position', [1 1 6 2], 'Sensor', sTemp); |
| 127 | +d.addWidget('number', 'Position', [7 1 6 2], 'Sensor', sPress); |
| 128 | +d.addWidget('gauge', 'Position', [13 1 6 4], 'Sensor', sTemp, 'Style', 'arc'); |
| 129 | +d.addWidget('gauge', 'Position', [19 1 6 4], 'Sensor', sPress, 'Style', 'arc'); |
| 130 | + |
| 131 | +% Collapsible group equivalent — vibration detail |
| 132 | +d.addWidget('fastsense', 'Position', [1 5 24 6], 'Sensor', sVib); |
| 133 | + |
| 134 | +% Tabbed group equivalent — overview tab (visible) + trends tab (hidden) |
| 135 | +d.addWidget('number', 'Position', [1 11 6 2], 'Sensor', sTemp); |
| 136 | +d.addWidget('number', 'Position', [7 11 6 2], 'Sensor', sPress); |
| 137 | +d.addWidget('status', 'Position', [13 11 5 2], 'Sensor', sTemp); |
| 138 | +d.addWidget('fastsense', 'Position', [1 13 12 6], 'Sensor', sTemp); |
| 139 | +d.addWidget('fastsense', 'Position', [13 13 12 6], 'Sensor', sFlow); |
| 140 | + |
| 141 | +%% 3. Render |
| 142 | +d.render(); |
| 143 | + |
| 144 | +fprintf('Dashboard rendered with %d widgets.\n', numel(d.Widgets)); |
| 145 | +fprintf('GroupWidget Phase A required for group/collapsible/tabbed container modes.\n'); |
| 146 | +fprintf('See: docs/superpowers/plans/2026-03-18-dashboard-groupwidget-phase-a.md\n'); |
0 commit comments