1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
|
<script lang="ts">
import type { ItemAuctionsSchema, SimpleAuctionSchema } from './APITypes'
import type { PreviewedAuctionData } from './utils'
export let item: ItemAuctionsSchema
export let currentlyPreviewedAuction: PreviewedAuctionData | null
let svgEl: SVGElement
let maxCoins: number = item.auctions.reduce((max, auction) => Math.max(max, auction.coins), 0)
let currentTimestamp = Math.floor(Date.now() / 1000)
let earliestTimestamp = item.auctions.length > 0 ? item.auctions[0].ts : 0
let hoursBetween = (currentTimestamp - earliestTimestamp) / (60 * 60)
const gridWidth = 100 / hoursBetween
// this code is bad but it works
let heightCoinInterval = Math.ceil(Math.pow(10, Math.floor(Math.log10(maxCoins / 5))))
if (heightCoinInterval < maxCoins / 20) {
heightCoinInterval *= 5
} else if (heightCoinInterval < maxCoins / 10) {
heightCoinInterval *= 2
}
const gridHeight = 100 / (maxCoins / heightCoinInterval)
function getAuctionCoordinates(auction: SimpleAuctionSchema) {
const timestampPercentage =
(auction.ts - earliestTimestamp) / (currentTimestamp - earliestTimestamp)
return [timestampPercentage * 100, 100 - (auction.coins / maxCoins) * 100]
}
function updateNearest(e: MouseEvent) {
const rect = svgEl.getBoundingClientRect()
const mouseCoords = [e.clientX - rect.left, e.clientY - rect.top]
let nearestDistance = Number.MAX_SAFE_INTEGER
let nearestAuction: SimpleAuctionSchema | null = null
for (const auction of item.auctions) {
const auctionCoordsSvg = getAuctionCoordinates(auction)
const auctionCoords = [
(auctionCoordsSvg[0] * rect.width) / 100,
(auctionCoordsSvg[1] * rect.height) / 100,
]
const distance =
Math.pow(mouseCoords[0] - auctionCoords[0], 2) +
Math.pow(mouseCoords[1] - auctionCoords[1], 2)
if (distance < nearestDistance) {
nearestDistance = distance
nearestAuction = auction
}
}
if (nearestAuction) {
const [svgX, svgY] = getAuctionCoordinates(nearestAuction)
const [x, y] = [(svgX * rect.width) / 100, (svgY * rect.height) / 100]
currentlyPreviewedAuction = {
pageX: window.scrollX + rect.left + x,
pageY: window.scrollY + rect.top + y,
auction: nearestAuction,
}
} else currentlyPreviewedAuction = null
}
function shortenBigNumber(n: number) {
if (n < 1000) return n
if (n < 1_000_000) return parseFloat((n / 1000).toPrecision(2)).toLocaleString() + 'k'
if (n < 1_000_000_000) return parseFloat((n / 1_000_000).toPrecision(2)).toLocaleString() + 'M'
if (n < 1_000_000_000_000)
return parseFloat((n / 1_000_000_000).toPrecision(2)).toLocaleString() + 'B'
}
</script>
{#if item.auctions.length > 0}
<svg viewBox="0 0 100 100" class="item-auction-history">
<defs>
<pattern
id="grid-{item.id}"
width={gridWidth}
height={gridHeight}
patternUnits="userSpaceOnUse"
x="0%"
y="100%"
>
<path
d="M {gridWidth} {gridHeight} L 0 {gridHeight} 0 0"
fill="none"
stroke="#fff2"
stroke-width="1"
/>
</pattern>
</defs>
{#each new Array(Math.floor(maxCoins / heightCoinInterval) + 1) as _, intervalIndex}
<text
x="-1"
y={Math.min(Math.max(5, 100 - intervalIndex * gridHeight + 2), 100)}
fill="var(--theme-darker-text)"
font-size="6px"
text-anchor="end">{shortenBigNumber(heightCoinInterval * intervalIndex)}</text
>
{/each}
<rect
width="100%"
height="100%"
fill="url(#grid-{item.id})"
on:mousemove={updateNearest}
bind:this={svgEl}
/>
{#each item.auctions as auction}
{@const [x, y] = getAuctionCoordinates(auction)}
<circle
cx={x}
cy={y}
r="1"
stroke-width="4"
fill={auction.bin ? '#11b' : '#1b1'}
class:selected-auction={currentlyPreviewedAuction?.auction?.id === auction?.id}
/>
{/each}
</svg>
{/if}
<style>
.item-auction-history {
height: 10em;
width: 100%;
}
.selected-auction {
stroke: #06e7;
}
</style>
|