Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions assets/icons.json
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,8 @@
"bar-chart": "M3,22V8H7V22H3M10,22V2H14V22H10M17,22V14H21V22H17Z",
"gantt-chart": "M2,5H10V2H12V22H10V18H6V15H10V13H4V10H10V8H2V5M14,5H17V8H14V5M14,10H19V13H14V10M14,15H22V18H14V15Z",
"crown": "M5 16L3 5L8.5 10L12 4L15.5 10L21 5L19 16H5M19 19C19 19.6 18.6 20 18 20H6C5.4 20 5 19.6 5 19V18H19V19Z",
"earth": "M17.9,17.39C17.64,16.59 16.89,16 16,16H15V13A1,1 0 0,0 14,12H8V10H10A1,1 0 0,0 11,9V7H13A2,2 0 0,0 15,5V4.59C17.93,5.77 20,8.64 20,12C20,14.08 19.2,15.97 17.9,17.39M11,19.93C7.05,19.44 4,16.08 4,12C4,11.38 4.08,10.78 4.21,10.21L9,15V16A2,2 0 0,0 11,18M12,2A10,10 0 0,0 2,12A10,10 0 0,0 12,22A10,10 0 0,0 22,12A10,10 0 0,0 12,2Z",
"city": "M15,23H13V21H15V23M19,21H17V23H19V21M15,17H13V19H15V17M7,21H5V23H7V21M7,17H5V19H7V17M19,17H17V19H19V17M15,13H13V15H15V13M19,13H17V15H19V13M21,9A2,2 0 0,1 23,11V23H21V11H11V23H9V15H3V23H1V15A2,2 0 0,1 3,13H9V11A2,2 0 0,1 11,9V7A2,2 0 0,1 13,5H15V1H17V5H19A2,2 0 0,1 21,7V9M19,9V7H13V9H19Z"
"drop": "M12 18C12 18.7 12.12 19.36 12.34 20C12.23 20 12.12 20 12 20C8.69 20 6 17.31 6 14C6 10 12 3.25 12 3.25S16.31 8.1 17.62 12C16.93 12.06 16.28 12.22 15.67 12.47C15 10.68 13.5 8.33 12 6.39C10 8.96 8 12.23 8 14C8 16.21 9.79 18 12 18M19 17V14H17V17H14V19H17V22H19V19H22V17H19Z",
"faucet": "M21 21H3C3 19.9 3.9 19 5 19H19C20.11 19 21 19.89 21 21M19 7C19 5.39 17.93 3 15 3S11 5.39 11 7V18H13V7C13 6.54 13.17 5 15 5S17 6.54 17 7H16.5V9H19.5V7H19M7 12C6.45 12 6 12.45 6 13V14H3V15H6V18H8V13C8 12.45 7.55 12 7 12M21 14H18V13C18 12.45 17.55 12 17 12S16 12.45 16 13V18H18V15H21V14Z"
}
3 changes: 3 additions & 0 deletions assets/styles/base.scss
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ $grayscale: (
// --supply: #65c7f8;
--supply: #1ca7ed;
--staking: #85f891;
--geo-map: #8B8C8D;
}

[theme="dimmed"] {
Expand Down Expand Up @@ -139,6 +140,7 @@ $grayscale: (
--block-progress-fill-background: #33a853;
--logo-name: var(--txt-primary);
--bar-fill: rgb(243, 147, 45);
--geo-map: #8B8C8D;
}

[theme="light"] {
Expand Down Expand Up @@ -200,6 +202,7 @@ $grayscale: (
--block-progress-fill-background: #33a853;
--logo-name: var(--txt-primary);
--bar-fill: rgb(243, 147, 45);
--geo-map: #8B8C8D;
}

@font-face {
Expand Down
5 changes: 5 additions & 0 deletions components/LeftSidebar.vue
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,11 @@ const mainLinks = reactive([
path: "/stats?tab=rollups",
queryParam: {tab: "rollups"},
},
{
name: "Ecosystem",
path: "/stats?tab=ecosystem",
queryParam: {tab: "ecosystem"},
},
],
},
])
Expand Down
290 changes: 290 additions & 0 deletions components/modules/stats/BarplotChartCard.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,290 @@
<script setup>
/** Vendor */
import * as d3 from "d3"
import { DateTime } from "luxon"

/** Stats Components */
import DiffChip from "@/components/modules/stats/DiffChip.vue"

/** Services */
import { abbreviate, capitilize, comma, formatBytes } from "@/services/utils"

/** API */
import { fetchSeries, fetchSeriesCumulative } from "@/services/api/stats"

const props = defineProps({
series: {
type: Object,
required: true,
},
data: {
type: Array,
required: true,
},
})

const chartEl = ref()
const tooltipEl = ref()
const tooltip = ref({
show: false,
})
const labels = ref(props.data.map(d => d.name))

