WackoWiki: Timeline

https://wackowiki.org/doc     Version: 24 (19.01.2024 09:50)

Timeline


see also /Dev/PatchesHacks/Timeline[link1]


1. Evaluation

I tend more to do a formatter out of it, or an action.
We should parse the description text as Wiki syntax so the user can add links, images and additional formatting.

Simply put, there is the CSS and the HTML and the data input as an array we provide which is then processed via foreach.
The CSS should put in a separate CSS, the formatter can't add a CSS file dynamically to the header like an action - its static once parsed into body_r.

There is a way to add a action call in the syntax, so it would load the style sheet dynamically - guess it requires a addhtml action, calling the function with the same name, to do that. However this would allow every user to inject random items, so we should add a new resource call option to the PostWacko class, where certain resources can be loaded dynamically via addhtml() in a formatter, such as CSS or JavaScript files.

'<ignore><!--notypo--><!--resource:begin-->' .
	'addhtml ' .
		'location='header' .
		'text=' . '<link rel="stylesheet" media="screen" href="' . $this->db->base_path . Ut::join_path(THEME_DIR, '_common/timeline.css') . '">' .
'<!--resource:end--><!--/notypo--></ignore>'	


We need a format the user provide which gets then parsed into an array, JSON or a delimiter and line-break?
direction: 'l|r'
flag: 'Moscow|Amsterdam'
time: 'September 12-13, 2022|July 1 & 2, 2022'
description: 'PHP Russia 2022|Dutch PHP Conference 2022'	

The format should be carefully selected for consistent use and further use in template boxes.

1.1. Formatter

%%(timeline)
JSON?

or

l | Moscow | September 12-13, 2022 | PHP Russia 2022
r | Amsterdam | July 1 & 2, 2022 | Dutch PHP Conference 2022
%%

<?php

/*
    converts inline data into a timeline

    % %(timeline
        [order="asc|desc"]
        [delim="comma|semicolon"]
        )
    content
    % %

    [0] direction: 'l|r'    (left|right)
    [1] flag: 'Amsterdam'
    [2] time: 'July 1 & 2, 2022'
    [3] description: 'Dutch PHP Conference 2022'

    e.g.
    l | Moscow | September 12-13, 2022 | PHP Russia 2022
    r | Amsterdam | July 1 & 2, 2022 | Dutch PHP Conference 2022

    https://wackowiki.org/doc/Dev/PatchesHacks/Timeline
*/

// set defaults
$options['delim']    ??= 'pipe';
$options['order']    ??= 'asc';

$delim    = match($options['delim']) {
    'semicolon'    => ';',
    default        => '|',
};

// set CSS styles once
if (!isset($this->timline))
{
    $this->timline    = true;
    $tpl->style_n    = true;
}

// get data
$lines    = preg_split('/[\n]/u', $text);

if ($options['order'] == 'desc')
{
    $lines = array_reverse($lines);
}

foreach ($lines as $line)
{
    // blank
    if (preg_match('/^#|^\s*$/u', $line))
    {
        continue;
    }

    $tpl->enter('n_');

    $item = explode('|', $line);
    {
        // debug
        #Ut::debug_print_r($item);

        $direction    = match(trim($item[0])) {
            'r'            => 'r',
            default        => 'l',
        };

        // further string processing here (links, filter, ...)
        $flag            = trim($item[1] ?? '');
        $time            = trim($item[2] ?? '');
        $description    = trim($item[3] ?? '');

        if ($flag)
        {
            $tpl->direction        = $direction;
            $tpl->flag            = $flag;
            $tpl->time            = $time;
            $tpl->description    = $this->format($description, 'wiki', ['post_wacko' => true]);
        }
    }

    $tpl->leave(); // n_
}


[ === main === ]
<ignore>
	<!--notypo-->
	[ ' style ' ]
	<ul class="timeline">
		[= n _ =
		<li>
			<div class="direction-[ ' direction ' ]">
				<div class="flag-wrapper">
					<span class="flag">[ ' flag | e ' ]</span>
					<span class="time-wrapper">
						<span class="time">[ ' time | e ' ]</span>
					</span>
				</div>
				<div class="desc">[ ' description ' ]</div>
			</div>
		</li>
		=]
	</ul>	
	<!--/notypo-->
</ignore>

[ === style === ]
<style>[ ' n css ' ]</style>

[ === css === ]
[ ' nonstatic ' ]
/* ================ The Timeline ================ */

.timeline {
	position: relative;
	width: 660px;
	margin: 0 auto;
	margin-top: 20px;
	padding: 1em 0;
	list-style-type: none;
}

[ ' // and more CSS ... ' ]	



2. Source

<!-- The Timeline -->

<ul class="timeline">

  <!-- Item 1 -->
  <li>
    <div class="direction-r">
      <div class="flag-wrapper">
        <span class="flag">Freelancer</span>
        <span class="time-wrapper"><span class="time">2013 - present</span></span>
      </div>
      <div class="desc">My current employment. Way better than the position before!</div>
    </div>
  </li>

  <!-- Item 2 -->
  <li>
    <div class="direction-l">
      <div class="flag-wrapper">
        <span class="flag">Apple Inc.</span>
        <span class="time-wrapper"><span class="time">2011 - 2013</span></span>
      </div>
      <div class="desc">My first employer. All the stuff I've learned and projects I've been working on.</div>
    </div>
  </li>

  <!-- Item 3 -->
  <li>
    <div class="direction-r">
      <div class="flag-wrapper">
        <span class="flag">Harvard University</span>
        <span class="time-wrapper"><span class="time">2008 - 2011</span></span>
      </div>
      <div class="desc">A description of all the lectures and courses I have taken and my final degree?</div>
    </div>
  </li>

