@webwriter/chemdraw
Version:
Periodic table of the elements. Each element can be focused on to get a brief overview of its properties.
133 lines (118 loc) • 4.91 kB
text/typescript
import { LitElementWw } from '@webwriter/lit';
import { LitElement, PropertyValueMap, TemplateResult, html } from 'lit';
import { customElement, property } from 'lit/decorators.js';
import periodicTable from '../periodicTable.json';
import { PeriodicTableStyles } from './PeriodicTableStyles';
export interface Element {
name: string;
appearance: null | string;
atomic_mass: number;
boil: number | null;
category: string;
density: number | null;
discovered_by: null | string;
melt: number | null;
molar_heat: number | null;
named_by: null | string;
number: number;
period: number;
group: number;
phase: Phase;
source: string;
bohr_model_image: null | string;
bohr_model_3d: null | string;
spectral_img: null | string;
summary: string;
symbol: string;
xpos: number;
ypos: number;
wxpos: number;
wypos: number;
shells: number[];
electron_configuration: string;
electron_configuration_semantic: string;
electron_affinity: number | null;
electronegativity_pauling: number | null;
ionization_energies: number[];
'cpk-hex': null | string;
image: Image;
block: Block;
}
export enum Block {
D = 'd',
F = 'f',
P = 'p',
S = 's',
}
export interface Image {
title: string;
url: string;
attribution: string;
}
export enum Phase {
Gas = 'Gas',
Liquid = 'Liquid',
Solid = 'Solid',
}
('webwriter-periodic-table')
export class PeriodicTable extends LitElementWw {
static styles = PeriodicTableStyles;
static shadowRootOptions = { ...LitElement.shadowRootOptions, delegatesFocus: true };
({ type: Object, attribute: true, reflect: true })
private accessor selectedElement: Element = periodicTable.elements[1] as Element;
focus() {
console.log('focus');
this.dispatchEvent(new CustomEvent('focus'));
}
render() {
return html`
<div class="periodic-table">
<div class="selected-element" style="grid-column: 5/8; grid-row: 1/4;">
<div class="element-number">${this.selectedElement.number}</div>
<div class="element-symbol">${this.selectedElement.symbol}</div>
<div class="element-name">${this.selectedElement.name}</div>
<div class="element-mass">${this.selectedElement.atomic_mass.toFixed(3)}</div>
<div class="element-electron-configuration">
${this.formatElectronConfiguration(this.selectedElement.electron_configuration_semantic)}
</div>
<div class="element-first-ionization-energy">${this.selectedElement.ionization_energies[0]}</div>
<div class="element-electronegativity">${this.selectedElement.electronegativity_pauling}</div>
</div>
${Object.entries(periodicTable.elements as Element[]).map(([key, value]) => {
return html`
<div
class=${`element ${value.category}`}
style="grid-column: ${value.xpos}; grid-row: ${value.ypos};"
@click=${() => {
const e = new CustomEvent('element-click', {
detail: { element: value },
bubbles: true,
composed: true,
});
this.dispatchEvent(e);
this.selectedElement = value;
this.focus();
}}
>
<div class="element-number">${value.number}</div>
<div class="element-symbol">${value.symbol}</div>
<!-- <div class="hover-only">
<div class="element-name">${value.name}</div>
<div class="element-mass">${value.atomic_mass}</div>
</div> -->
</div>
`;
})}
</div>
`;
}
private formatElectronConfiguration(configuration: string): TemplateResult {
const parts = configuration.split(' ');
const brackets = ['[', '*'].includes(parts[0][0]) ? parts[0] : null;
const config = parts.slice(brackets ? 1 : 0).map((part) => {
const subparts = part.split(new RegExp('([A-Za-z]+)'));
return html` ${subparts[0]}${subparts[1]}<sup>${subparts[2]}</sup> `;
});
return html` ${brackets} ${config}`;
}
}