pict-section-activitygraph 1.0.2 → 1.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pict-section-activitygraph",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.4",
|
|
4
4
|
"description": "Pict section that renders a stacked-bar activity timeline: one bar per period, stacked by type or actor, across parallel domains. Themed SVG, no chart dependency.",
|
|
5
5
|
"main": "source/Pict-Section-ActivityGraph.js",
|
|
6
6
|
"scripts": {
|
|
@@ -73,6 +73,7 @@ function _colorFor(pStackBy, pKey, pIndex)
|
|
|
73
73
|
/**
|
|
74
74
|
* pModel:
|
|
75
75
|
* { Period, StackBy ('ActivityType'|'ActorKind'), Measure ('EventCount'|'Volume'),
|
|
76
|
+
* LegendPosition ('top-left'|'top-right'|'bottom-left'|'bottom-right'|'none', default 'bottom-right'), Title (chart title; '' omits it, default 'System Activity'),
|
|
76
77
|
* Columns: [ { Key, Title, Accent } ], Buckets: [ { PeriodStart, PeriodKey, Domain, ActivityType, ActorKind, EventCount, Volume } ] }
|
|
77
78
|
*/
|
|
78
79
|
function buildSVG(pModel)
|
|
@@ -83,6 +84,10 @@ function buildSVG(pModel)
|
|
|
83
84
|
let tmpMeasure = (tmpModel.Measure === 'Volume') ? 'Volume' : 'EventCount';
|
|
84
85
|
let tmpColumns = Array.isArray(tmpModel.Columns) && tmpModel.Columns.length ? tmpModel.Columns : [{ Key: 'WorkItems', Title: 'Work Items', Accent: '#e0891e' }, { Key: 'Knowledge', Title: 'Knowledge', Accent: '#3f7fd0' }];
|
|
85
86
|
let tmpBuckets = Array.isArray(tmpModel.Buckets) ? tmpModel.Buckets : [];
|
|
87
|
+
let tmpLegendOptions = { 'top-left': 1, 'top-right': 1, 'bottom-left': 1, 'bottom-right': 1, 'none': 1 };
|
|
88
|
+
let tmpLegendPosition = tmpLegendOptions[tmpModel.LegendPosition] ? tmpModel.LegendPosition : 'bottom-right';
|
|
89
|
+
// Title text; pass an empty string to omit it (e.g. when the host page already has its own heading).
|
|
90
|
+
let tmpTitle = (typeof tmpModel.Title === 'string') ? tmpModel.Title : 'System Activity';
|
|
86
91
|
|
|
87
92
|
// --- geometry ---
|
|
88
93
|
const WIDTH = 940;
|
|
@@ -139,26 +144,48 @@ function buildSVG(pModel)
|
|
|
139
144
|
+ '<text x="' + (WIDTH / 2) + '" y="' + (tmpH / 2) + '" text-anchor="middle" font-family="sans-serif" font-size="14" fill="' + _C.muted + '">No activity in this range yet.</text></svg>';
|
|
140
145
|
}
|
|
141
146
|
|
|
142
|
-
|
|
147
|
+
// Legend placement is configurable (default bottom-right, out of the title's way). When it is not at
|
|
148
|
+
// the top, the chart body starts higher and grows a band at the bottom to hold it.
|
|
149
|
+
let tmpLegendTop = tmpLegendPosition.indexOf('top') === 0;
|
|
150
|
+
let tmpLegendRight = tmpLegendPosition.indexOf('right') >= 0;
|
|
151
|
+
let tmpShowLegend = (tmpLegendPosition !== 'none') && (tmpStackKeys.length > 0);
|
|
152
|
+
|
|
153
|
+
// Without a title, reclaim its band so the chart starts higher.
|
|
154
|
+
let tmpTop = tmpTitle ? TOP : (TOP - 22);
|
|
155
|
+
let tmpChartTop = tmpTop + ((tmpShowLegend && tmpLegendTop) ? LEGEND_H : 10);
|
|
143
156
|
const GROUP_H = 22; // a super-group (quarter/year) gets its own header band, so its label
|
|
144
157
|
// never shares a line with the per-row date labels.
|
|
145
158
|
// Count the super-group bands up front so the SVG height accounts for them.
|
|
146
159
|
let tmpGroupCount = 0;
|
|
147
160
|
let tmpScanGroup = null;
|
|
148
161
|
tmpPeriodOrder.forEach((pRow) => { let tmpG = _superGroup(pRow.PeriodStart, tmpPeriod); if (tmpG && tmpG !== tmpScanGroup) { tmpGroupCount += 1; tmpScanGroup = tmpG; } });
|
|
149
|
-
let
|
|
162
|
+
let tmpBodyBottom = tmpChartTop + tmpPeriodOrder.length * ROW_H + tmpGroupCount * GROUP_H;
|
|
163
|
+
let tmpHeight = tmpBodyBottom + ((tmpShowLegend && !tmpLegendTop) ? LEGEND_H : 0) + 16;
|
|
150
164
|
let tmpSVG = [];
|
|
151
165
|
tmpSVG.push('<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 ' + WIDTH + ' ' + tmpHeight + '" width="100%" role="img" aria-label="System activity, one bar per ' + _esc(tmpPeriod) + ' stacked by ' + _esc(tmpStackBy) + ', each column scaled 0 to ' + tmpMaxTotal + '" font-family="-apple-system, Segoe UI, Roboto, sans-serif">');
|
|
152
166
|
|
|
153
|
-
//
|
|
154
|
-
|
|
155
|
-
tmpSVG.push('<text x="0" y="' + (TOP - 22) + '" font-size="15" font-weight="600" fill="' + _C.text + '">System Activity</text>');
|
|
156
|
-
tmpStackKeys.forEach((pKey) =>
|
|
167
|
+
// title (top-left; omitted when Title is an empty string)
|
|
168
|
+
if (tmpTitle)
|
|
157
169
|
{
|
|
158
|
-
tmpSVG.push('<
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
170
|
+
tmpSVG.push('<text x="0" y="' + (tmpTop - 22) + '" font-size="15" font-weight="600" fill="' + _C.text + '">' + _esc(tmpTitle) + '</text>');
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// legend: a swatch + label per stack key, placed per LegendPosition. The total width is measured
|
|
174
|
+
// first so a right-aligned legend ends flush with the chart's right edge.
|
|
175
|
+
if (tmpShowLegend)
|
|
176
|
+
{
|
|
177
|
+
let tmpItems = tmpStackKeys.map((pKey) => ({ Key: pKey, Color: tmpColorByKey[pKey], W: 16 + _esc(pKey).length * 6.8 }));
|
|
178
|
+
let tmpTotalW = tmpItems.reduce((pSum, pItem) => pSum + pItem.W + 16, 0) - 16;
|
|
179
|
+
let tmpLX = tmpLegendRight ? (WIDTH - PAD_R - tmpTotalW) : PAD_L;
|
|
180
|
+
let tmpSwatchY = tmpLegendTop ? (tmpTop - 4) : (tmpBodyBottom + 14);
|
|
181
|
+
let tmpTextY = tmpLegendTop ? (tmpTop + 5) : (tmpBodyBottom + 23);
|
|
182
|
+
tmpItems.forEach((pItem) =>
|
|
183
|
+
{
|
|
184
|
+
tmpSVG.push('<rect x="' + tmpLX.toFixed(1) + '" y="' + tmpSwatchY + '" width="11" height="11" rx="2" fill="' + pItem.Color + '"/>');
|
|
185
|
+
tmpSVG.push('<text x="' + (tmpLX + 16).toFixed(1) + '" y="' + tmpTextY + '" font-size="12" fill="' + _C.muted + '">' + _esc(pItem.Key) + '</text>');
|
|
186
|
+
tmpLX += pItem.W + 16;
|
|
187
|
+
});
|
|
188
|
+
}
|
|
162
189
|
|
|
163
190
|
// column headers: accent-colored title (left) + the axis scale (right). Both columns share one global
|
|
164
191
|
// max, so labeling each track's full-width value with the same number makes the shared, comparable
|
|
@@ -25,6 +25,8 @@ const _DEFAULTS =
|
|
|
25
25
|
Periods: ['Day', 'Week', 'Month', 'Quarter', 'Year'],
|
|
26
26
|
StackBy: 'ActivityType',
|
|
27
27
|
Measure: 'EventCount',
|
|
28
|
+
LegendPosition: 'bottom-right',
|
|
29
|
+
Title: 'System Activity',
|
|
28
30
|
Columns: [
|
|
29
31
|
{ Key: 'WorkItems', Title: 'Work Items', Accent: '#e0891e' },
|
|
30
32
|
{ Key: 'Knowledge', Title: 'Knowledge', Accent: '#3f7fd0' }
|
|
@@ -71,6 +73,8 @@ class PictViewActivityGraph extends libPictView
|
|
|
71
73
|
Periods: Array.isArray(tmpConfig.Periods) ? tmpConfig.Periods : _DEFAULTS.Periods,
|
|
72
74
|
StackBy: tmpConfig.StackBy || _DEFAULTS.StackBy,
|
|
73
75
|
Measure: tmpConfig.Measure || _DEFAULTS.Measure,
|
|
76
|
+
LegendPosition: tmpConfig.LegendPosition || _DEFAULTS.LegendPosition,
|
|
77
|
+
Title: (typeof tmpConfig.Title === 'string') ? tmpConfig.Title : _DEFAULTS.Title,
|
|
74
78
|
Columns: Array.isArray(tmpConfig.Columns) ? tmpConfig.Columns : _DEFAULTS.Columns,
|
|
75
79
|
Buckets: []
|
|
76
80
|
};
|
|
@@ -146,6 +150,8 @@ class PictViewActivityGraph extends libPictView
|
|
|
146
150
|
Period: this._State.Period,
|
|
147
151
|
StackBy: this._State.StackBy,
|
|
148
152
|
Measure: this._State.Measure,
|
|
153
|
+
LegendPosition: this._State.LegendPosition,
|
|
154
|
+
Title: this._State.Title,
|
|
149
155
|
Columns: this._State.Columns,
|
|
150
156
|
Buckets: this._State.Buckets
|
|
151
157
|
});
|