</ul>


body {  
  margin: 0;
  padding: 0;
  background: rgb(230,230,230);

  color: rgb(50,50,50);
  font-family: 'Open Sans', sans-serif;
  font-size: 112.5%;
  line-height: 1.6em;
}

/* ================ The Timeline ================ */

.timeline {
    position: relative;
    width: 660px;
    margin: 0 auto;
    margin-top: 20px;
    padding: 1em 0;
    list-style-type: none;
}

.timeline:before {
    position: absolute;
    left: 50%;
    top: 0;
    content: ' ';
    display: block;
    width: 6px;
    height: 100%;
    margin-left: -3px;
    background: rgb(80,80,80);
    background: linear-gradient(to bottom, rgba(80,80,80,0) 0%, rgb(80,80,80) 8%, rgb(80,80,80) 92%, rgba(80,80,80,0) 100%);

    z-index: 5;
}

.timeline li {
    padding: 1em 0;
}

.timeline li:after {
    content: '';
    display: block;
    height: 0;
    clear: both;
    visibility: hidden;
}

.direction-l {
    position: relative;
    width: 300px;
    float: left;
    text-align: right;
}

.direction-r {
    position: relative;
    width: 300px;
    float: right;
}

.flag-wrapper {
    position: relative;
    display: inline-block;

    text-align: center;
}

.flag {
    position: relative;
    display: inline;
    background: rgb(248,248,248);
    padding: 6px 10px;
    border-radius: 5px;

    font-weight: 600;
    text-align: left;
}

.direction-l .flag {
    box-shadow: -1px 1px 1px rgba(0,0,0,0.15), 0 0 1px rgba(0,0,0,0.15);
}

.direction-r .flag {
    box-shadow: 1px 1px 1px rgba(0,0,0,0.15), 0 0 1px rgba(0,0,0,0.15);
}

.direction-l .flag:before,
.direction-r .flag:before {
    position: absolute;
    top: 50%;
    right: -40px;
    content: ' ';
    display: block;
    width: 12px;
    height: 12px;
    margin-top: -10px;
    background: #fff;
    border-radius: 10px;
    border: 4px solid rgb(255,80,80);
    z-index: 10;
}

.direction-r .flag:before {
    left: -40px;
}

.direction-l .flag:after {
    content: '';
    position: absolute;
    left: 100%;
    top: 50%;
    height: 0;
    width: 0;
    margin-top: -8px;
    border: solid transparent;
    border-left-color: rgb(248,248,248);
    border-width: 8px;
    pointer-events: none;
}

.direction-r .flag:after {
    content: '';
    position: absolute;
    right: 100%;
    top: 50%;
    height: 0;
    width: 0;
    margin-top: -8px;
    border: solid transparent;
    border-right-color: rgb(248,248,248);
    border-width: 8px;
    pointer-events: none;
}

.time-wrapper {
    display: inline;

    line-height: 1rem;
    font-size: 0.66666rem;
    color: rgb(250,80,80);
    vertical-align: middle;
}

.direction-l .time-wrapper {
    float: left;
}

.direction-r .time-wrapper {
    float: right;
}

.time {
    display: inline-block;
    padding: 4px 6px;
    background: rgb(248,248,248);
}

.desc {
    margin: 1rem 0.75rem 0 0;

    font-size: 0.77777rem;
    font-style: italic;
    line-height: 1.5rem;
}

.direction-r .desc {
    margin: 1em 0 0 0.75rem;
}

/* ================ Timeline Media Queries ================ */

@media screen and (max-width: 660px) {

    .timeline {
        width: 100%;
        padding: 4rem 0 1rem 0;
    }

    .timeline li {
        padding: 2rem 0;
    }

    .direction-l,
    .direction-r {
        float: none;
        width: 100%;

        text-align: center;
    }

    .flag-wrapper {
        text-align: center;
    }

    .flag {
        background: rgb(255,255,255);
        z-index: 15;
    }

    .direction-l .flag:before,
    .direction-r .flag:before {
        position: absolute;
        top: -30px;
        left: 50%;
        content: ' ';
        display: block;
        width: 12px;
        height: 12px;
        margin-left: -9px;
        background: #fff;
        border-radius: 10px;
        border: 4px solid rgb(255,80,80);
        z-index: 10;
    }

    .direction-l .flag:after,
    .direction-r .flag:after {
        content: '';
        position: absolute;
        left: 50%;
        top: -8px;
        height: 0;
        width: 0;
        margin-left: -8px;
        border: solid transparent;
        border-bottom-color: rgb(255,255,255);
        border-width: 8px;
        pointer-events: none;
    }

    .time-wrapper {
        display: block;
        position: relative;
        margin: 4px 0 0 0;
        z-index: 14;
    }

    .direction-l .time-wrapper {
        float: none;
    }

    .direction-r .time-wrapper {
        float: none;
    }

    .desc {
        position: relative;
        margin: 1rem 0 0 0;
        padding: 1rem;
        background: rgb(245,245,245);
        box-shadow: 0 0 1px rgba(0,0,0,0.20);

        z-index: 15;
    }

    .direction-l .desc,
    .direction-r .desc {
        position: relative;
        margin: 1rem 1em 0 1rem;
        padding: 1rem;

        z-index: 15;
    }

}

@media screen and (min-width: 400px ?? max-width: 660px) {

    .direction-l .desc,
    .direction-r .desc {
        margin: 1em 4em 0 4em;
    }

}