MediaWiki:Gadget-wix-interactive.js: Difference between revisions
Content deleted Content added
No edit summary |
No edit summary |
||
Line 25:
'liability-waterfall': initLiabilityWaterfall,
'prob-weighted': initProbWeighted,
'discount-rate': initDiscountRate,
'balance-sheet': initBalanceSheet
};
Line 1,984 ⟶ 1,985:
// Initial render
update();
}
/* ================================================================
BALANCE SHEET
IFRS 17 balance sheet: two-column stacked bar (assets vs
liabilities) with click-to-explore detail card and brackets.
Blocks read from data-wix-blocks JSON attribute.
================================================================ */
function initBalanceSheet( container ) {
/* ── Read blocks from data attribute ─────────────────────── */
var raw = wix.data( container, 'wix-blocks', '[]' );
var blocks;
try {
blocks = JSON.parse( raw );
} catch ( e ) {
return;
}
if ( !blocks.length ) {
return;
}
var TOTAL = blocks[ 0 ].val;
/* ── Colors (WIX tokens for brackets/axis) ───────────────── */
var styles = getComputedStyle( container );
var colorAxis = styles.getPropertyValue( '--wix-text-muted' ).trim() || '#54595d';
var colorLine = styles.getPropertyValue( '--wix-border' ).trim() || '#c8ccd1';
/* ── State ───────────────────────────────────────────────── */
var selected = 0;
var hitAreas = [];
var W, H;
/* ── Build UI ────────────────────────────────────────────── */
wix.empty( container );
var wrapper = wix.el( 'div', { className: 'wix-eng-wrapper' } );
container.appendChild( wrapper );
// Canvas
var canvas = wix.el( 'canvas', { style: { cursor: 'pointer' } } );
wrapper.appendChild( wix.el( 'div', { className: 'wix-bs-chart' }, [ canvas ] ) );
// Detail card
var detailTitle = wix.el( 'div', { className: 'wix-wf-detail-title' } );
var detailBody = wix.el( 'div', { className: 'wix-wf-detail-body' } );
var detailCard = wix.el( 'div', { className: 'wix-wf-detail' }, [ detailTitle, detailBody ] );
wrapper.appendChild( detailCard );
/* ── Canvas Helpers ──────────────────────────────────────── */
function resize() {
var dpr = window.devicePixelRatio || 1;
var rect = canvas.getBoundingClientRect();
if ( rect.width === 0 ) {
return;
}
W = rect.width;
H = 400;
canvas.width = W * dpr;
canvas.height = H * dpr;
canvas.getContext( '2d' ).setTransform( dpr, 0, 0, dpr, 0, 0 );
}
/* roundRect polyfill */
function roundRect( ctx2, x, y, w, h, r ) {
if ( ctx2.roundRect ) {
ctx2.beginPath();
ctx2.roundRect( x, y, w, h, r );
return;
}
ctx2.beginPath();
ctx2.moveTo( x + r, y );
ctx2.lineTo( x + w - r, y );
ctx2.arcTo( x + w, y, x + w, y + r, r );
ctx2.lineTo( x + w, y + h - r );
ctx2.arcTo( x + w, y + h, x + w - r, y + h, r );
ctx2.lineTo( x + r, y + h );
ctx2.arcTo( x, y + h, x, y + h - r, r );
ctx2.lineTo( x, y + r );
ctx2.arcTo( x, y, x + r, y, r );
ctx2.closePath();
}
/* Bracket with 1 or 2 line label */
function drawBracket( ctx2, x, y1, y2, label1, label2 ) {
var mid = ( y1 + y2 ) / 2;
ctx2.strokeStyle = colorLine;
ctx2.lineWidth = 1;
ctx2.beginPath();
ctx2.moveTo( x, y1 );
ctx2.lineTo( x + 6, y1 );
ctx2.lineTo( x + 6, y2 );
ctx2.lineTo( x, y2 );
ctx2.stroke();
ctx2.fillStyle = colorAxis;
ctx2.font = '400 11px system-ui, sans-serif';
ctx2.textAlign = 'left';
ctx2.textBaseline = 'middle';
if ( label2 ) {
ctx2.fillText( label1, x + 12, mid - 7 );
ctx2.fillText( label2, x + 12, mid + 7 );
} else {
ctx2.fillText( label1, x + 12, mid );
}
}
/* ── Draw ────────────────────────────────────────────────── */
function draw() {
var ctx2 = canvas.getContext( '2d' );
ctx2.clearRect( 0, 0, W, H );
hitAreas = [];
var padTop = 30;
var padBot = 10;
var padL = 10;
var bracketZone = 150;
var colGap = 8;
var colW = Math.min( 180, ( W - padL - bracketZone - colGap ) / 2 );
var chartH = H - padTop - padBot;
var xA = padL;
var xL = padL + colW + colGap;
// Column headers
ctx2.font = '400 13px system-ui, sans-serif';
ctx2.textAlign = 'center';
ctx2.fillStyle = colorAxis;
ctx2.fillText( 'Assets', xA + colW / 2, padTop - 10 );
ctx2.fillText( 'Liabilities', xL + colW / 2, padTop - 10 );
// ── Assets column (single block) ──
var aBlock = blocks[ 0 ];
var isSA = selected === 0;
ctx2.fillStyle = isSA ? aBlock.hColor : aBlock.color;
roundRect( ctx2, xA, padTop, colW, chartH, 4 );
ctx2.fill();
ctx2.fillStyle = aBlock.textColor;
ctx2.font = '500 15px system-ui, sans-serif';
ctx2.textAlign = 'center';
ctx2.textBaseline = 'middle';
ctx2.fillText( aBlock.label, xA + colW / 2, padTop + chartH / 2 - 14 );
ctx2.fillStyle = aBlock.subColor;
ctx2.font = '400 12px system-ui, sans-serif';
ctx2.fillText( aBlock.sub, xA + colW / 2, padTop + chartH / 2 + 4 );
ctx2.fillText( '\u20AC' + aBlock.val + 'm', xA + colW / 2, padTop + chartH / 2 + 22 );
hitAreas.push( { x: xA, y: padTop, w: colW, h: chartH, idx: 0 } );
// ── Liabilities column (stacked segments) ──
var liab = blocks.slice( 1 );
var segGap = 3;
var totalGap = ( liab.length - 1 ) * segGap;
var availH = chartH - totalGap;
var y = padTop;
var yPositions = [];
var i, b, bH, isSel, midY;
for ( i = 0; i < liab.length; i++ ) {
b = liab[ i ];
bH = ( b.val / TOTAL ) * availH;
isSel = selected === i + 1;
ctx2.fillStyle = isSel ? b.hColor : b.color;
roundRect( ctx2, xL, y, colW, bH, 4 );
ctx2.fill();
midY = y + bH / 2;
ctx2.textAlign = 'center';
ctx2.textBaseline = 'middle';
if ( bH > 60 ) {
ctx2.fillStyle = b.textColor;
ctx2.font = '500 14px system-ui, sans-serif';
ctx2.fillText( b.label, xL + colW / 2, midY - 10 );
ctx2.fillStyle = b.subColor;
ctx2.font = '400 11px system-ui, sans-serif';
ctx2.fillText( b.sub, xL + colW / 2, midY + 6 );
ctx2.fillText( '\u20AC' + b.val + 'm', xL + colW / 2, midY + 22 );
} else if ( bH > 35 ) {
ctx2.fillStyle = b.textColor;
ctx2.font = '500 14px system-ui, sans-serif';
ctx2.fillText( b.label, xL + colW / 2, midY - 6 );
ctx2.fillStyle = b.subColor;
ctx2.font = '400 11px system-ui, sans-serif';
ctx2.fillText( '\u20AC' + b.val + 'm', xL + colW / 2, midY + 10 );
} else {
ctx2.fillStyle = b.textColor;
ctx2.font = '500 14px system-ui, sans-serif';
ctx2.fillText( b.label + ' \u20AC' + b.val + 'm', xL + colW / 2, midY );
}
hitAreas.push( { x: xL, y: y, w: colW, h: bH, idx: i + 1 } );
yPositions.push( { top: y, bot: y + bH } );
y += bH + segGap;
}
// ── Brackets ──
var bx1 = xL + colW + 10;
if ( yPositions.length >= 1 ) {
drawBracket( ctx2, bx1, yPositions[ 0 ].top + 2, yPositions[ 0 ].bot - 2, 'Firm\u2019s funds' );
}
if ( yPositions.length >= 4 ) {
drawBracket( ctx2, bx1, yPositions[ 1 ].top + 2, yPositions[ 3 ].bot - 2, 'Technical', 'provisions' );
var bx2 = bx1 + 72;
drawBracket( ctx2, bx2, yPositions[ 2 ].top + 2, yPositions[ 3 ].bot - 2, 'Fulfilment', 'cash flows' );
}
}
/* ── Detail Card ─────────────────────────────────────────── */
function renderDetail() {
var b = blocks[ selected ];
detailTitle.textContent = b.question;
detailTitle.style.color = b.textColor;
detailBody.textContent = b.body;
}
/* ── Hit Testing ─────────────────────────────────────────── */
function hitTest( e ) {
var rect = canvas.getBoundingClientRect();
var mx = e.clientX - rect.left;
var my = e.clientY - rect.top;
for ( var i = hitAreas.length - 1; i >= 0; i-- ) {
var a = hitAreas[ i ];
if ( mx >= a.x && mx <= a.x + a.w && my >= a.y && my <= a.y + a.h ) {
return a.idx;
}
}
return -1;
}
/* ── Event Wiring ────────────────────────────────────────── */
canvas.addEventListener( 'mousemove', function ( e ) {
canvas.style.cursor = hitTest( e ) >= 0 ? 'pointer' : 'default';
} );
canvas.addEventListener( 'click', function ( e ) {
var h = hitTest( e );
if ( h >= 0 ) {
selected = h;
draw();
renderDetail();
}
} );
window.addEventListener( 'resize', function () {
resize();
draw();
} );
// Initial render
resize();
draw();
renderDetail();
}
| |||