diff --git a/.idea/dictionaries/Schme.xml b/.idea/dictionaries/Schme.xml index 491e446..2030168 100644 --- a/.idea/dictionaries/Schme.xml +++ b/.idea/dictionaries/Schme.xml @@ -2,18 +2,22 @@ contenthash + cybersecurity ffffff gifsicle imagemin jpegtran lato + markwon mozjpeg noquotes opacify optipng pngquant raleway + screenshot transparentize + webm webp diff --git a/.idea/workspace.xml b/.idea/workspace.xml index 221eb63..0111147 100644 --- a/.idea/workspace.xml +++ b/.idea/workspace.xml @@ -2,28 +2,30 @@ + + + + + - - - - - + + + + + + + - - - - + + - - - diff --git a/custom.d.ts b/custom.d.ts index 762d252..8c6550d 100644 --- a/custom.d.ts +++ b/custom.d.ts @@ -1,34 +1,47 @@ declare module "*.svg" { - const content: string; + import { url } from "src/model/misc"; + const content: url; export default content; } declare module "*.png" { - const content: string; + import { ResponsiveImage } from "src/model/misc"; + const content: ResponsiveImage; export default content; } declare module "*.jpg" { - const content: string; + import { ResponsiveImage } from "src/model/misc"; + const content: ResponsiveImage; export default content; } declare module "*.jpeg" { - const content: string; + import { ResponsiveImage } from "src/model/misc"; + const content: ResponsiveImage; export default content; } declare module "*.gif" { - const content: string; + import { url } from "src/model/misc"; + const content: url; export default content; } declare module "*.mp4" { - const content: string; + import { url } from "src/model/misc"; + const content: url; + export default content; +} + +declare module "*.webm" { + import { url } from "src/model/misc"; + const content: url; export default content; } declare module "*.pdf" { - const content: string; + import { url } from "src/model/misc"; + const content: url; export default content; } diff --git a/package.json b/package.json index 9c46bf3..735ea3a 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,6 @@ "description": "An easily configurable portfolio.", "private": true, "scripts": { - "test": "echo \"Error: no test specified\" && exit 1", "start": "webpack-dev-server --mode development", "build": "webpack" }, @@ -35,7 +34,9 @@ "optimize-css-assets-webpack-plugin": "^5.0.3", "prettier": "^1.19.1", "resolve-url-loader": "^3.1.1", + "responsive-loader": "^1.2.0", "sass-loader": "^8.0.0", + "sharp": "^0.23.4", "style-loader": "^1.0.2", "svg-url-loader": "^3.0.3", "terser-webpack-plugin": "^2.3.1", diff --git a/src/framework/helper.ts b/src/framework/helper.ts index 1a14187..bdd67e3 100644 --- a/src/framework/helper.ts +++ b/src/framework/helper.ts @@ -1,4 +1,4 @@ -import { html } from "../model/misc"; +import { html, ResponsiveImage } from "../model/misc"; export const createElement = (from: html): HTMLElement => { const element: HTMLElement = document.createElement("div"); @@ -88,3 +88,6 @@ export const range = ({ } }; }; + +export const last = (list: Array): T => + list.length > 0 ? list[list.length - 1] : undefined; diff --git a/src/model/content.ts b/src/model/content.ts index 2a38cc1..eca9afd 100644 --- a/src/model/content.ts +++ b/src/model/content.ts @@ -1,4 +1,4 @@ -import { url } from "./misc"; +import { ResponsiveImage, url } from "./misc"; interface Anchor { type: "a"; @@ -6,11 +6,19 @@ interface Anchor { text: string; } -interface Video { +export type Video = { type: "video"; - src: url; -} + mp4: url; + webm: url; + options?: string; +}; -export type TypedContent = Anchor | Video; +export type Image = { + type: "img"; + alt: string; + image: ResponsiveImage; +}; + +export type TypedContent = Anchor | Video | Image; export type Content = Array; diff --git a/src/model/misc.ts b/src/model/misc.ts index e5f4adc..affee68 100644 --- a/src/model/misc.ts +++ b/src/model/misc.ts @@ -1,2 +1,16 @@ export type url = string; + export type html = string; + +export type ResponsiveImage = { + srcSet: string; + src: url; + placeholder: string; + width: number; + height: number; + images: Array<{ + path: url; + width: number; + height: number; + }>; +}; diff --git a/src/model/portfolio.ts b/src/model/portfolio.ts index c10fe76..1b93c3a 100644 --- a/src/model/portfolio.ts +++ b/src/model/portfolio.ts @@ -1,5 +1,5 @@ import { url } from "./misc"; -import { Content } from "./content"; +import { Content, Image, Video } from "./content"; export interface Portfolio { config: Config; @@ -11,19 +11,18 @@ export interface Portfolio { export interface Config { showMore: string; showLess: string; - aPictureOf: string; } export interface Header { name: string; - picture: url; + picture: Image; about: Content; } export interface TimelineElement { title: string; date: string; - picture: url; + figure: Image | Video; description: string; more?: Content; link?: url; @@ -34,8 +33,6 @@ export interface Footer { email: string; cv: url; cvName: string; - githubLinkName: string; - githubLink: url; lastEditName: string; lastEdit: Date; } diff --git a/src/page/about/about.html.ts b/src/page/about/about.html.ts index 52136b0..fe212e7 100644 --- a/src/page/about/about.html.ts +++ b/src/page/about/about.html.ts @@ -1,13 +1,12 @@ import { Header } from "../../model/portfolio"; import { html } from "../../model/misc"; +import { PageContent } from "../content/content"; + import "./about.scss"; -export const generate = ( - { name, picture, about }: Header, - aPictureOf: string -): html => ` +export const generate = ({ name, picture }: Header): html => `
- ${aPictureOf} ${name} + ${PageContent.parseTypedContent(picture, true)}

${name}

`; diff --git a/src/page/about/about.ts b/src/page/about/about.ts index 0e3efc3..341040b 100644 --- a/src/page/about/about.ts +++ b/src/page/about/about.ts @@ -6,8 +6,8 @@ import { generate } from "./about.html"; import { createElement } from "../../framework/helper"; export class PageHeader extends PageElement { - public constructor(header: Header, aPictureOf: string) { - const root = createElement(generate(header, aPictureOf)); + public constructor(header: Header) { + const root = createElement(generate(header)); const content = new PageContent(header.about); super([content]); diff --git a/src/page/content/content.scss b/src/page/content/content.scss index a442235..57d5057 100644 --- a/src/page/content/content.scss +++ b/src/page/content/content.scss @@ -11,3 +11,15 @@ text-align: left; } } + +.image-container { + font-size: 0; + box-shadow: inset $shadow1, inset $shadow2; + pointer-events: none; + img { + pointer-events: all; + cursor: pointer; + position: relative; + z-index: -2; + } +} diff --git a/src/page/content/content.ts b/src/page/content/content.ts index 9aa9d08..716f8e5 100644 --- a/src/page/content/content.ts +++ b/src/page/content/content.ts @@ -1,13 +1,44 @@ import { Content, TypedContent } from "../../model/content"; import "./content.scss"; import { PageElement } from "../../framework/page-element"; -import { createElement } from "../../framework/helper"; +import { createElement, last } from "../../framework/helper"; +import { html } from "../../model/misc"; export class PageContent extends PageElement { private static isTyped(content): content is TypedContent { return (content as TypedContent).type !== undefined; } + public static parseTypedContent( + element: TypedContent, + disableInnerShadow?: boolean + ): html { + if (element.type === "a") { + return ` ${element.text} `; + } + if (element.type === "video") { + return ` + + `; + } + if (element.type === "img") { + return ` + ${!disableInnerShadow ? `
` : ""} + ${element.alt} + ${!disableInnerShadow ? `
` : ""} + `; + } + + throw new Error("Unhandled type."); + } + public constructor(content: Content) { super(); @@ -15,18 +46,11 @@ export class PageContent extends PageElement { createElement(`
${content - .map(element => { - if (PageContent.isTyped(element)) { - if (element.type === "a") { - return ` ${element.text} `; - } - if (element.type === "video") { - return ``; - } - throw new Error("Unhandled type."); - } - return `

${element}

`; - }) + .map(element => + PageContent.isTyped(element) + ? PageContent.parseTypedContent(element) + : `

${element}

` + ) .join("\n")}
`) diff --git a/src/page/footer/footer.html.ts b/src/page/footer/footer.html.ts index c06bf50..2e8a768 100644 --- a/src/page/footer/footer.html.ts +++ b/src/page/footer/footer.html.ts @@ -1,9 +1,9 @@ import { Footer } from "../../model/portfolio"; -import { html, url } from "../../model/misc"; +import { html } from "../../model/misc"; +import emailIcon from "../../static/icons/at.svg"; +import cvIcon from "../../static/icons/cv.svg"; import "./footer.scss"; -import cvIcon from "../../static/icons/cv.svg"; -import emailIcon from "../../static/icons/at.svg"; export const generate = ({ title, @@ -11,9 +11,7 @@ export const generate = ({ cv, cvName, lastEditName, - lastEdit, - githubLinkName, - githubLink + lastEdit }: Footer): html => `