Update features

This commit is contained in:
schmelczerandras 2020-11-18 23:31:26 +01:00
parent f66f052d7e
commit 4d7d15c3c7
34 changed files with 482 additions and 238 deletions

View file

@ -1,12 +1,11 @@
import { Header } from '../../types/portfolio';
import './about.scss';
import { Header } from '../../types/portfolio';
import { html } from '../../types/html';
export const generate = ({ name }: Header): html => `
<section id="about">
<div class="picture"></div>
<div class="placeholder"></div>
<h1>${name}</h1>
</section>
<section id="about">
<div class="picture"></div>
<div class="placeholder"></div>
<h1>${name}</h1>
</section>
`;

View file

@ -2,6 +2,7 @@
section#about {
@include card-base();
padding: var(--normal-margin);
background-color: var(--accent-color);
font-size: 0;
@ -31,7 +32,7 @@ section#about {
p,
h1 {
color: var(--very-light-text-color);
margin-top: var(--small-margin);
margin-top: var(--line-height);
}
@include on-large-screen {

View file

@ -1,6 +1,5 @@
import { PageContent } from '../content/content';
import { Header } from '../../types/portfolio';
import { generate } from './about.html';
import { createElement } from '../../helper/create-element';
import { PageThemeSwitcher } from '../theme-switcher/theme-switcher';

View file

@ -2,5 +2,5 @@ import './background.scss';
import { html } from '../../types/html';
export const generate = (): html => `
<canvas id="background"></canvas>
<canvas id="background"></canvas>
`;

View file

@ -1,7 +1,8 @@
import './anchor.scss';
import { html } from '../../../types/html';
import { url } from '../../../types/url';
export const generate = ({ href, text }: { href: string; text: string }): html => `
export const generate = ({ href, text }: { href: url; text: string }): html => `
<a class="primitive-anchor"
href="${href}"
target="_blank"

View file

@ -0,0 +1,23 @@
import './image-anchor.scss';
import { html } from '../../../types/html';
import { url } from '../../../types/url';
export const generate = ({
href,
svg,
title,
}: {
href: url;
svg: url;
title: string;
}): html => `
<a class="image-anchor"
href="${href}"
target="_blank"
>
<div class="svgContainer">
${svg}
</div>
<p>${title}</p>
</a>
`;

View file

@ -0,0 +1,21 @@
@use '../../../style/mixins' as *;
.image-anchor {
@include image-button(var(--icon-size));
.svgContainer {
position: relative;
margin: auto;
@include square(var(--icon-size));
svg {
stroke: var(--normal-text-color);
}
}
p {
padding-bottom: var(--small-margin);
font-size: 0.9rem;
font-style: italic;
}
}

View file

@ -0,0 +1,11 @@
import { PageElement } from '../../page-element';
import { createElement } from '../../../helper/create-element';
import { url } from '../../../types/url';
import { generate } from './image-anchor.html';
export const ImageAnchorFactory = (svg: string, title: string) =>
class ImageAnchor extends PageElement {
public constructor(href: url) {
super(createElement(generate({ href, svg, title })));
}
};

View file

@ -16,10 +16,10 @@ export const generate = ({
}): html => `
${container ? `<div class="figure-container">` : ''}
<img tabindex="0"
srcset="${image.srcSet}"
sizes="${sizes}"
src="${last(image.images)?.path}"
alt="${alt}"
srcset="${image.srcSet}"
sizes="${sizes}"
src="${last(image.images)?.path}"
alt="${alt}"
/>
${container ? `</div>` : ''}
`;

View file

@ -0,0 +1,15 @@
import './preview.scss';
import play from '../../../static/icons/play-button.svg';
import loading from '../../../static/icons/loading.svg';
import { html } from '../../../types/html';
export const generate = ({ alt }: { alt: string }): html => `
<div class="preview">
<img image-viewer-ignore class="poster" />
<div class="overlay">
<iframe title="${alt}" height=300 allowfullscreen loading="lazy"></iframe>
<div class="loading">${loading}</div>
<div class="load-button">${play}</div>
</div>
</div>
`;

View file

@ -0,0 +1,65 @@
@use '../../../style/mixins' as *;
.preview {
position: relative;
.overlay {
@include square(100%);
position: absolute;
left: 0;
top: 0;
* {
position: absolute;
left: 0;
right: 0;
}
.load-button {
@include image-button(var(--large-icon-size));
@include absolute-center;
@include square(calc(var(--large-icon-size) + var(--normal-margin) * 2));
&:hover svg {
box-shadow: var(--shadow);
}
svg {
border-radius: 10000px;
backdrop-filter: blur(16px);
transition: transform var(--transition-time), box-shadow var(--transition-time);
stroke: var(--normal-text-color);
}
}
.loading {
visibility: hidden;
&,
& > svg {
@include square(var(--large-icon-size));
@include absolute-center;
}
}
iframe {
@include square(100%);
border: none;
&:fullscreen {
border-radius: 0;
}
}
}
&.loaded {
.figure-container,
.load-button {
visibility: hidden;
}
}
&.waiting {
.loading {
visibility: visible;
}
}
}

View file

@ -0,0 +1,39 @@
import { PageElement } from '../../page-element';
import { createElement } from '../../../helper/create-element';
import { generate } from './preview.html';
import { Image } from '../image/image';
import { ResponsiveImage } from '../../../types/responsive-image';
import { OnLoadEvent } from '../../../events/concrete-events/on-load-event';
export class Preview extends PageElement {
public constructor(poster: ResponsiveImage, private readonly url: string, alt: string) {
super(createElement(generate({ alt })));
this.url += '?portfolioView';
this.attachElementByReplacing('.poster', new Image(poster, alt));
this.query('.load-button').addEventListener('click', this.loadContent.bind(this));
this.query('iframe').addEventListener('load', () => {
this.htmlRoot.classList.remove('waiting');
});
}
public handleOnLoadEvent(event: OnLoadEvent): OnLoadEvent {
new IntersectionObserver(e => {
if (!e[0].isIntersecting) {
this.unloadContent();
}
}).observe(this.htmlRoot.parentElement);
return event;
}
public loadContent() {
this.htmlRoot.classList.add('waiting');
this.htmlRoot.classList.add('loaded');
(this.query('iframe') as HTMLIFrameElement).src = this.url;
}
public unloadContent() {
this.htmlRoot.classList.remove('loaded');
(this.query('iframe') as HTMLIFrameElement).src = '';
}
}

View file

@ -1,4 +1,3 @@
.primitive-text {
text-align: left;
margin-top: var(--line-height);
}

View file

@ -6,6 +6,10 @@ footer#page-footer {
margin-top: var(--large-margin);
width: 100%;
a {
@include link;
}
h2 {
@include title-font();
}
@ -27,11 +31,8 @@ footer#page-footer {
img,
svg {
@include max-square(var(--icon-size));
margin-right: var(--small-margin);
* {
fill: var(--normal-text-color);
}
margin-right: calc(var(--small-margin) / 2);
stroke: var(--normal-text-color);
}
a {

View file

@ -4,8 +4,8 @@ import './image-viewer.scss';
import { html } from '../../types/html';
export const generate = (): html => `
<section id="image-viewer">
<div id="container"></div>
<img tabindex="0" id="cancel" src="${cancel}" alt="cancel"/>
</section>
<section id="image-viewer">
<div id="container"></div>
<div tabindex="0" id="cancel">${cancel}</div>
</section>
`;

View file

@ -28,12 +28,11 @@ section#image-viewer {
}
#cancel {
@include square(var(--icon-size));
@include square(calc(var(--large-icon-size) + var(--normal-margin) * 2));
position: absolute;
box-sizing: content-box;
padding: var(--normal-margin);
right: 0;
top: 0;
cursor: pointer;
@include image-button(var(--large-icon-size));
}
}

