Homer timeline

by PhilQ

HTML

<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/css/bootstrap.min.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.13.1/css/all.min.css">
<!-- <link href="https://fonts.googleapis.com/css2?family=Nunito+Sans:wght@300;400;700;900&display=swap" rel="stylesheet"> -->

<div class="timeline-wrapper container-fluid">
  <div class="row">

    <div class="timeline-left-content col bg-white dropshadow-r-2 pt-4">

      <div class="timeline-inner-wrapper">

        <div class="timeline-head row align-items-center">
          <div class="timeline-image col-3 col-xl-2">
            <img src="data:image/png;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs=" />
          </div>
          <div class="timeline-name col-9 col-xl-10">
            <h2 class="font-weight-black text-green">Student Name</h2>
          </div>
        </div>

        <div class="timeline row">
          <div class="scrolling-box">
						
            <div class="timeline-row row type-add">
							<!-- <form> -->
              <div class="col-3 col-xl-2">
								<a href="#" class="fas icon"></a>
								<div class="timeline-add-select-type">
									<a href="#" class="timeline-select-type-lesson" title="Les"><i class="fas fa-book"></i></a>
									<a href="#" class="timeline-select-type-assignment" title="Opdracht"><i class="fas fa-pencil-alt"></i></a>
									<a href="#" class="timeline-select-type-document" title="Bestand"><i class="fas fa-paperclip"></i></a>
									<a href="#" class="timeline-select-type-message" title="Les"><i class="fas fa-comment"></i></a>
								</div>
              </div>
              <div class="col-9 col-xl-10">
								<input type="text" class="timeline-add-title form-control" value="" placeholder="Type hier een titel" />
              	<span class="timeline-add-content">
									<span class="timeline-add-lesson">Hier komen invoervelden per type.</span>
									<!-- submit...

SCSS

// Fonts
//@import url('https://fonts.googleapis.com/css2?family=Nunito+Sans:wght@300;400;700;900&display=swap');

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

// Colors
$theme-colors: ("green": #45cd98,
  "yellow": #f9e5a5,
  "orange": #fac694,
  "red": #e37762,
  "darkred": #752e38,
);
$theme-colors: map-merge($theme-colors, ("green-60": mix(map-get($theme-colors, "green"), #fff, 60%),
    "green-25": mix(map-get($theme-colors, "green"), #fff, 25%),
    "green-10": mix(map-get($theme-colors, "green"), #fff, 10%),
    "light-grey": rgb(247, 247, 247),
    "dark-grey": rgb(153, 153, 153),
    "border-grey": rgb(204, 204, 204),
    "cb-grey": rgb(212, 212, 212),
    "cb-dark-grey": rgb(179, 179, 179),
    "near-black": #222,
  ));

// Utilities helper
$utilities: () !default;
$utilities: map-merge(("color": (class: text,
      property: color,
      values: $theme-colors),
    "background": (class: bg,
      property: background-color,
      values: $theme-colors),
    "dropshadow": (class: dropshadow,
      property: box-shadow,
      values: ("b-1": 0 -8px 8px 8px rgba(0, 0, 0, .2),
        "b-2": 0 -7px 8px 8px rgba(0, 0, 0, .2),
        "r-1": -8px -8px 8px 8px rgba(0, 0, 0, .2),
        "r-2": -7px -7px 8px 8px rgba(0, 0, 0, .2),
      )),
    // ...
    // "margin": (
    //   responsive: true,
    //   property: margin,
    //   class: m,
    //   values: map-merge($spacers, (auto: auto))
    // ),
    // ...
  ), $utilities);

// Generate utility classes
@each $uk, $u in $utilities {
  @each $vk, $v in map-get($u, "values") {
    .#{map-get($u, "class")}-#{$vk} {
      #{map-get($u, "property")}: $v;
    }
  }
}

// Bootstrap function (just for JSFiddle)
@function theme-color($key: "primary") {
  @return map-get($theme-colors, $key);
}


.font-weight-black {
  font-weight: 900;
}


// General styling
html,
body {
  height: 100%;
}

body {
  font-family: 'Nunito Sans', sans-serif;
  font-size: 16px;
 ...

JavaScript

$( document ).ready(function(){

	$('.timeline-row.type-add').each(function(i,el){
		var $el = $( el );
		
		// Type selector
		$el.find('> div:first-child > a.icon').on('click', function(ev){
			ev.preventDefault();
			var $this = $( this );
			if ( $this.next().hasClass('show') ) {
				$this.parent().parent()
				// Remove all classes starting with "type-" except "type-add"
				.removeClass(function(i2, css) {
					return (css.match(/(?:^|\s)type-(?!add).+/g) || []).join(' ');
				});
				//TODO: Reset form
			}
			$this.next().toggleClass('show');
		})
		.next().find('a').on('click', function(ev){
			ev.preventDefault();
			var $this = $( this );
			var item_type = $this.attr('class').replace(/^timeline-select-/, '');
			// Close selector
			$this.parent()
				.removeClass('show')
			.parent().parent()
				// Remove all classes starting with "type-" except "type-add"
				.removeClass(function(i2, css) {
					return (css.match(/(?:^|\s)type-(?!add).+/g) || []).join(' ');
				})
				// Set selected type
				.addClass( item_type );
			//TODO: set hidden input field
			//TODO: only show input fields for this type (or use Vue.JS')
		});
	});
	
	//TODO: focus/blur event handling (also with inputs)
	//TODO: reset "+" at cancel --> when is this?

});