diff options
Diffstat (limited to 'src/lib')
-rw-r--r-- | src/lib/APITypes.d.ts | 96 | ||||
-rw-r--r-- | src/lib/Collapsible.svelte | 40 | ||||
-rw-r--r-- | src/lib/Emoji.svelte | 11 | ||||
-rw-r--r-- | src/lib/GlobalTooltip.svelte | 94 | ||||
-rw-r--r-- | src/lib/GlobalTooltip.ts | 76 | ||||
-rw-r--r-- | src/lib/minecraft/Inventory.svelte | 14 | ||||
-rw-r--r-- | src/lib/minecraft/Item.svelte | 21 | ||||
-rw-r--r-- | src/lib/minecraft/MinecraftTooltip.svelte | 18 | ||||
-rw-r--r-- | src/lib/minecraft/Username.svelte | 9 | ||||
-rw-r--r-- | src/lib/minecraft/heads/Head2d.svelte | 6 | ||||
-rw-r--r-- | src/lib/minecraft/heads/Head3d.svelte | 6 | ||||
-rw-r--r-- | src/lib/minecraft/inventory.ts | 5 | ||||
-rw-r--r-- | src/lib/profile.ts | 61 | ||||
-rw-r--r-- | src/lib/sections/Infobox.svelte | 36 | ||||
-rw-r--r-- | src/lib/sections/Inventories.svelte | 2 | ||||
-rw-r--r-- | src/lib/sections/Skills.svelte | 1 | ||||
-rw-r--r-- | src/lib/sections/StatList.svelte | 42 | ||||
-rw-r--r-- | src/lib/utils.ts | 8 |
18 files changed, 368 insertions, 178 deletions
diff --git a/src/lib/APITypes.d.ts b/src/lib/APITypes.d.ts new file mode 100644 index 0000000..04f9220 --- /dev/null +++ b/src/lib/APITypes.d.ts @@ -0,0 +1,96 @@ +export interface CleanMemberProfile { + member: CleanMemberProfilePlayer + profile: CleanFullProfileBasicMembers + customization?: AccountCustomization +} + +export interface CleanMemberProfilePlayer extends CleanPlayer { + profileName: string + first_join: number + last_save: number + bank?: Bank + purse?: number + stats?: StatItem[] + rawHypixelStats?: { + [key: string]: number + } + minions?: CleanMinion[] + fairy_souls?: FairySouls + inventories?: Inventories + objectives?: Objective[] + skills?: Skill[] + visited_zones?: Zone[] + collections?: Collection[] + slayers?: SlayerData +} + +export interface CleanBasicPlayer { + uuid: string + username: string +} + +export interface CleanPlayer extends CleanBasicPlayer { + rank: CleanRank + socials: CleanSocialMedia + profiles?: CleanBasicProfile[] +} + +export interface StatItem { + rawName: string + value: number + categorizedName: string + category: string | null + unit: string | null +} + +interface Item { + id: string + count: number + vanillaId: string + display: { + name: string + lore: string[] + glint: boolean + } + reforge?: string + anvil_uses?: number + timestamp?: string + enchantments?: { + [name: string]: number + } + head_texture?: string +} +export declare type Inventory = Item[] +export declare const INVENTORIES: { + armor: string + inventory: string + ender_chest: string + talisman_bag: string + potion_bag: string + fishing_bag: string + quiver: string + trick_or_treat_bag: string + wardrobe: string +} +export declare type Inventories = { + [name in keyof typeof INVENTORIES]: Item[] +} + + +export interface CleanUser { + player: CleanPlayer | null + profiles?: CleanProfile[] + activeProfile?: string + online?: boolean + customization?: AccountCustomization +} + +export interface CleanProfile extends CleanBasicProfile { + members?: CleanBasicMember[] +} + +/** A basic profile that only includes the profile uuid and name */ +export interface CleanBasicProfile { + uuid: string + name?: string +} diff --git a/src/lib/Collapsible.svelte b/src/lib/Collapsible.svelte new file mode 100644 index 0000000..f3a28d2 --- /dev/null +++ b/src/lib/Collapsible.svelte @@ -0,0 +1,40 @@ +<!-- + @component + + Non-JS collapsible content. + --> + +<details> + <summary> + <slot name="title"> + <h2>Details</h2> + </slot> + </summary> + <div> + <slot /> + </div> +</details> + +<style> + summary > :global(*) { + display: inline; + } + summary { + cursor: pointer; + } + summary::marker { + content: ''; + } + summary::before { + /* the background image is an arrow pointing down */ + background-image: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyMCIgaGVpZ2h0PSIyMCIgdmlld0JveD0iMCAwIDIwIDIwIj48cGF0aCBkPSJNMTcuNSA0Ljc1bC03LjUgNy41LTcuNS03LjVMMSA2LjI1bDkgOSA5LTl6IiBmaWxsPSIjYWFhIi8+IDwvc3ZnPg==); + width: 20px; + height: 20px; + display: inline-block; + margin-right: 1em; + content: ''; + } + details[open] summary::before { + transform: rotate(180deg); + } +</style> diff --git a/src/lib/Emoji.svelte b/src/lib/Emoji.svelte index 0e8f4a7..1869f37 100644 --- a/src/lib/Emoji.svelte +++ b/src/lib/Emoji.svelte @@ -1,17 +1,20 @@ <!-- @component - All the emojis inside this component will be turned into Twemojis. + All the emojis inside the value will be turned into Twemojis. --> <script lang="ts"> + // Interestingly, the comment above adds whitespace so we don't need to add + // padding before the emoji like we usually would. + // This is very likely a SvelteKit bug, so when it's fixed we should add + // `margin-left: .25em` to .profile-emoji + import { twemojiHtml } from './utils' export let value: string </script> -<span> - {@html twemojiHtml(value)} -</span> +<span>{@html twemojiHtml(value)}</span> <style> :global(.emoji) { diff --git a/src/lib/GlobalTooltip.svelte b/src/lib/GlobalTooltip.svelte index 2f7b738..f504607 100644 --- a/src/lib/GlobalTooltip.svelte +++ b/src/lib/GlobalTooltip.svelte @@ -1,94 +1,20 @@ <script lang="ts"> import { onMount } from 'svelte' + import { onMouseMove, registerItem, setTooltipEl } from './GlobalTooltip' - let tooltipEl + let tooltipEl: HTMLDivElement + $: setTooltipEl(tooltipEl) - // this script handles the item hover lore tooltip - onMount(() => { - // TODO: have something that automatically registers the event listener when we create a new MinecraftTooltip - const itemEls = document.getElementsByClassName('minecraft-tooltip') - let tooltipLocked = false - function moveTooltipToMouse(e) { - const mouseX = e.pageX - const mouseY = e.pageY - console.log(mouseY + tooltipEl.offsetHeight, window.innerHeight + window.scrollY - 10) - // if it's going to be off the bottom of the screen, move it up - if (mouseY + tooltipEl.offsetHeight > window.innerHeight + window.scrollY - 10) { - // put it at the bottom of the screen - tooltipEl.style.top = `${ - window.innerHeight + window.scrollY - 10 - tooltipEl.offsetHeight - }px` - } else { - // otherwise, put it at the mouse's y position - tooltipEl.style.top = mouseY + 'px' - } - // if it's going to be off the right of the screen, move it left - if (mouseX + tooltipEl.offsetWidth > window.innerWidth + window.scrollX - 10) { - // put it at the right of the screen - tooltipEl.style.left = `${ - window.innerWidth + window.scrollX - 10 - tooltipEl.offsetWidth - }px` - } else { - // otherwise, put it at the mouse's x position - tooltipEl.style.left = mouseX + 'px' - } - } - document.addEventListener('mousemove', e => { - if (!tooltipLocked && tooltipEl.style.display !== 'none') { - moveTooltipToMouse(e) - } - }) + // // this script handles the item hover lore tooltip + // onMount(() => { + // // TODO: have something that automatically registers the event listener when we create a new MinecraftTooltip + // const itemEls = document.getElementsByClassName('minecraft-tooltip') - for (const itemEl of itemEls as unknown as HTMLElement[]) { - // if (!(itemEl instanceof HTMLElement)) continue - - // if the item doesn't have lore or a name, that must mean it's air - // if (!itemEl.dataset.loreHtml && !itemEl.dataset.nameHtml) continue - - itemEl.addEventListener('mouseover', e => { - if (!tooltipLocked) { - moveTooltipToMouse(e) - // copy the lore and name from the tooltip-lore and - // tooltip-name elements inside the item el - const loreHtml = itemEl.getElementsByClassName('tooltip-lore')[0].innerHTML - const nameHtml = itemEl.getElementsByClassName('tooltip-name')[0].innerHTML - tooltipEl.innerHTML = `<p class="item-lore-name">${nameHtml}</p><p class="item-lore-text">${loreHtml}</p>` - } - tooltipEl.style.display = 'block' - }) - itemEl.addEventListener('mouseout', () => { - if (!tooltipLocked) { - tooltipEl.innerHTML = '' - tooltipEl.style.display = 'none' - } - }) - itemEl.addEventListener('click', e => { - tooltipLocked = !tooltipLocked - moveTooltipToMouse(e) - tooltipEl.style.display = 'block' - if (tooltipLocked) { - tooltipEl.style.userSelect = 'auto' - tooltipEl.style.pointerEvents = 'auto' - } else { - tooltipEl.style.userSelect = null - tooltipEl.style.pointerEvents = null - } - const loreHtml = itemEl.getElementsByClassName('tooltip-lore')[0].innerHTML - const nameHtml = itemEl.getElementsByClassName('tooltip-name')[0].innerHTML - tooltipEl.innerHTML = `<p class="item-lore-name">${nameHtml}</p><p class="item-lore-text">${loreHtml}</p>` - }) - document.addEventListener('mousedown', e => { - if (tooltipLocked && !tooltipEl.contains(e.target)) { - tooltipLocked = false - tooltipEl.style.userSelect = null - tooltipEl.style.pointerEvents = null - tooltipEl.style.display = 'none' - } - }) - } - }) + // for (const itemEl of itemEls as unknown as HTMLElement[]) registerItem(itemEl) + // }) </script> +<svelte:window on:mousemove={onMouseMove} /> <div id="global-tooltip" style="display: none" bind:this={tooltipEl} /> <style> diff --git a/src/lib/GlobalTooltip.ts b/src/lib/GlobalTooltip.ts new file mode 100644 index 0000000..d2c1020 --- /dev/null +++ b/src/lib/GlobalTooltip.ts @@ -0,0 +1,76 @@ +let tooltipEl: HTMLDivElement +let tooltipLocked = false + +export function setTooltipEl(el: HTMLDivElement) { + tooltipEl = el +} + +export function onMouseMove(e: MouseEvent) { + if (!tooltipLocked && tooltipEl.style.display !== 'none') { + moveTooltipToMouse(e) + } +} + +function moveTooltipToMouse(e: MouseEvent) { + const mouseX = e.pageX + const mouseY = e.pageY + // if it's going to be off the bottom of the screen, move it up + if (mouseY + tooltipEl.offsetHeight > window.innerHeight + window.scrollY - 10) { + // put it at the bottom of the screen + tooltipEl.style.top = `${window.innerHeight + window.scrollY - 10 - tooltipEl.offsetHeight}px` + } else { + // otherwise, put it at the mouse's y position + tooltipEl.style.top = mouseY + 'px' + } + // if it's going to be off the right of the screen, move it left + if (mouseX + tooltipEl.offsetWidth > window.innerWidth + window.scrollX - 10) { + // put it at the right of the screen + tooltipEl.style.left = `${window.innerWidth + window.scrollX - 10 - tooltipEl.offsetWidth}px` + } else { + // otherwise, put it at the mouse's x position + tooltipEl.style.left = mouseX + 'px' + } +} + +export function registerItem(itemEl: HTMLElement) { + itemEl.addEventListener('mouseover', e => { + if (!tooltipLocked) { + moveTooltipToMouse(e) + // copy the lore and name from the tooltip-lore and + // tooltip-name elements inside the item el + const loreHtml = itemEl.getElementsByClassName('tooltip-lore')[0].innerHTML + const nameHtml = itemEl.getElementsByClassName('tooltip-name')[0].innerHTML + tooltipEl.innerHTML = `<p class="item-lore-name">${nameHtml}</p><p class="item-lore-text">${loreHtml}</p>` + } + tooltipEl.style.display = 'block' + }) + itemEl.addEventListener('mouseout', () => { + if (!tooltipLocked) { + tooltipEl.innerHTML = '' + tooltipEl.style.display = 'none' + } + }) + itemEl.addEventListener('click', e => { + tooltipLocked = !tooltipLocked + moveTooltipToMouse(e) + tooltipEl.style.display = 'block' + if (tooltipLocked) { + tooltipEl.style.userSelect = 'auto' + tooltipEl.style.pointerEvents = 'auto' + } else { + tooltipEl.style.userSelect = '' + tooltipEl.style.pointerEvents = '' + } + const loreHtml = itemEl.getElementsByClassName('tooltip-lore')[0].innerHTML + const nameHtml = itemEl.getElementsByClassName('tooltip-name')[0].innerHTML + tooltipEl.innerHTML = `<p class="item-lore-name">${nameHtml}</p><p class="item-lore-text">${loreHtml}</p>` + }) + document.addEventListener('mousedown', e => { + if (tooltipLocked && !tooltipEl.contains(e.target as Node)) { + tooltipLocked = false + tooltipEl.style.userSelect = '' + tooltipEl.style.pointerEvents = '' + tooltipEl.style.display = 'none' + } + }) +} diff --git a/src/lib/minecraft/Inventory.svelte b/src/lib/minecraft/Inventory.svelte index d29b1e0..3068f67 100644 --- a/src/lib/minecraft/Inventory.svelte +++ b/src/lib/minecraft/Inventory.svelte @@ -1,22 +1,24 @@ <script lang="ts"> + import type { Inventory, Item as APIItem } from '$lib/APITypes' + import Item from './Item.svelte' - export let items + export let items: Inventory export let name = '' export let pack = '' export let groupLimit = 9 - if (name === 'inventory') - // in the inventory, the first 9 items are the hotbar and should be at the end - items = items.slice(9).concat(items.slice(0, 9)) - // each item group has 9 items - let itemGroups = [] + let itemGroups: APIItem[][] = [] $: { itemGroups = [] for (let i = 0; i < items.length; i += groupLimit) { itemGroups.push(items.slice(i, i + groupLimit)) } + if (name === 'inventory') { + // in the inventory, the first 9 items are the hotbar and should be at the end + itemGroups = itemGroups.slice(1).concat(itemGroups.slice(0, 1)) + } } </script> diff --git a/src/lib/minecraft/Item.svelte b/src/lib/minecraft/Item.svelte index c944f1b..9f2dcc3 100644 --- a/src/lib/minecraft/Item.svelte +++ b/src/lib/minecraft/Item.svelte @@ -17,12 +17,12 @@ $: imageUrl = item ? itemToUrl(item, pack) : null </script> -<MinecraftTooltip> - <span slot="name">{@html itemNameHtml}</span> - <span slot="lore">{@html itemLoreHtml}</span> - <span class="item" class:item-slot={isslot}> - <!-- we have an if here because the item might be air --> - {#if item} +{#if item} + <MinecraftTooltip> + <span slot="name">{@html itemNameHtml}</span> + <span slot="lore">{@html itemLoreHtml}</span> + <span class="item" class:item-slot={isslot}> + <!-- we have an if here because the item might be air --> {#if imageUrl} <img loading="lazy" @@ -34,9 +34,12 @@ {#if item.count !== 1} <span class="item-count">{item.count}</span> {/if} - {/if} - </span> -</MinecraftTooltip> + </span> + </MinecraftTooltip> +{:else} + <!-- don't do all that if the item doesn't actually exist --> + <span class="item" class:item-slot={isslot} /> +{/if} <style> .item { diff --git a/src/lib/minecraft/MinecraftTooltip.svelte b/src/lib/minecraft/MinecraftTooltip.svelte index 9c4e274..ae68f8b 100644 --- a/src/lib/minecraft/MinecraftTooltip.svelte +++ b/src/lib/minecraft/MinecraftTooltip.svelte @@ -3,15 +3,23 @@ A tooltip that looks like when you hover over a Minecraft item in an inventory. This requires JavaScript. --> +<script lang="ts"> + import { registerItem } from '$lib/GlobalTooltip' + import { onMount } from 'svelte' -<span class="minecraft-tooltip"> + let el + + onMount(() => { + registerItem(el) + }) +</script> + +<span class="minecraft-tooltip" bind:this={el}> <span class="tooltip-name"> <slot name="name" /> - </span> - <span class="tooltip-lore"> + </span><span class="tooltip-lore"> <slot name="lore" /> - </span> - <slot /> + </span><slot /> </span> <style> diff --git a/src/lib/minecraft/Username.svelte b/src/lib/minecraft/Username.svelte index 555a226..701e50c 100644 --- a/src/lib/minecraft/Username.svelte +++ b/src/lib/minecraft/Username.svelte @@ -22,16 +22,13 @@ <ConditionalLink href="/player/{player.username}" isWrapped={hyperlinkToProfile}> {#if headType == '3d'} - <Head3d {player} isPartOfUsername={true} /> - {:else if headType == '2d'} + <Head3d {player} isPartOfUsername={true} />{:else if headType == '2d'} <Head2d {player} isPartOfUsername={true} /> - {/if} - {#if prefix} + {/if}{#if prefix} <span class="username-rank-prefix"> {@html formattingCodeToHtml(player.rank.colored)} </span> - {/if} - <span class="username" style="color: {player.rank.color}">{player.username}</span> + {/if}<span class="username" style="color: {player.rank.color}">{player.username}</span> </ConditionalLink> <style> diff --git a/src/lib/minecraft/heads/Head2d.svelte b/src/lib/minecraft/heads/Head2d.svelte index d4e9ca8..9d71551 100644 --- a/src/lib/minecraft/heads/Head2d.svelte +++ b/src/lib/minecraft/heads/Head2d.svelte @@ -6,7 +6,7 @@ <img loading="lazy" class="head head2d" - class:userHead={isPartOfUsername} + class:player-head={isPartOfUsername} src="https://crafatar.com/avatars/{player.uuid}?size=8&overlay" alt="{player.username}'s face" /> @@ -27,4 +27,8 @@ height: 1em; width: 1em; } + + .player-head { + margin-right: 0.2em; + } </style> diff --git a/src/lib/minecraft/heads/Head3d.svelte b/src/lib/minecraft/heads/Head3d.svelte index f8d2657..2400f4b 100644 --- a/src/lib/minecraft/heads/Head3d.svelte +++ b/src/lib/minecraft/heads/Head3d.svelte @@ -6,7 +6,7 @@ <img loading="lazy" class="head head3d" - class:userHead={isPartOfUsername} + class:player-head={isPartOfUsername} src="https://www.mc-heads.net/head/{player.uuid}/128" alt="{player.username}'s head" /> @@ -24,4 +24,8 @@ height: 1em; width: 1em; } + + .player-head { + margin-right: 0.2em; + } </style> diff --git a/src/lib/minecraft/inventory.ts b/src/lib/minecraft/inventory.ts index cb926b4..faaea85 100644 --- a/src/lib/minecraft/inventory.ts +++ b/src/lib/minecraft/inventory.ts @@ -1,6 +1,7 @@ import * as skyblockAssets from 'skyblock-assets' import vanilla from 'skyblock-assets/matchers/vanilla.json' import packshq from 'skyblock-assets/matchers/vanilla.json' +import furfsky_reborn from 'skyblock-assets/matchers/furfsky_reborn.json' interface Item { @@ -52,10 +53,8 @@ export function itemToUrl(item: Item, packName?: string): string { textureUrl = skyblockAssets.getTextureUrl({ id: item.vanillaId, nbt: itemNbt, - packs: [packshq, vanilla] + packs: [furfsky_reborn, vanilla] }) - if (!textureUrl) - console.log('no texture', item) return textureUrl } diff --git a/src/lib/profile.ts b/src/lib/profile.ts index 320a5dc..c2c945e 100644 --- a/src/lib/profile.ts +++ b/src/lib/profile.ts @@ -1,3 +1,4 @@ +import type { CleanMemberProfile, StatItem } from './APITypes' import { cleanId, millisecondsToTime } from './utils' /** @@ -10,7 +11,7 @@ export function prettyTimestamp(ms: number) { return timeAsString } -export function generateInfobox(data, opts: { meta: boolean }): string[] { +export function generateInfobox(data: CleanMemberProfile): string[] { const result: string[] = [] result.push(`๐พ Last save: ${prettyTimestamp(data.member.last_save * 1000)}`) @@ -22,35 +23,37 @@ export function generateInfobox(data, opts: { meta: boolean }): string[] { if (data.profile.minion_count >= data.profile.maxUniqueMinions) result.push(`๐ค Minion count: ${data.profile.minion_count}`) - let mostSignificantKillsStat = null - let mostSignificantDeathsStat = null - - for (const stat of data.member.stats) { - if ( - stat.category === 'kills' - && stat.rawName != 'kills' - && stat.value >= 200_000 - && stat.value > (mostSignificantKillsStat?.value ?? 0) - ) - mostSignificantKillsStat = stat - if ( - stat.category === 'deaths' - && stat.rawName != 'deaths' - && stat.value > 1_000_000 - && stat.value > (mostSignificantDeathsStat?.value ?? 0) - ) - mostSignificantDeathsStat = stat + if (data.member.stats) { + let mostSignificantKillsStat: StatItem | null = null + let mostSignificantDeathsStat: StatItem | null = null + + for (const stat of data.member.stats) { + if ( + stat.category === 'kills' + && stat.rawName != 'kills' + && stat.value >= 200_000 + && stat.value > (mostSignificantKillsStat?.value ?? 0) + ) + mostSignificantKillsStat = stat + if ( + stat.category === 'deaths' + && stat.rawName != 'deaths' + && stat.value > 1_000_000 + && stat.value > (mostSignificantDeathsStat?.value ?? 0) + ) + mostSignificantDeathsStat = stat + } + + if (mostSignificantKillsStat) + result.push( + `โ๏ธ ${mostSignificantKillsStat.value.toLocaleString()} ${mostSignificantKillsStat.unit || cleanId(mostSignificantKillsStat.rawName).toLowerCase()}` + ) + + if (mostSignificantDeathsStat) + result.push( + `โ ${mostSignificantDeathsStat.value.toLocaleString()} ${mostSignificantDeathsStat.unit || cleanId(mostSignificantDeathsStat.rawName).toLowerCase()}` + ) } - if (mostSignificantKillsStat) - result.push( - `โ๏ธ ${mostSignificantKillsStat.value.toLocaleString()} ${mostSignificantKillsStat.unit || cleanId(mostSignificantKillsStat.rawName).toLowerCase()}` - ) - - if (mostSignificantDeathsStat) - result.push( - `โ ${mostSignificantDeathsStat.value.toLocaleString()} ${mostSignificantDeathsStat.unit || cleanId(mostSignificantDeathsStat.rawName).toLowerCase()}` - ) - return result }
\ No newline at end of file diff --git a/src/lib/sections/Infobox.svelte b/src/lib/sections/Infobox.svelte index 756987d..7670dec 100644 --- a/src/lib/sections/Infobox.svelte +++ b/src/lib/sections/Infobox.svelte @@ -2,37 +2,23 @@ import { generateInfobox } from '$lib/profile' import Username from '$lib/minecraft/Username.svelte' import Emoji from '$lib/Emoji.svelte' + import { onMount } from 'svelte' export let data -</script> -<!-- <div id="infobox"> - <h2>{{ render.username(data.member, prefix=true) }} ({{ data.member.profileName }})</h2> - <p>{{ '๐พ'|twemojiHtml|safe }} Last save: {% if getTime() - data.member.last_save < 60 * 60 * 24 * 7 %}{{ ((getTime() - data.member.last_save) * 1000)|cleannumber('time') }} ago {% else %}{{ data.member.last_save|cleannumber('date') }}{% endif %}</p> - <p>{{ '๐ถ'|twemojiHtml|safe }} Profile created: {% if getTime() - data.member.first_join < 60 * 60 * 24 * 7 %}{{ ((getTime() - data.member.first_join) * 1000)|cleannumber('time') }} ago {% else %}{{ data.member.first_join|cleannumber('date') }}{% endif %}</p> - <p>{{ 'โจ'|twemojiHtml|safe }} Fairy souls: {{ data.member.fairy_souls.total }}/{{ getConstants().max_fairy_souls }}</p> -{%- if data.profile.minion_count == getConstants().max_minions -%}<p>{{ '๐ค'|twemojiHtml|safe }} Minion count: {{ data.profile.minion_count }}</p>{% endif %} -{%- set mostSignificantKillsStat = {} -%} -{%- set mostSignificantDeathsStat = {} -%} -{%- for stat in data.member.stats -%} -{%- if stat.category == 'kills' and stat.rawName != 'kills' and stat.value >= 200000 and stat.value > (mostSignificantKillsStat.value or 0) -%} -{%- set mostSignificantKillsStat = stat -%} -{%- endif -%} -{%- if stat.category == 'deaths' and stat.rawName != 'deaths' and stat.value >= 1000000 and stat.value > (mostSignificantDeathsStat.value or 0) -%} -{%- set mostSignificantDeathsStat = stat -%} -{%- endif -%} -{%- endfor -%} -{%- if mostSignificantKillsStat.value -%} - <p>{{ 'โ๏ธ'|twemojiHtml|safe }} {{ mostSignificantKillsStat.value|cleannumber(mostSignificantKillsStat.unit or mostSignificantKillsStat.rawName|clean|lower) }}</p> -{%- endif -%} -{%- if mostSignificantDeathsStat.value -%} - <p>{{ 'โ '|twemojiHtml|safe }} {{ mostSignificantDeathsStat.value|cleannumber(mostSignificantDeathsStat.unit or mostSignificantDeathsStat.rawName|clean|lower) }}</p> -{%- endif -%} -</div> --> + // onMount(() => { + // // reload the data every second so the infobox updates + // const interval = setInterval(() => { + // data = data + // }, 1000) + + // return () => clearInterval(interval) + // }) +</script> <div id="infobox"> <h2><Username player={data.member} prefix /> ({data.member.profileName})</h2> - {#each generateInfobox(data, { meta: false }) as item} + {#each generateInfobox(data) as item} <p><Emoji value={item} /></p> {/each} </div> diff --git a/src/lib/sections/Inventories.svelte b/src/lib/sections/Inventories.svelte index 49a00c2..42607b6 100644 --- a/src/lib/sections/Inventories.svelte +++ b/src/lib/sections/Inventories.svelte @@ -27,7 +27,7 @@ {#each displayingInventories as inventoryName} {#if inventoryName === selectedInventoryName} <div id={inventoryName} class="inventory-content"> - <Inventory items={data.member.inventories[inventoryName]} {pack} /> + <Inventory items={data.member.inventories[inventoryName]} {pack} name={inventoryName} /> </div> {/if} {/each} diff --git a/src/lib/sections/Skills.svelte b/src/lib/sections/Skills.svelte index 7111c43..6e1efdb 100644 --- a/src/lib/sections/Skills.svelte +++ b/src/lib/sections/Skills.svelte @@ -70,5 +70,6 @@ } ul > li { width: 10em; + margin: 0.25em 0.25em 0 0; } </style> diff --git a/src/lib/sections/StatList.svelte b/src/lib/sections/StatList.svelte new file mode 100644 index 0000000..266ceb3 --- /dev/null +++ b/src/lib/sections/StatList.svelte @@ -0,0 +1,42 @@ +<!-- + @component + + A sorted list of a user's stats, with the total sometimes being at the top. +--> +<script lang="ts"> + import { cleanId, millisecondsToTime } from '$lib/utils' + import type { StatItem } from '$lib/APITypes' + + export let stats: StatItem[] +</script> + +<ul> + {#each stats as stat} + <li class:total-stat={stat.categorizedName === 'total'}> + <span class="stat-name">{cleanId(stat.categorizedName)}</span>: + {#if stat.unit === 'time'} + {millisecondsToTime(stat.value)} + {:else} + {stat.value.toLocaleString()} + {/if} + </li> + {/each} +</ul> + +<style> + .total-stat .stat-name { + font-weight: bold; + } + + .total-stat { + font-size: 1.2em; + list-style-type: none; + position: relative; + right: 1em; + bottom: 0.2em; + } + + ul { + margin-top: 0.5em; + } +</style> diff --git a/src/lib/utils.ts b/src/lib/utils.ts index f16021d..197bf6b 100644 --- a/src/lib/utils.ts +++ b/src/lib/utils.ts @@ -26,7 +26,7 @@ const colorCodeCharacter = 'ยง' export function formattingCodeToHtml(formatted: string): string { let htmlOutput = '' // we store the hex code, not the formatting code - let currentColor = null + let currentColor: null | string = null // we store the css code, not the formatting code const activeSpecialCodes: string[] = [] function reset() { @@ -50,7 +50,7 @@ export function formattingCodeToHtml(formatted: string): string { // if there's already a color, close that tag if (currentColor) htmlOutput += '</span>' currentColor = colorCodes[colorCharacter] - htmlOutput += `<span style="color: ${currentColor}">` + htmlOutput += `<span style="color:${currentColor}">` } } else if (specialCodes[colorCharacter]) { if (!activeSpecialCodes.includes(specialCodes[colorCharacter])) { @@ -133,7 +133,7 @@ export function twemojiHtml(s: string) { const htmlEncoded = s.replace('<', '<').replace('>', '>').replace('&', '&') // replace unicode emojis with <img src="/emoji/[hex].svg"> const asTwemoji = htmlEncoded.replace(emojiRegex, (match) => { - return `<img src="/emoji/${[...match].map(p => p.codePointAt(0).toString(16)).join('-')}.svg" class="emoji">` + return `<img src="/emoji/${[...match].map(p => p.codePointAt(0)!.toString(16)).join('-')}.svg" class="emoji">` }) return asTwemoji } @@ -150,5 +150,5 @@ export function formatNumber(n: number, digits = 3) { { value: 1e18, symbol: 'E' }, ] const item = numberSymbolsLookup.slice().reverse().find(item => n >= item.value) - return (n / item.value).toPrecision(digits).replace(/\.0+$|(\.[0-9]*[1-9])0+$/, '$1') + item.symbol + return (n / (item?.value ?? 1)).toPrecision(digits).replace(/\.0+$|(\.[0-9]*[1-9])0+$/, '$1') + (item?.symbol ?? '') }
\ No newline at end of file |