{
"data": {
"abdullahi3000.near": {
"widget": {
"Data.Balances": {
"": "const balances = props.balances; // expected to be in Pikespeak.ai balances API format\n\nif (!balances) {\n const baseApi = \"https://api.pikespeak.ai\";\n const publicApiKey = \"36f2b87a-7ee6-40d8-80b9-5e68e587a5b5\";\n const fetchApiConfig = {\n mode: \"cors\",\n headers: {\n \"x-api-key\": publicApiKey,\n },\n };\n const res = fetch(\n `${baseApi}/account/balances?accounts=${\"harmonic-guild-v1.sputnik-dao.near\"}`,\n fetchApiConfig\n );\n if (!res.body) {\n return \"Loading...\";\n }\n balances = res.body;\n}\n\n// Parsing the data to the format expected by the chart\nconst balancesTotal = balances.balancesTotal.sort(\n (a, b) => b.usdPrice - a.usdPrice\n);\nconst balanceData = balancesTotal.map((balance) => balance.usdPrice);\nconst balanceLabels = balancesTotal.map((balance) => balance.contract);\n\nlet colors = props.colors ?? [\n \"#4498E0\",\n \"#FFD50D\",\n \"#F29BC0\",\n \"#F19D38\",\n \"#82E299\",\n];\n\nconst other_colors = [\n \"#1f77b4\", // Muted Blue\n \"#ff7f0e\", // Safety Orange\n \"#2ca02c\", // Cooked Asparagus Green\n \"#d62728\", // Brick Red\n \"#9467bd\", // Muted Purple\n \"#8c564b\", // Chestnut Brown\n \"#e377c2\", // Raspberry Yogurt Pink\n \"#7f7f7f\", // Middle Gray\n \"#bcbd22\", // Curry Yellow-Green\n \"#17becf\", // Blue-Teal\n];\n\n// use other colors if balanceData.length > colors.length\nif (balanceData.length > colors.length) {\n for (let i = colors.length; i < balanceData.length; i++) {\n colors.push(other_colors[i % other_colors.length]);\n }\n}\n\n// if still not enough, use random colors\nif (balanceData.length > colors.length) {\n for (let i = colors.length; i < balanceData.length; i++) {\n colors.push(\"#\" + Math.floor(Math.random() * 16777215).toString(16));\n }\n}\n\nconst chartData = {\n labels: balanceLabels,\n datasets: [\n {\n data: balanceData,\n label: \"Balance in USD\",\n backgroundColor: colors,\n hoverBackgroundColor: colors,\n hoverOffset: -4,\n borderAlign: \"inner\",\n borderWidth: 2,\n hoverBorderWidth: 0,\n },\n ],\n};\n\nconst code = `\n\n<html>\n <head>\n <script src=\"https://unpkg.com/chart.js@4.3.0/dist/chart.umd.js\"></script>\n <script\n type=\"text/javascript\"\n src=\"https://cdnjs.cloudflare.com/ajax/libs/iframe-resizer/4.3.6/iframeResizer.contentWindow.js\"\n ></script>\n </head>\n <body>\n <style>\n * {\n font-family: \"Open Sans\", sans-serif !important; \n }\n #container {\n position: relative;\n width: 100%;\n height: 100%;\n display: grid;\n grid-template-columns: 260px auto;\n grid-template-rows: 80px 1fr;\n column-gap: 26px;\n justify-items: center;\n }\n #titleContainer {\n display: flex;\n flex-direction: column;\n justify-content: center;\n align-items: center;\n text-align: center;\n margin: 12px auto;\n gap: 8px;\n }\n #currentName {\n font-size: 16px;\n color: rgb(153, 153, 153);\n font-weight: 600;\n text-overflow: ellipsis;\n max-width: 220px;\n overflow: hidden;\n }\n #currentValue {\n font-size: 24px;\n font-weight: 600;\n color: rgb(0, 0, 0);\n font-family: \"Open Sans\", sans-serif;\n }\n #donutContainer {\n width: 260px;\n height: 260px;\n }\n #tableContainer {\n width: 100%;\n height: 100%;\n overflow: auto;\n grid-row-start: 1;\n grid-row-end: 3;\n grid-column: 2;\n }\n\n \n @media (max-width: 768px) {\n #container {\n grid-template-columns: 1fr;\n grid-template-rows: 80px 260px auto;\n row-gap: 26px;\n }\n #titleContainer {\n grid-row: 1;\n grid-column: 1;\n }\n #donutContainer {\n grid-row: 2;\n grid-column: 1;\n }\n #tableContainer {\n grid-row: 3;\n grid-column: 1;\n }\n }\n\n #table { \n display: flex;\n flex-direction: column;\n flex: 1;\n min-width: 300px;\n \n height: 100%;\n justify-content: center;\n}\n #table .item {\n padding: 17px 0;\n border-bottom: 1px solid #eee;\n font-size: 14px;\n color: #999;\n display: grid;\n grid-template-columns: 20px 1fr 1fr 1fr;\n align-items: center;\n gap: 10px;\n }\n\n #table.hover .item {\n opacity: 0.3 !important;\n }\n\n #table.hover .item.hover {\n opacity: 1 !important;\n }\n \n #table .item:last-child {\n border-bottom: none;\n }\n\n #table .item > span:nth-child(2) {\n text-transform: lowercase;\n color: #000;\n max-width: 200px;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n\n #table .item > span:nth-child(3) {\n text-align: center;\n }\n\n #table .item > span:nth-child(4) {\n text-align: end;\n }\n\n #table .ball {\n width: 15px;\n height: 15px;\n border-radius: 50%;\n }\n\n </style>\n <div id=\"container\">\n <div id=\"titleContainer\">\n <span id=\"currentName\"> Total Balance </span>\n <span id=\"currentValue\"> </span>\n </div>\n <div id=\"donutContainer\">\n <canvas id=\"donutChart\"></canvas>\n </div>\n <div id=\"tableContainer\">\n <div id=\"table\">\n\n </div>\n </div>\n </div>\n </body>\n \n <script>\n \n function initTable (balances, colors){\n var balancesTable = document.getElementById(\"table\");\n\n balances.balancesTotal.forEach(function(balance, i) {\n var itemDiv = document.createElement(\"div\");\n itemDiv.className = \"item\";\n itemDiv.id = \"table-item-\"+i;\n\n var ballSpan = document.createElement(\"span\");\n ballSpan.className = \"ball\";\n ballSpan.style.background = colors[i];\n\n var contractSpan = document.createElement(\"span\");\n contractSpan.title = balance.contract;\n contractSpan.textContent = balance.contract;\n\n var priceSpan = document.createElement(\"span\");\n priceSpan.title = \"$\"+Number(balance.usdPrice).toFixed(2);\n priceSpan.textContent = \"$\"+Number(balance.usdPrice).toFixed(2);\n\n var amountSpan = document.createElement(\"span\");\n amountSpan.title = Number(balance.amount);\n amountSpan.textContent = Number(balance.amount).toFixed(2) + \" \" + balance.symbol;\n\n itemDiv.appendChild(ballSpan);\n itemDiv.appendChild(contractSpan);\n itemDiv.appendChild(priceSpan);\n itemDiv.appendChild(amountSpan);\n\n balancesTable.appendChild(itemDiv);\n });\n}\n\n function createChart(ctx, data, colors) {\n const chart = new Chart(ctx, {\n type: \"doughnut\",\n data: data,\n options: {\n cutout: \"66%\",\n responsive: true,\n plugins: {\n legend: {\n display: false,\n },\n\n insideDoughnut: {},\n },\n onHover: handleHover,\n },\n plugins: [\n {\n id: \"insideDoughnut\",\n beforeDraw: beforeDraw,\n },\n ],\n });\n\n function beforeDraw(chart){\n const {\n chartArea: { left, top, right, bottom },\n ctx,\n } = chart;\n const centerX = (left + right) / 2;\n const centerY = (top + bottom) / 2;\n\n // Save the current canvas state\n ctx.save();\n\n // Set the font, alignment, baseline, and color\n ctx.font = \"bold \" + (right - left) * 0.14 + \"px sans-serif\";\n ctx.textAlign = \"center\";\n ctx.textBaseline = \"middle\";\n ctx.fillStyle = \"#000\";\n\n // If an element is being hovered\n if (chart.hoveredElement) {\n const { datasetIndex, dataIndex } = chart.hoveredElement;\n const dataset = chart.data.datasets[datasetIndex];\n const total = dataset.data.reduce((a, b) => a + b, 0);\n const value = dataset.data[dataIndex];\n const percentage = ((value / total) * 100).toLocaleString(\n undefined,\n {\n maximumFractionDigits: 2,\n }\n );\n\n // Draw the text\n ctx.fillText(percentage + \"%\", centerX, centerY);\n } else {\n ctx.fillText(\"100%\", centerX, centerY);\n }\n\n // Restore the canvas state\n ctx.restore();\n }\n\n function handleHover(event, chartElement) {\n const activePoints = chart.getElementsAtEventForMode(\n event,\n \"nearest\",\n { intersect: true },\n true\n );\n\n const currentValue = document.getElementById('currentValue');\n const currentName = document.getElementById('currentName');\n\n if (chartElement[0]) {\n const datasetIndex = chartElement[0].datasetIndex;\n const dataIndex = chartElement[0].index;\n\n const dataset = chart.data.datasets[datasetIndex];\n\n // Store the original colors\n if (!dataset.originalColors) {\n dataset.originalColors = dataset.backgroundColor.slice();\n }\n\n if (activePoints.length > 0) {\n dataset.backgroundColor = dataset.backgroundColor.map((old, i) => {\n if (i !== dataIndex) {\n return colors[i] + \"30\";\n }\n return colors[i];\n });\n\n const value = dataset.data[dataIndex];\n const total = dataset.data.reduce((a, b) => a + b, 0);\n const percentage = ((value / total) * 100).toLocaleString(\n undefined,\n {\n maximumFractionDigits: 2,\n }\n );\n\n // Update the title\n currentValue.innerHTML = \"$\" + value.toLocaleString(undefined, {\n maximumFractionDigits: 2,\n });\n currentName.innerHTML = chart.data.labels[dataIndex];\n\n\n // Store the hovered element indexes in the chart instance\n chart.hoveredElement = { datasetIndex, dataIndex };\n\n // Add hover state to the table\n document.querySelectorAll(\".item\").forEach(function(item) {\n item.classList.remove(\"hover\");\n });\n var tableItem = document.getElementById(\"table-item-\"+dataIndex);\n var tableEl = document.getElementById(\"table\");\n tableItem.classList.add(\"hover\");\n tableEl.classList.add(\"hover\");\n\n } else {\n // Reset all elements to their original \n dataset.backgroundColor = dataset.originalColors;\n chart.hoveredElement = null;\n \n document.getElementById(\"table\").classList.remove(\"hover\");\n document.querySelectorAll(\".item\").forEach(function(item) {\n item.classList.remove(\"hover\");\n });\n }\n } else {\n // Reset all elements to their original color when not hovering over any element\n chart.data.datasets.forEach((dataset) => {\n if (dataset.originalColors) {\n dataset.backgroundColor = dataset.originalColors;\n }\n });\n chart.hoveredElement = null;\n\n currentValue.innerHTML = currentValue.dataset.value;\n currentName.innerHTML = currentName.dataset.value;\n\n \n \n document.getElementById(\"table\").classList.remove(\"hover\");\n document.querySelectorAll(\".item\").forEach(function(item) {\n item.classList.remove(\"hover\");\n });\n }\n\n chart.update();\n }\n\n }\n\n window.addEventListener(\"message\", function (event) {}, false);\n\n\n const formatNumber = (num) => {\n if (num >= 1000000000) {\n return (num / 1000000000).toFixed(1).replace(/\\.0$/, \"\") + \"b\";\n }\n if (num >= 1000000) {\n return (num / 1000000).toFixed(1).replace(/\\.0$/, \"\") + \"m\";\n }\n if (num >= 1000) {\n return (num / 1000).toFixed(1).replace(/\\.0$/, \"\") + \"k\";\n }\n return num;\n };\n\n\n const handleMessage = (m) => {\n const { data, colors, balances } = m;\n document.getElementById(\"currentName\").innerHTML = \"Total Balance\";\n document.getElementById(\"currentValue\").innerHTML = \"$\"+formatNumber(balances.totalUsd);\n document.getElementById(\"currentValue\").title = \"$\"+balances.totalUsd;\n\n document.getElementById(\"currentName\").dataset.value = \"Total Balance\";\n document.getElementById(\"currentValue\").dataset.value = \"$\"+formatNumber(balances.totalUsd);\n\n const ctx = document.getElementById(\"donutChart\").getContext(\"2d\");\n createChart(ctx, data, colors);\n initTable(balances, colors);\n\n window.iFrameResizer.onMessage = ()=>{};\n };\n\n window.iFrameResizer = {\n onMessage: handleMessage,\n };\n </script>\n</html>\n`;\n\nreturn (\n <div\n className=\"w-100\"\n style={{\n minHeight: \"300px\",\n minWidth: \"300px\",\n }}\n >\n <iframe\n iframeResizer\n className=\"w-100\"\n srcDoc={code}\n message={{\n data: chartData,\n colors,\n balances: {\n totalUsd: balances.totalUsd,\n balancesTotal: balancesTotal,\n },\n }}\n />\n </div>\n);\n"
}
}
}
}
}