Add icon labels

This commit is contained in:
Rich Lott / Artful Robot 2025-03-31 11:41:35 +01:00
parent dde3c8ec96
commit 8591d6c060
2 changed files with 51 additions and 14 deletions

View file

@ -51,6 +51,7 @@
</linearGradient> </linearGradient>
</defs> </defs>
<g transform="translate({{$ctrl.sankey.labelWidth}})">
<path ng-repeat="p in $ctrl.sankey.snakes" <path ng-repeat="p in $ctrl.sankey.snakes"
d="{{p.d}}" d="{{p.d}}"
fill="{{p.fill}}" fill="{{p.fill}}"
@ -58,6 +59,35 @@
> >
<title>{{p.source.contact_count}} {{p.fromCat.label}} → {{p.toCat.label}}</title> <title>{{p.source.contact_count}} {{p.fromCat.label}} → {{p.toCat.label}}</title>
</path> </path>
</g>
<!-- to-side labels -->
<g transform="translate({{$ctrl.sankey.labelWidth + $ctrl.sankey.snakeWidth}})">
<rect ng-repeat="toAna in $ctrl.sankey.analysisArray"
x=0
y="{{toAna.y}}"
width=16
height="{{toAna.toCatHeight}}"
fill="{{toAna.cat.color}}"
></rect>
<foreignobject ng-repeat="toAna in $ctrl.sankey.analysisArray"
x="20" y="{{toAna.y}}" width="{{$ctrl.sankey.labelWidth}}" height="{{toAna.thisCatHeight}}">
<span class=sankey-label style="display:block;color:{{toAna.cat.color}}"><i class="crm-i {{toAna.cat.icon}}" title="{{toAna.cat.label}}"></i></span>
</foreignobject>
</g>
<!-- from-side labels -->
<g transform="translate({{$ctrl.sankey.labelWidth - 16}})">
<rect ng-repeat="toAna in $ctrl.sankey.analysisArray"
x=0
y="{{toAna.y}}"
width=16
height="{{toAna.fromCatHeight}}"
fill="{{toAna.cat.color}}"
></rect>
<foreignobject ng-repeat="toAna in $ctrl.sankey.analysisArray"
x="-32" y="{{toAna.y}}" width="32" height="{{toAna.thisCatHeight}}">
<span class=sankey-label style="display:block;color:{{toAna.cat.color}}"><i class="crm-i {{toAna.cat.icon}}" title="{{toAna.cat.label}}"></i></span>
</foreignobject>
</g>
<!-- <!--
<g ng-repeat="label in $ctrl.sankey.labels"> <g ng-repeat="label in $ctrl.sankey.labels">
<text x="{{$ctrl.sankey.labelWidth - 4}}" <text x="{{$ctrl.sankey.labelWidth - 4}}"

View file

@ -9,7 +9,7 @@
const catDefsIndexed = {}; const catDefsIndexed = {};
// Development: set this true to use a small defined fixture instead of real data. // Development: set this true to use a small defined fixture instead of real data.
const useTestFixtures = true; const useTestFixtures = false;
let allFlowsData; let allFlowsData;
@ -23,6 +23,7 @@
width = Math.max(600, document.querySelector('.contact-cats-sankey').clientWidth), width = Math.max(600, document.querySelector('.contact-cats-sankey').clientWidth),
snakeWidth = width - 2*labelWidth; snakeWidth = width - 2*labelWidth;
ctrl.sankey.width = width; ctrl.sankey.width = width;
ctrl.sankey.snakeWidth = snakeWidth;
if (!(flowsData.length)) { if (!(flowsData.length)) {
ctrl.noStats = true; ctrl.noStats = true;
@ -53,6 +54,7 @@
// offsetWithinSourcesByCat tracks, per source, the place the // offsetWithinSourcesByCat tracks, per source, the place the
// next snake's left-top should be, relative to the top for that category. // next snake's left-top should be, relative to the top for that category.
const analysis = {}, analysisArray = [], offsetWithinSourcesByCat = {}; const analysis = {}, analysisArray = [], offsetWithinSourcesByCat = {};
ctrl.sankey.analysisArray = analysisArray;
ctrl.catDefs.forEach(cat => { ctrl.catDefs.forEach(cat => {
analysis[cat.id] = { sources: [], now: 0, previous: 0, cat, y:0 }; analysis[cat.id] = { sources: [], now: 0, previous: 0, cat, y:0 };
// This is just to make looping in order easier. // This is just to make looping in order easier.
@ -88,9 +90,11 @@
let accumulatedY = 0; let accumulatedY = 0;
analysisArray.forEach(toAna => { analysisArray.forEach(toAna => {
toAna.y = accumulatedY; toAna.y = accumulatedY;
let thisCatHeight = Math.ceil(Math.max(catHeightMin, scale * Math.max(toAna.now, toAna.previous))); toAna.toCatHeight = Math.max(1, Math.round(scale * toAna.now));
accumulatedY += thisCatHeight + catHeightGap; toAna.fromCatHeight = Math.max(1,Math.round(scale * toAna.previous));
console.log({toCat: toAna.cat.label, toCatY: toAna.y, accumulatedY, thisCatHeight}); toAna.thisCatHeight = Math.ceil(Math.max(catHeightMin, scale * Math.max(toAna.now, toAna.previous)));
accumulatedY += toAna.thisCatHeight + catHeightGap;
// console.log({toCat: toAna.cat.label, toCatY: toAna.y, accumulatedY, thisCatHeight});
// Intialise source offsets for this category. // Intialise source offsets for this category.
offsetWithinSourcesByCat[toAna.cat.id] = 0; offsetWithinSourcesByCat[toAna.cat.id] = 0;
// Make sure that our 'from' snake ends are ordered by category presentation_order // Make sure that our 'from' snake ends are ordered by category presentation_order
@ -160,10 +164,10 @@
if (useTestFixtures) { if (useTestFixtures) {
ctrl.startDate = new Date('2025-03-10'); ctrl.startDate = new Date('2025-03-10');
} }
ctrl.startDate = new Date('2025-03-10'); ctrl.startDate = new Date('2025-03-25');
ctrl.endDate = null; ctrl.endDate = null;
ctrl.stats = null; ctrl.stats = null;
ctrl.sankey = { height: 500, width: 900, labelWidth: 150, snakeWidth: 600, snakes: [] }; ctrl.sankey = { height: 500, width: 900, labelWidth: 150, snakeWidth: 600, snakes: [], analysisArray: [] };
if (useTestFixtures) { if (useTestFixtures) {
ctrl.catDefs = [ ctrl.catDefs = [
@ -173,7 +177,10 @@
} }
else { else {
ctrl.catDefs = await crmStatus({ start: ts('Loading...'), success: ''}, ctrl.catDefs = await crmStatus({ start: ts('Loading...'), success: ''},
crmApi4("ContactCategoryDefinition", 'get', { orderBy: { presentation_order: 'ASC' }, withLabels: true }) crmApi4("ContactCategoryDefinition", 'get', {
orderBy: { presentation_order: 'ASC' },
withLabels: true
})
); );
} }
ctrl.loading = false; ctrl.loading = false;