@tmjssz/jsonresume-theme-even
Version:
A flat JSON Resume theme, compatible with the latest resume schema
496 lines (488 loc) • 18.1 kB
JavaScript
var x = Object.freeze, L = Object.defineProperty;
var I = (t, i) => x(L(t, "raw", { value: x(i || t.slice()) }));
import { html as e } from "@rbardini/html";
import j from "micromark";
import C from "striptags";
const T = `const pluralize = (num, str) => \`\${num} \${num === 1 ? str : str.concat('s')}\`
class TimeDuration extends HTMLElement {
connectedCallback() {
const [from, to] = this.querySelectorAll('time')
if (!from) return
const startDate = new Date(from.dateTime || from.textContent)
const endDate = new Date(to ? to.dateTime || to.textContent : Date.now())
const diffDate = new Date(endDate - startDate)
const years = diffDate.getFullYear() - 1970
const months = diffDate.getMonth()
const days = diffDate.getDate() - 1
const segments = [
years && pluralize(years, 'yr'),
months && pluralize(months, 'mo'),
days && !years && !months && pluralize(days, 'day'),
].filter(Boolean)
if (!segments.length) return
const duration = document.createElement('time')
duration.classList.add('duration')
duration.textContent = '· '.concat(segments.join(' '))
this.append(' ', duration)
}
}
customElements.define('time-duration', TimeDuration)
`, E = ':root{color-scheme:light dark;--color-background-light: #ffffff;--color-dimmed-light: #f3f4f5;--color-primary-light: #191e23;--color-secondary-light: #6c7781;--color-tertiary-light: #d2d8dd;--color-accent-light: #0073aa;--color-background-dark: #191e23;--color-dimmed-dark: #23282d;--color-primary-dark: #fbfbfc;--color-secondary-dark: #989fa5;--color-tertiary-dark: #515357;--color-accent-dark: #00a0d2;--color-background: var(--color-background-light);--color-dimmed: var(--color-dimmed-light);--color-primary: var(--color-primary-light);--color-secondary: var(--color-secondary-light);--color-tertiary: var(--color-tertiary-light);--color-accent: var(--color-accent-light);--scale-ratio: 1.25;--scale0: 1rem;--scale1: calc(var(--scale0) * var(--scale-ratio));--scale2: calc(var(--scale1) * var(--scale-ratio));--scale3: calc(var(--scale2) * var(--scale-ratio));--scale4: calc(var(--scale3) * var(--scale-ratio));--scale5: calc(var(--scale4) * var(--scale-ratio))}@media (prefers-color-scheme: dark){:root{--color-background: var(--color-background-dark);--color-dimmed: var(--color-dimmed-dark);--color-primary: var(--color-primary-dark);--color-secondary: var(--color-secondary-dark);--color-tertiary: var(--color-tertiary-dark);--color-accent: var(--color-accent-dark)}}@media print{:root{--scale-ratio: 1.15}}*{box-sizing:border-box;margin:0;padding:0}html{font-size:14px}body{background:var(--color-background);color:var(--color-primary);display:grid;font:1em/1.3 Lato,sans-serif;gap:2em;grid-template-columns:[full-start] 1fr [main-start side-left-start content-start] minmax(min-content,18em) [side-left-end] minmax(min-content,20em) [content-end side-right-start] minmax(min-content,18em) [main-end side-right-end] 1fr [full-end];grid-template-rows:auto [content] 0;margin-bottom:4em}body:before{content:"";grid-column:full;grid-row:content}ol,ul{padding-left:1em}li::marker,.network{color:var(--color-secondary)}a{color:inherit}a:focus,a:hover{color:var(--color-accent)}.discrete-link>a{text-decoration:none}h1{font-weight:400}h1,h2,h3{line-height:1}h1>a,h2>a,h3>a,h4>a,h5>a,h6>a{text-decoration:none}h1{font-size:var(--scale5)}h2{color:var(--color-secondary);font-size:var(--scale4);font-weight:100}h3{color:var(--color-secondary);font-size:var(--scale3);margin-bottom:1rem;font-weight:100}h4{font-size:var(--scale2)}h5{font-size:var(--scale1)}h6{font-size:var(--scale0)}blockquote{border-left:.2em solid var(--color-dimmed);padding-left:1em}cite{color:var(--color-secondary);font-style:inherit}cite:before{content:"— "}svg{margin-right:.2em;vertical-align:text-bottom}.color-primary{color:var(--color-primary)}.material-icons,.zmdi{font-size:1.2em}.icon-item{display:flex;align-items:center;gap:.25em}.icon-item>a{text-decoration:none}.masthead{background:var(--color-dimmed);display:inherit;gap:2em;grid-column:full;grid-template-columns:inherit;padding:3.5em 0 2.5em;row-gap:1.5em;margin-bottom:-2em}.masthead>*{grid-column:main}.masthead>img{border:4px solid;border-radius:50%;margin:0 auto;max-width:12em}.masthead .title-row{display:flex;justify-content:space-between;align-items:center}article>*+*,blockquote>*+*,.timeline>div>*+*{margin-top:.6em}.main-content,.side-content{grid-column:main;display:grid;row-gap:3em}#work article .single .timeline{margin-top:0}#work article .single header{display:flex;justify-content:space-between;align-items:center}.meta{color:var(--color-secondary)}.stack{display:grid;gap:1.5em}.icon-list{display:flex;flex-wrap:wrap;gap:.4em 1.5em;justify-content:flex-start;list-style:none;padding:0}.grid-list{display:grid;gap:1em}.tag-list{display:flex;flex-wrap:wrap;gap:.4em;list-style:none;padding:0}.tag-list>li{background:var(--color-dimmed);border-radius:.7em;padding:.15em .4em}.timeline>div{position:relative}.timeline>div:not(:last-child){padding-bottom:1rem}.timeline>div:not(:last-child):before{content:"";position:absolute;top:2rem;left:-15px;width:2px;height:calc(100% - 2.4rem);background:var(--color-tertiary)}.timeline>div:not(:only-child):after{content:"";position:absolute;top:.3rem;left:-20px;width:.6rem;height:.6rem;background:var(--color-secondary);border:2px solid var(--color-background);border-radius:50%}.print-only{display:none}@media print,(min-width: 48em){h3{padding-bottom:.4em;border-bottom:1px solid var(--color-dimmed);margin-bottom:1em}.masthead img{grid-column:side-left;grid-row:span 2;max-width:100%}.icon-list{flex-direction:row}.grid-list{grid-template-columns:repeat(auto-fit,minmax(calc((100% - 1em)/2),1fr))}}@media print{html{font-size:12px}.masthead{row-gap:1em;padding:3em 0 2em}.main-content{grid-column:content;align-self:start}.side-content{grid-column:side-right;align-self:start}.side-content .grid-list{grid-template-columns:auto}.print-only{display:block}.page-break-before{margin-top:4em;page-break-before:always}}';
function P(t = {}) {
const i = t.themeOptions?.colors;
return i && Object.entries(i).map(([o, [n, r = n]]) => `--color-${o}-light:${n}; --color-${o}-dark:${r};`).join(" ");
}
function m(t, i = !1) {
const o = (
/** @type {string} */
j(t)
);
return i ? C(o) : o;
}
function y(t) {
return `<i class="zmdi zmdi-${t.toLowerCase()}"></i>`;
}
const R = (t) => t.replace(/^(https?:|)\/\//, "").replace(/\/$/, "");
function $(t, i) {
return i ? t ? e`<a href="${t}">${i}</a>` : i : t && e`<a href="${t}">${R(t)}</a>`;
}
const S = (t) => Intl.DisplayNames ? new Intl.DisplayNames(["en"], { type: "region" }).of(t) : t;
function q(t = {}) {
const { email: i, image: o, label: n, location: r, name: a, phone: l, profiles: s = [], summary: c, url: p } = t;
return e`
<header class="masthead">
${o && e`<img src="${o}" alt="" />`}
<div>
<div class="title-row">
${a && e`<h1>${a}</h1>`}
${r?.city && e`
<div class="icon-item">
${y("pin")}
<span>${r.city}${r.countryCode && e`, ${S(r.countryCode)}`}</span>
</div>
`}
</div>
${n && e`<h2>${n}</h2>`}
</div>
${c && e`<article>${m(c)}</article>`}
<ul class="icon-list">
${i && e`
<li class="icon-item">
${y("email")}
<a href="mailto:${i}">${i}</a>
</li>
`}
${l && e`
<li class="icon-item">
${y("smartphone")}
<a href="tel:${l.replace(/\s/g, "")}">${l}</a>
</li>
`}
${p && e`<li class="icon-item">${y("link")} ${$(p)}</li>`}
${s.map(
({ network: d, url: g, username: u }) => e`
<li class="icon-item">${d && y(d)} ${$(g, u)}</li>
`
)}
</ul>
</header>
`;
}
function A(t = {}) {
const { name: i, summary: o } = t;
return e`
${i && e`<title>${i}</title>`}
${o && e`<meta name="description" content="${m(o, !0)}" />`}
`;
}
function h(t = "", i = "", o = "", n = !1) {
return o.length > 0 && e`
<article${n ? ' class="page-break-before"' : ""}>
<header>
<h4>${t}</h4>
${i.length > 0 ? e`<div class="meta">${i}</div>` : ""}
</header>
${o}
</article>
`;
}
const F = (t) => new Date(t).toLocaleDateString("en", {
month: "short",
year: "numeric",
timeZone: "UTC"
});
function v(t) {
return e`<time datetime="${t}">${F(t)}</time>`;
}
function f(t = "", i = "", o = "", n = !1) {
return o.length > 0 && e`
<section ${i.length > 0 ? `id="${i}"` : ""}${n ? ' class="page-break-before"' : ""}>
<h3>${t}</h3>
${o}
</section>
`;
}
function M(t = []) {
const i = t[0];
return t.length > 0 && f(
"Awards",
"awards",
e`
<div class="stack">
${t.map(
({ awarder: o, date: n, summary: r, title: a, breakBefore: l }, s) => h(
a,
e`${o && e`<div>Awarded by <strong>${o}</strong></div>`} ${n && v(n)}`,
e`${r && m(r)}`,
!!l && s > 0
)
)}
</div>
`,
!!i?.breakBefore
);
}
function N(t = []) {
const i = t[0];
return t.length > 0 && f(
"Certificates",
"certificates",
e`
<div class="stack">
${t.map(
({ date: o, issuer: n, name: r, url: a, breakBefore: l }, s) => h(
$(a, r),
`${n && e`<div>Issued by <strong>${n}</strong></div>`} ${o && v(o)}`,
"",
!!l && s > 0
)
)}
</div>
`,
!!i?.breakBefore
);
}
function k(t, i) {
return i === t ? v(i) : e`<time-duration>${v(t)} – ${i ? v(i) : "Present"}</time-duration>`;
}
function O(t = []) {
const i = t[0];
return t.length > 0 && f(
"Education",
"education",
e`
<div class="stack">
${t.map(
({ area: o, courses: n = [], institution: r, startDate: a, endDate: l, studyType: s, url: c, breakBefore: p }, d) => h(
$(c, r),
e`<span>
<strong class="color-primary">${s}</strong>
<span>${o && e`· ${o}`}</span>
</span>
<div>${a && e`<div>${k(a, l)}</div>`}</div>`,
e`
${n.length > 0 && e`
<ul>
${n.map((g) => e`<li>${m(g)}</li>`)}
</ul>
`}
`,
!!p && d > 0
)
)}
</div>
`,
!!i?.breakBefore
);
}
function W(t = []) {
const i = t[0];
return t.length > 0 && f(
"Interests",
"interests",
e`
<div class="grid-list">
${t.map(
({ keywords: o = [], name: n }) => e`
<div>
${n && e`<h5>${n}</h5>`}
${o.length > 0 && e`
<ul class="tag-list">
${o.map((r) => e`<li>${r}</li>`)}
</ul>
`}
</div>
`
)}
</div>
`,
!!i?.breakBefore
);
}
function H(t = []) {
const i = t[0];
return t.length > 0 && f(
"Languages",
"languages",
e`
<div class="grid-list">
${t.map(
({ fluency: o, language: n }) => e`<div>${n && e`<h5>${n}</h5>`} <span class="meta">${o}</span></div>`
)}
</div>
`,
!!i?.breakBefore
);
}
const U = (t) => Intl.ListFormat ? new Intl.ListFormat("en").format(t) : t.join(", ");
function V(t = []) {
const i = t[0];
return t.length > 0 && f(
"Projects",
"projects",
e` <div class="stack">
${t.map(
({
breakBefore: o,
description: n,
entity: r,
highlights: a = [],
keywords: l = [],
name: s,
startDate: c,
endDate: p,
roles: d = [],
type: g,
url: u
}, w) => h(
$(u, s),
e`<div>
${d.length > 0 && e`<strong>${U(d)}</strong>`}
${r && e`at <strong>${r}</strong>`}
</div>
${c && e`<div>${k(c, p)}</div>`} ${g && e`<div>${g}</div>`}`,
e`
${n && m(n)}
${a.length > 0 && e`
<ul>
${a.map((b) => e`<li>${m(b)}</li>`)}
</ul>
`}
${l.length > 0 && e`
<ul class="tag-list">
${l.map((b) => e`<li>${b}</li>`)}
</ul>
`}
`,
!!o && w > 0
)
)}
</div>`,
!!i?.breakBefore
);
}
function Y(t = []) {
const i = t[0];
return t.length > 0 && f(
"Publications",
"publications",
e`
<div class="stack">
${t.map(
({ name: o, publisher: n, releaseDate: r, summary: a, url: l, breakBefore: s }, c) => h(
$(l, o),
e`<div>
${n && e`<div>Published by <strong>${n}</strong></div>`}
${r && v(r)}
</div>`,
e`${a && m(a)}`,
!!s && c > 0
)
)}
</div>
`,
!!i?.breakBefore
);
}
function Z(t = []) {
const i = t[0];
return t.length > 0 && f(
"References",
"references",
e`
<div class="stack">
${t.map(
({ name: o, reference: n }) => e`
<blockquote>
${n && m(n)}
${o && e`
<p>
<cite>${o}</cite>
</p>
`}
</blockquote>
`
)}
</div>
`,
!!i?.breakBefore
);
}
function G(t = []) {
const i = t[0];
return t.length > 0 && f(
"Skills",
"skills",
e`
<div class="grid-list">
${t.map(
({ keywords: o = [], name: n }) => e`
<div>
${n && e`<h5>${n}</h5>`}
${o.length > 0 && e`
<ul class="tag-list">
${o.map((r) => e`<li>${r}</li>`)}
</ul>
`}
</div>
`
)}
</div>
`,
!!i?.breakBefore
);
}
function J(t = []) {
const i = t[0];
return t.length > 0 && f(
"Volunteer",
"volunteer",
e`
<div class="stack">
${t.map(
({ highlights: o = [], organization: n, position: r, startDate: a, endDate: l, summary: s, url: c, breakBefore: p }, d) => h(
$(c, n),
e`<div>
<strong>${r}</strong>
${a && e`<div>${k(a, l)}</div>`}
</div>`,
e`
${s && m(s)}
${o.length > 0 && e`
<ul>
${o.map((g) => e`<li>${m(g)}</li>`)}
</ul>
`}
`,
!!p && d > 0
)
)}
</div>
`,
!!i?.breakBefore
);
}
function K({ breakBefore: t, description: i, name: o, url: n, items: r = [] }) {
if (r.length === 0) return !1;
const a = r.length === 1, l = r[0], s = $(n, o), c = a ? l.position : s, p = a ? e`
<span>
<strong class="discrete-link color-primary">${s}</strong>
${l.location && e`<span>${s && "· "}${l.location}</span>`}
</span>
<div>${l.startDate && k(l.startDate, l.endDate)}</div>
` : i;
return h(
c,
p,
e`
<div${a ? ' class="single"' : ""}>
<div class="timeline">
${r.map(
({ highlights: d = [], location: g, summary: u, startDate: w, endDate: b, position: D }) => e`
<div>
<div>
${!a && e`<h5>${D}</h5>`}
<span>
<span class="meta">${g && !a ? g : ""} </span>
</span>
<div class="meta">
${w && !a && e`<div>${k(w, b)}</div>`}
</div>
</div>
${u && m(u)}
${d.length > 0 && e`
<ul>
${d.map((B) => e`<li>${m(B)}</li>`)}
</ul>
`}
</div>
`
)}
</div>
</div>
`,
t
);
}
function Q(t = []) {
const i = t.reduce(
(n, { description: r, name: a, url: l, ...s }) => {
const c = n[n.length - 1];
return c && c.name === a && c.description === r && c.url === l ? c.items.push(s) : n.push({ description: r, name: a, url: l, items: [s] }), n;
},
/** @type {NestedWork[]} */
[]
), o = t[0];
return t.length > 0 && f(
"Experience",
"work",
e`<div class="stack">
${i.map(
({ breakBefore: n, ...r }, a) => K({ ...r, breakBefore: !!n && a > 0 })
)}
</div>`,
!!o?.breakBefore
);
}
var z;
function X(t, { css: i, js: o } = {}) {
return e`<!doctype html>
<html lang="en" style="${P(t.meta)}">
<head>
<meta charset="utf-8" />
${A(t.basics)}
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Lato:300,400,700&display=swap" />
<link
rel="stylesheet"
href="https://cdnjs.cloudflare.com/ajax/libs/material-design-iconic-font/2.2.0/css/material-design-iconic-font.min.css"
/>
${i && e`<style>
${i}
</style>`}
${o && e(z || (z = I([`<script type="module">
`, `
<\/script>`])), o)}
</head>
<body>
${q(t.basics)}
<div class="main-content">
${Q(t.work)} ${J(t.volunteer)} ${O(t.education)}
${V(t.projects)} ${M(t.awards)} ${N(t.certificates)}
${Y(t.publications)}
</div>
<div class="side-content">
${G(t.skills)} ${H(t.languages)} ${W(t.interests)}
${Z(t.references)}
</div>
</body>
</html>`;
}
const ot = {
mediaType: "print",
printBackground: !0
}, nt = (t) => X(t, { css: E, js: T });
export {
ot as pdfRenderOptions,
nt as render
};