Familiewaaier

by PhilQ

HTML

<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.7.2/css/all.min.css">
<div id="familiewaaier"></div>

SCSS

*, :before, :after {
	margin: 0;
	padding: 0;
	box-sizing: border-box;
}

html, body {
	width: 100%;
	height: 100%;
}

body {
  background: #20262E;
  padding: 20px;
  font-family: Helvetica, sans-serif;
}

@import url('https://fonts.googleapis.com/css2?family=Jost:ital,wght@0,100..900;1,100..900&display=swap');

:root {
	--e-global-color-primary: #782A24;
	--e-global-color-secondary: #B89349;
	--e-global-color-text: #615956;
	--e-global-color-accent: #EBB54F;
	--e-global-color-69c5e9e: #EBB54F;
	--e-global-color-b2b2f2c: #674E22;
	--e-global-color-19f3416: #E34439;
	--e-global-color-3504911: #331A0F;
	--e-global-color-63f8136: #F2D987;
	--e-global-color-0b40fbb: #F5EFD0;
	--e-global-color-8a491fc: #9F987A;
	--e-global-color-9eb4bcd: #615956;
	--e-global-color-10c6832: #222222;
	--e-global-color-94148c1: #FFFFFF;
	--e-global-typography-primary-font-family: "Eczar";
	--e-global-typography-primary-font-weight: 600;
	--e-global-typography-secondary-font-family: "Eczar";
	--e-global-typography-secondary-font-weight: 400;
	--e-global-typography-text-font-family: "Jost";
	--e-global-typography-text-font-weight: 400;
	--e-global-typography-accent-font-family: "Jost";
	--e-global-typography-accent-font-weight: 500;
}

#familiewaaier {
	position: relative;
	display: flex;
	justify-content: stretch;
	align-items: stretch;
  background: #fff;
  border-radius: 4px;
  margin: 0 auto;
	width: fit-content;
	height: fit-content;
	min-width: 70%;
  max-width: 80vw;
	max-height: 90vh;
	overflow: clip;
	
	&:fullscreen {
		width: 100vw;
		height: 100vh;
	}
	
	.inner {
		position: relative;
		overflow: auto;
		min-width: 100%;
		min-height: 100%;

		svg {
			z-index: 1;
		}
	}
	
	.controls {
		position: absolute;
		top: 1em;
		right: 1em;
		z-index: 2;
		display: flex;
		gap: 1em;
		
		button {
			display: flex;
			justify-content: center;
			align-items: center;
			font-family: var(--e-global-typography-accent-font-family), Sans-serif;
			font-weight:...

JavaScript

class VHW_FamilyTree {
  // Data and objects
  // NOTE: ordered by date of birth
  // {
  //   id,
  //   name,
  //   parent_id,
  //   *gen,
  //   *offspring count,
  //   *parent offspring count offset,
  // }
  // * = will be set by script
  persons = [];
  items = [];
  gens = [[]];
  child_of_classes = {};
  parent_of_classes = {};
  max_gen_width = 0;
  wrapper = null;
  svg = null;
  w = 200;
  h = 200;
  x = 0;
  y = 0;
  cx = 0;
  cy = 0;
  zoom = 1.0;

  // Settings
  r_per_gen = 100;
  margin = 50;
  rotate = -90;

  constructor(selector, persons) {
    this.wrapper = document.querySelector(selector);
    if (!this.wrapper || this.wrapper.classList.contains('loaded')) {
      return;
    }

    // Normalize rotation to positive multitude of 90
    this.rotate = Math.round(
      (
        (
          (this.rotate % 360) + 360
        ) % 360
      ) / 90
    ) * 90;

    this.persons = persons;
    this.prepareItems();
    this.prepareAncestorClasses();

    if (this.items.length) {
      const inner = document.createElement('div');
      inner.classList.add('inner');
  
      this.max_gen_width = this.items[0][4];
      this.svg = this.createTag('svg');
      this.setSize();
      // this.updateViewbox();
      inner.appendChild(this.svg);
      
      this.insertItems(this.svg, this.items[0]);
      
      this.addControls();
      
      this.wrapper.appendChild(inner);
      this.wrapper.classList.add('loaded');
      // window.addEventListener('resize', () => this.updateViewbox() );
    }
  }

  prepareItems() {
    // Group names by parent
    let ids = new Set([0]);
    let parent_of = {};
    let grouped_by_parent = {};
    for (const person of this.persons) {
      person[3] = 0; // gen
      person[4] = 0; // offspring count
      person[5] = 0; // parent offspring count offset
  
      let id = `${person[0]}`;
      let parent_id = `${person[2]}`;
      parent_of[id] = parent_id;
      ids.add(id);
      grouped_by_parent[parent_id] =...