const buildChart = (chart) => {
const { height, width } = chart.getBoundingClientRect()
const margin = { top: 6, right: 12, bottom: 42, left: 12 }
const chartWidth = width - margin.left - margin.right
const chartHeight = height - margin.top - margin.bottom
const maxAmount = d3.max(props.data.map(d => d.amount))

const x = d3.scaleBand()
.range([ 0, chartWidth ])
.domain(labels.value)
.padding(0.3)

const y = d3.scaleLinear()
.domain([0, maxAmount])
.range([ chartHeight, 0])

const onPointerMoved = function (event) {
tooltip.value.show = true

const index = Math.round((d3.pointer(event)[0] - x.step() / 2) / x.step());
const idx = Math.max(0, Math.min(index, labels.value.length - 1));

const elements = document.querySelectorAll(`[metric=${props.series.name}]`)
elements.forEach(el => {
if (+el.getAttribute('id') === idx) {
el.style.filter = "brightness(1.2)"
} else {
el.style.filter = "brightness(0.6)"
}
})

const value = props.data[idx]
let xPos = x(value.name) + x.bandwidth() / 2
let yPos = y(value.amount) - 5
if (tooltipEl.value) {
if (xPos + tooltipEl.value.wrapper.getBoundingClientRect().width > chartWidth) {
xPos = x(value.name) - tooltipEl.value.wrapper.getBoundingClientRect().width + x.bandwidth() / 2
}

yPos = y(value.amount) - tooltipEl.value.wrapper.getBoundingClientRect().height - 5
}
tooltip.value.x = xPos
tooltip.value.y = yPos
tooltip.value.amount = value.amount
}

const onPointerLeft = function (event, d) {
const elements = document.querySelectorAll(`[metric=${props.series.name}]`)
elements.forEach(el => {
el.style.filter = "brightness(1)"
})

tooltip.value.show = false
}

/** SVG Container */
const svg = d3
.create("svg")
.attr("width", width)
.attr("height", height)
.attr("viewBox", [0, 0, width, height])
.on("pointerenter pointermove", onPointerMoved)
.on("pointerleave", onPointerLeft)
.on("touchstart", (event) => event.preventDefault())

// Add axises
svg.append("g")
.attr("transform", "translate(0," + chartHeight + ")")
.attr("color", "var(--op-20)")
.call(d3.axisBottom(x))
.selectAll("text")
.attr("id", (d, i) => i)
.attr("metric", props.series.name)
.attr("color", "var(--txt-primary)")
.attr("font-size", "12")
.attr("transform", "translate(-10, 4) rotate(-60)")
.style("text-anchor", "end")
.style("transition", "all 0.3s ease-in-out")

svg.append("g")
.attr("color", "var(--op-20)")
.call(d3.axisLeft(y))

// Bars
svg.selectAll("bar")
.data(props.data)
.enter()
.append("rect")
.attr("id", (d, i) => i)
.attr("metric", props.series.name)
.attr("x", d => x(d.name))
.attr("width", x.bandwidth())
.attr("fill", "var(--brand)")
.attr("height", d => chartHeight - y(0))
.attr("y", d => y(0))

svg.selectAll("rect")
.transition()
.duration(800)
.attr("y", d => y(d.amount))
.attr("height", d => chartHeight - y(d.amount))
.delay((d, i) => i * 100)
.on("end", () => d3.select(this).style("transition", "all 0.3s ease-in-out"))

if (chart.children[0]) chart.children[0].remove()
chart.append(svg.node())
}

onMounted(() => {
buildChart(chartEl.value.wrapper)
})
</script>

<template>
<Flex direction="column" justify="start" gap="8" wide :class="$style.wrapper">
<Flex align="center" justify="start" wide>
<Text size="14" weight="600" color="secondary"> {{ `By ${series.title}` }} </Text>
</Flex>

<Flex align="center" :class="$style.chart_wrapper">
<Transition name="fastfade">
<div v-if="tooltip.show" :class="$style.tooltip_wrapper">
<Flex
ref="tooltipEl"
align="center"
justify="between"
:style="{ transform: `translate(${tooltip.x}px, ${tooltip.y}px)` }"
gap="8"
:class="$style.tooltip"
>
<Text size="12" weight="500" color="secondary"> Amount: </Text>
<Text size="12" weight="600" color="primary"> {{ tooltip.amount }} </Text>
</Flex>
</div>
</Transition>

<Flex ref="chartEl" :class="$style.chart" />
</Flex>
</Flex>
</template>

<style module>
.wrapper {
width: 100%;
height: 100%;

background: var(--card-background);
border-radius: 12px;

padding: 16px;
}

.legend_wrapper {
max-width: 55%;
}

.legend_item {
transition: all 0.6s ease;
}

.legend {
width: 10px;
height: 10px;

border-radius: 5px;
cursor: pointer;

margin-right: 6px;
}

.clickable {
cursor: pointer;
}

.chart_wrapper {
width: 100%;
height: 100%;

position: relative;
}

.chart {
width: 100%;
height: 100%;
position: absolute;

overflow: hidden;

& svg {
overflow: visible;
}
}

.fadein {
opacity: 0;
animation-name: fadeIn;
animation-duration: 1s;
animation-fill-mode: forwards;
}

.loading {
animation: loading 1s ease infinite;
}

.tooltip_wrapper {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;

& .tooltip {
min-width: 50px;
height: 22px;
pointer-events: none;
position: absolute;
z-index: 10;

background: var(--card-background);
border-radius: 4px;
box-shadow: inset 0 0 0 1px var(--op-5), 0 14px 34px rgba(0, 0, 0, 15%), 0 4px 14px rgba(0, 0, 0, 5%);

padding: 0 6px;

transition: all 0.2s ease;
}
}

@keyframes loading {
0% {
opacity: 1;
}

50% {
opacity: 0.5;
}

100% {
opacity: 1;
}
}

@keyframes fadeIn {
from {
opacity: 0;
}
to {
opacity: 1;
}
}

@media (max-width: 1000px) {
.wrapper {
max-width: initial;
width: 100%;
}
}
</style>
Loading