Skip to content

Commit

Permalink
add individual element detail pages (closes #1)
Browse files Browse the repository at this point in the history
  • Loading branch information
janosh committed Jun 6, 2022
1 parent 0bb686b commit 4142c4d
Show file tree
Hide file tree
Showing 14 changed files with 234 additions and 86 deletions.
47 changes: 38 additions & 9 deletions src/labels.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,41 @@
import { Element } from './types'

export const heatmap_labels: Record<string, keyof Element> = {
'Atomic Mass (u)': `atomic_mass`,
'Atomic Radius (Å)': `atomic_radius`,
'Covalent Radius (Å)': `covalent_radius`,
Electronegativity: `electronegativity`,
'Density (solid: g/cm³, gas: g/liter)': `density`,
'Boiling Point (K)': `boiling_point`,
'Melting Point (K)': `melting_point`,
'First Ionization Energy (eV)': `first_ionization`,
// TODO add labels and units for all elemental properties
export const element_property_labels: Partial<
Record<keyof Element, [string, string | null]>
> = {
number: [`Atomic Number`, null],
atomic_mass: [`Atomic Mass`, `u`],
melting_point: [`Melting Point`, `K`],
boiling_point: [`Boiling Point`, `K`],
density: [`Density`, `solid: g/cm³, gas: g/liter`],
atomic_radius: [`Atomic Radius`, `Å`],
covalent_radius: [`Covalent Radius`, `Å`],
electronegativity: [`Electronegativity`, null],
first_ionization: [`First Ionization Energy`, `eV`],
electron_affinity: [`Electron Affinity`, null],
n_shells: [`Number of Shells`, null],
n_valence: [`Electron Valency`, null],
shells: [`Electron Shell Occupations`, null],
specific_heat: [`Specific Heat`, `J/g`], // TODO check correct unit
}

const heatmap_keys: (keyof Element)[] = [
`atomic_mass`,
`atomic_radius`,
`covalent_radius`,
`electronegativity`,
`density`,
`boiling_point`,
`melting_point`,
`first_ionization`,
]

export const heatmap_labels: Partial<Record<string, keyof Element>> =
Object.fromEntries(
heatmap_keys.map((key) => {
const [label, unit] = element_property_labels[key] ?? []
if (!label) throw `Unexpected missing label ${label}`
return [label + (unit ? ` (${unit})` : ``), key]
})
)
5 changes: 3 additions & 2 deletions src/lib/ChemicalElement.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
$: category = element.category.replaceAll(` `, `-`)
$: color = $heatmap ? (value ? $color_scale?.(value) : `transparent`) : undefined
$: bg_color = value ? $color_scale(value) : `transparent`
</script>

<a
Expand All @@ -21,7 +21,8 @@
$active_element?.name === element.name}
{style}
on:mouseenter
style:background-color={color}
on:mouseleave
style:background-color={$heatmap ? bg_color : null}
>
{#if show_number}
<span class="atomic-number">
Expand Down
15 changes: 8 additions & 7 deletions src/lib/ElementPhoto.svelte
Original file line number Diff line number Diff line change
@@ -1,21 +1,22 @@
<script lang="ts">
import { active_element as element } from '../stores'
import { active_element } from '../stores'
import { Element } from '../types'
export let show_photo = true
export let style = ``
// element defaults to active_element store but can be pinned by passing it as prop
export let element: Element | null = $active_element
$: src = `https://images-of-elements.com/s/${$element?.name?.toLowerCase()}.jpg`
$: src = `https://images-of-elements.com/s/${element?.name?.toLowerCase()}.jpg`
</script>

{#if show_photo && $element}
{#if element}
{#key src}
<img {src} alt={$element?.name} onerror="this.style.display='none'" />
<img {src} alt={element?.name} onerror="this.style.display='none'" {style} />
{/key}
{/if}

<style>
img {
grid-column: 1 / span 2;
grid-row: 9 / span 2;
width: 100%;
height: 100%;
object-fit: cover;
Expand Down
72 changes: 36 additions & 36 deletions src/lib/ElementStats.svelte
Original file line number Diff line number Diff line change
@@ -1,48 +1,48 @@
<script lang="ts">
import { active_element as element } from '../stores'
import { last_element } from '../stores'
export let padding = `1vw 3vw`
export let precision = 2
$: element = $last_element
</script>

{#if $element}
<h2 style:padding>
{$element?.number} - {$element?.name} <small>{$element?.category}</small>
</h2>
<div style:padding>
<section>
<p>
Atomic Mass
<abbr title="Dalton aka atomic mass unit">(u)</abbr>
</p>
<strong>{parseFloat($element?.atomic_mass?.toFixed(precision))}</strong>
</section>
<section>
<p>
Density
<abbr title="grams per cubic centimeter">(g/cm&sup3;)</abbr>
</p>
<strong>{parseFloat($element?.density?.toFixed(precision))}</strong>
</section>
<section>
<p>Phase</p>
<strong>{$element?.phase}</strong>
</section>
<section>
<p>Year of Discovery</p>
<strong>
{$element?.year}
</strong>
</section>
{#if element}
<div style:padding style="overflow: hidden;">
<h2>
{element.number} - {element.name} <small>{element.category}</small>
</h2>
<div style="display: flex;">
<section>
<p>
Atomic Mass
<abbr title="Dalton aka atomic mass unit">(u)</abbr>
</p>
<strong>{parseFloat(element.atomic_mass?.toFixed(precision))}</strong>
</section>
<section>
<p>
Density
<abbr title="grams per cubic centimeter">(g/cm&sup3;)</abbr>
</p>
<strong>{parseFloat(element.density?.toFixed(precision))}</strong>
</section>
<section>
<p>Phase</p>
<strong>{element.phase}</strong>
</section>
<section>
<p>Year of Discovery</p>
<strong>
{element.year}
</strong>
</section>
</div>
</div>
{:else}
<h3 style="text-align: center;">Try hovering an element!</h3>
{/if}

<style>
div {
display: flex;
}
div > section {
width: 25%;
}
Expand All @@ -55,21 +55,21 @@
font-size: clamp(6pt, 1.2vw, 15pt);
}
h2 {
margin: 0 0 2vw;
font-size: clamp(9pt, 3vw, 25pt);
white-space: nowrap;
}
h2 small {
margin-left: 8pt;
font-weight: 100;
opacity: 0.7;
}
abbr {
font-size: 0.9vw;
text-decoration: none;
}
h3 {
margin: 0 0 2vw;
font-size: clamp(9pt, 3vw, 25pt);
font-size: clamp(9pt, 3vw, 20pt);
white-space: nowrap;
align-self: center;
}
</style>
3 changes: 2 additions & 1 deletion src/lib/PeriodicTable.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
<div class="periodic-table">
<TableInset>
{#if $heatmap}
<ScatterPlot />
<ScatterPlot on:hover={(e) => ($active_element = e.detail.element)} />
{:else}
<ElementStats />
{/if}
Expand All @@ -24,6 +24,7 @@
<ChemicalElement
{element}
on:mouseenter={() => ($active_element = element)}
on:mouseleave={() => ($active_element = null)}
show_name={show_names}
style="grid-column: {element.column}; grid-row: {element.row};"
value={element[$heatmap]}
Expand Down
12 changes: 8 additions & 4 deletions src/lib/ScatterPlot.svelte
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
<script lang="ts">
import { extent } from 'd3-array'
import { scaleLinear } from 'd3-scale'
import { createEventDispatcher } from 'svelte'
import elements from '../periodic-table-data.ts'
import { active_element, color_scale, heatmap } from '../stores'
import { Element } from '../types'
Expand All @@ -9,6 +10,10 @@
export let style = ``
const dispatch = createEventDispatcher<{ hover: { element: Element } }>()
type $$Events = { hover: CustomEvent<{ element: Element }> }
let data: [number, number, Element][]
$: data = elements.map((el) => [el.number, el[$heatmap], el])
const padding = 10 // pixels
Expand All @@ -26,7 +31,7 @@
.domain(yrange)
.range([height - padding, padding])
let scaled_data: [number, number, string, Element][]
let scaled_data: [number, number, Element][]
$: scaled_data = data
.filter(([x, y]) => !(isNaN(x) || isNaN(y) || x === null || y === null))
.map(([x, y, ...rest]) => [x_scale(x), y_scale(y), ...rest])
Expand All @@ -37,14 +42,13 @@
<svg>
<Line data={scaled_data} />
{#each scaled_data as [x, y, element]}
{@const fill = $color_scale(y)}
{@const active = $active_element?.name === element.name}
<Datapoint
{x}
{y}
{fill}
on:mouseenter={() => ($active_element = element)}
fill={$color_scale?.(y)}
{active}
on:mouseenter={() => dispatch(`hover`, { element })}
/>
{/each}
</svg>
Expand Down
2 changes: 1 addition & 1 deletion src/lib/ScatterPoint.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
export let fill = `gray`
export let active = false
const tX = tweened(0, { duration: 400, easing: cubicOut, local: true })
const tX = tweened(0, { duration: 400, easing: cubicOut })
const tY = tweened(0, { duration: 400, easing: cubicOut })
$: tX.set(x)
Expand Down
3 changes: 1 addition & 2 deletions src/lib/TableInset.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,7 @@

<style>
aside {
width: 100%;
height: 100%;
display: grid;
box-sizing: border-box;
}
</style>
16 changes: 8 additions & 8 deletions src/periodic-table-data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4957,7 +4957,7 @@ export default [
name: `Meitnerium`,
appearance: null,
atomic_mass: 278,
category: `probably transition metal`,
category: `transition metal`,
density: 37.4,
discoverer: `Gesellschaft für Schwerionenforschung`,
molar_heat: null,
Expand Down Expand Up @@ -5002,7 +5002,7 @@ export default [
name: `Darmstadtium`,
appearance: null,
atomic_mass: 281,
category: `probably transition metal`,
category: `transition metal`,
density: 34.8,
discoverer: `Gesellschaft für Schwerionenforschung`,
molar_heat: null,
Expand Down Expand Up @@ -5047,7 +5047,7 @@ export default [
name: `Roentgenium`,
appearance: null,
atomic_mass: 282,
category: `probably transition metal`,
category: `transition metal`,
density: 28.7,
discoverer: `Gesellschaft für Schwerionenforschung`,
molar_heat: null,
Expand Down Expand Up @@ -5137,7 +5137,7 @@ export default [
name: `Nihonium`,
appearance: null,
atomic_mass: 286,
category: `probably transition metal`,
category: `post transition metal`,
density: 16,
discoverer: `RIKEN`,
molar_heat: null,
Expand Down Expand Up @@ -5227,7 +5227,7 @@ export default [
name: `Moscovium`,
appearance: null,
atomic_mass: 289,
category: `probably post-transition metal`,
category: `post-transition metal`,
density: 13.5,
discoverer: `Joint Institute for Nuclear Research`,
molar_heat: null,
Expand Down Expand Up @@ -5272,7 +5272,7 @@ export default [
name: `Livermorium`,
appearance: null,
atomic_mass: 293,
category: `probably post-transition metal`,
category: `post-transition metal`,
density: 12.9,
discoverer: `Joint Institute for Nuclear Research`,
molar_heat: null,
Expand Down Expand Up @@ -5317,7 +5317,7 @@ export default [
name: `Tennessine`,
appearance: null,
atomic_mass: 294,
category: `probably metalloid`,
category: `metalloid`,
density: 7.17,
discoverer: `Joint Institute for Nuclear Research`,
molar_heat: null,
Expand Down Expand Up @@ -5362,7 +5362,7 @@ export default [
name: `Oganesson`,
appearance: null,
atomic_mass: 294,
category: `probably noble gas`,
category: `noble gas`,
density: 4.95,
discoverer: `Joint Institute for Nuclear Research`,
molar_heat: null,
Expand Down
Loading

0 comments on commit 4142c4d

Please sign in to comment.