View file

@ -17,8 +17,9 @@ export class PageImageViewer extends PageElement {
const media = Array.prototype.slice.call(document.querySelectorAll('img'));
media
.filter((e: HTMLElement) => e.parentElement !== this.htmlRoot)
.filter((e: HTMLElement) => !e.attributes['image-viewer-ignore'])
.forEach((e: HTMLImageElement) => (e.onclick = this.handleClick.bind(this)));
return super.handleOnLoadEvent(event);
}

View file

@ -44,6 +44,11 @@ export abstract class PageElement extends EventHandler implements EventBroadcast
this.children.push(element);
}
protected attachElementAsChildOf(query: string, element: PageElement) {
this.query(query).appendChild(element.htmlRoot);
this.children.push(element);
}
protected attachElement(element: PageElement) {
this.htmlRoot.appendChild(element.htmlRoot);
this.children.push(element);

View file

@ -2,5 +2,5 @@ import './theme-switcher.scss';
import { html } from '../../types/html';
export const generate = (): html => `
<input id="theme-switcher" aria-label="color-theme-switch" type="checkbox" name="switch-theme"/>
<input id="theme-switcher" aria-label="color-theme-switch" type="checkbox" name="switch-theme"/>
`;

View file

@ -1,34 +1,35 @@
import { TimelineElement } from '../../../types/portfolio';
import info from '../../../static/icons/info.svg';
import './timeline-element.scss';
import { html } from '../../../types/html';
export const generate = (
{ date, title, more }: TimelineElement,
showMore: string,
showLess: string
showMore: string
): html => `
<section class="timeline-element">
<div class="line-container">
<div class="line"></div>
<p class="date">${date}</p>
<section class="timeline-element">
<div class="line-container">
<div class="line"></div>
<p class="date">${date}</p>
</div>
<div class="card">
<div class="figure"></div>
<div class="lower">
<h2>${title}</h2>
<div class="description"></div>
${more ? '<div class="more"></div>' : ''}
<div class="buttons">
${
more
? `
<div class="info-button">
<div class="svgContainer">${info}</div>
<p>${showMore}</p>
</div>`
: ''
}
</div>
<div class="card">
<h2>${title}</h2>
<div class="figure"></div>
<div class="description"></div>
${
more
? `
<div class="more"></div>
<div class="buttons">
<a tabindex="0" class="show-more">${showMore}</a>
<a tabindex="0" class="show-less">${showLess}</a>
</div>
`
: ''
}
<div class="link"></div>
</div>
</section>
</div>
</div>
</section>
`;

View file

@ -91,46 +91,75 @@ section.timeline-element {
background-color: var(--card-color);
}
& > *:not(:first-child) {
margin-top: var(--line-height);
img,
video,
iframe {
border-radius: var(--border-radius) var(--border-radius) 0 0;
}
.content {
margin-top: 0;
}
$border-width: 1px;
h2 {
@include sub-title-font();
}
& > p {
font-style: italic;
text-align: center;
}
.more {
overflow: hidden;
height: 0;
transition: height var(--transition-time);
}
.buttons {
position: relative;
margin-top: var(--line-height);
.show-more,
.show-less {
transition: opacity var(--transition-time);
.lower {
& > * {
padding: 0 var(--normal-margin);
margin-top: var(--small-margin);
}
.show-more {
opacity: 1;
h2 {
@include sub-title-font();
}
.show-less {
@include absolute-center();
opacity: 0;
visibility: hidden;
& > p {
text-align: center;
}
.more {
overflow: hidden;
margin: 0;
height: 0;
transition: height var(--transition-time);
.content p {
margin-top: var(--line-height);
}
}
.buttons {
display: flex;
justify-content: center;
border-top: $border-width solid var(--normal-text-color);
margin: 0;
padding: 0;
margin-top: var(--small-margin);
.info-button {
@include image-button(var(--icon-size));
.svgContainer {
position: relative;
margin: auto;
@include square(var(--icon-size));
svg {
stroke: var(--normal-text-color);
}
}
p {
padding-bottom: var(--small-margin);
font-size: 0.9rem;
font-style: italic;
}
}
& > * {
flex: 1;
padding-top: var(--small-margin);
&:not(:last-child) {
border-right: $border-width solid var(--normal-text-color);
}
}
}
}
}

View file

@ -8,13 +8,15 @@ import { OnBodyDimensionsChangedEvent } from '../../../events/concrete-events/on
export class PageTimelineElement extends PageElement {
private isOpen: boolean;
private more: HTMLElement;
private showMore: string;
private showLess: string;
public constructor(
timelineElement: TimelineElement,
showMore: string,
showLess: string
) {
const root = createElement(generate(timelineElement, showMore, showLess));
const root = createElement(generate(timelineElement, showMore));
if (timelineElement.more) {
const content = new PageContent(timelineElement.more);
@ -23,30 +25,25 @@ export class PageTimelineElement extends PageElement {
this.isOpen = false;
this.more = root.querySelector('.more');
this.more.appendChild(content.htmlRoot);
window.addEventListener('resize', this.handleResize.bind(this));
root
.querySelector('.buttons')
.addEventListener('click', this.toggleOpen.bind(this));
addEventListener('resize', this.handleResize.bind(this));
this.query('.info-button').addEventListener('click', this.toggleOpen.bind(this));
} else super(root);
this.attachElementByReplacing('.figure', timelineElement.figure);
this.attachElementByReplacing('.description', timelineElement.description);
timelineElement.links.forEach(l => this.attachElementAsChildOf('.buttons', l));
if (timelineElement.link) {
this.attachElementByReplacing('.link', timelineElement.link);
}
this.showMore = showMore;
this.showLess = showLess;
}
private toggleOpen() {
const showMore = this.query('.show-more') as HTMLElement;
const showLess = this.query('.show-less') as HTMLElement;
if (this.isOpen) {
PageTimelineElement.show(showMore);
PageTimelineElement.hide(showLess);
this.query('.info-button p').innerText = this.showMore;
this.closeMore();
} else {
PageTimelineElement.show(showLess);
PageTimelineElement.hide(showMore);
this.query('.info-button p').innerText = this.showLess;
this.openMore();
}

View file

@ -2,5 +2,5 @@ import './timeline.scss';
import { html } from '../../types/html';
export const generate = (): html => `
<div id="timeline"></div>
<div id="timeline"></div>
`;

View file

@ -1,10 +1,7 @@
@use '../../style/mixins' as *;
div#timeline {
@include on-large-screen {
// workaround for IE
& > :first-child {
margin-top: var(--large-margin);
}
@include on-large-screen {
div#timeline > :first-child {
margin-top: var(--large-margin);
}
}