UNPKG

jsonresume-theme-crewshin

Version:

A flat JSON Resume theme, compatible with the latest resume schema

449 lines (438 loc) 16.5 kB
var y = Object.freeze, w = Object.defineProperty; var b = (t, i) => y(w(t, "raw", { value: y(i || t.slice()) })); import { html as e } from "@rbardini/html"; import D from "micromark"; import L from "striptags"; import k from "feather-icons"; const z = `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) `, C = ':root{color-scheme:light dark;--color-background-light: #ffffff;--color-dimmed-light: #f3f4f5;--color-primary-light: #191e23;--color-secondary-light: #6c7781;--color-accent-light: #0073aa;--color-background-dark: #191e23;--color-dimmed-dark: #23282d;--color-primary-dark: #fbfbfc;--color-secondary-dark: #ccd0d4;--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-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-accent: var(--color-accent-dark)}}*{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.5 Lato,sans-serif;gap:2em;grid-template-columns:[full-start] 1fr [main-start side-start] minmax(min-content,12em) [side-end content-start] minmax(min-content,36em) [main-end content-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}:not(.icon-list,.tag-list)>li+li{margin-top:.4em}li::marker,.network{color:var(--color-secondary)}a{color:var(--color-accent);text-decoration:none}a:focus,a:hover{text-decoration:underline}h1,h2,h3,h5{font-weight:400}h1,h2,h3{line-height:1.2}h1{font-size:var(--scale5)}h2{color:var(--color-secondary);font-size:var(--scale4)}h3{color:var(--color-secondary);font-size:var(--scale3);grid-column:side;margin-bottom:1rem}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}.masthead{background:var(--color-dimmed);display:inherit;gap:inherit;grid-column:full;grid-template-columns:inherit;padding:4em 0;text-align:center}.masthead>*,section{grid-column:main}.masthead>img{border:4px solid;border-radius:50%;margin:0 auto;max-width:12em}article>*+*,blockquote>*+*,.timeline>div>*+*{margin-top:.6em}.meta{color:var(--color-secondary)}.stack{display:grid;gap:1.5em}.icon-list{display:flex;flex-wrap:wrap;gap:.4em 1em;justify-content:center;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:.2em;padding:.2em .6em}.timeline>div{position:relative}.timeline>div:not(:last-child){padding-bottom:1rem}.timeline>div:not(:last-child):before{content:"";position:absolute;top:1rem;left:-15px;width:2px;height:100%;background:var(--color-secondary)}.timeline>div:not(:only-child):after{content:"";position:absolute;top:.6rem;left:-20px;width:8px;height:8px;background:var(--color-secondary);border:2px solid var(--color-background);border-radius:50%}@media print,(min-width: 48em){h3{text-align:right;margin-bottom:inherit}.masthead{text-align:inherit}.masthead>*,section{grid-column:content}.masthead img{grid-column:side;grid-row:span 2;max-width:100%}section{display:contents}.icon-list{flex-direction:column}.grid-list{grid-template-columns:repeat(auto-fit,minmax(calc((100% - 1em)/2),1fr))}}@media print{.duration{display:none}}'; function j(t = {}) { const i = t.themeOptions?.colors; return i && Object.entries(i).map(([o, [n, a = n]]) => `--color-${o}-light:${n}; --color-${o}-dark:${a};`).join(" "); } function s(t, i = !1) { const o = ( /** @type {string} */ D(t) ); return i ? L(o) : o; } const I = (t) => new Date(t).toLocaleDateString("en", { month: "short", year: "numeric", timeZone: "UTC" }); function u(t) { return e`<time datetime="${t}">${I(t)}</time>`; } function T(t = []) { return t.length > 0 && e` <section id="awards"> <h3>Awards</h3> <div class="stack"> ${t.map( ({ awarder: i, date: o, summary: n, title: a }) => e` <article> <header> <h4>${a}</h4> <div class="meta"> ${i && e`<div>Awarded by <strong>${i}</strong></div>`} ${o && u(o)} </div> </header> ${n && s(n)} </article> ` )} </div> </section> `; } const P = (t) => t.replace(/^(https?:|)\/\//, "").replace(/\/$/, ""); function g(t, i) { return i ? t ? e`<a href="${t}">${i}</a>` : i : t && e`<a href="${t}">${P(t)}</a>`; } function R(t = []) { return t.length > 0 && e` <section id="certificates"> <h3>Certificates</h3> <div class="stack"> ${t.map( ({ date: i, issuer: o, name: n, url: a }) => e` <article> <header> <h4>${g(a, n)}</h4> <div class="meta"> ${o && e`<div>Issued by <strong>${o}</strong></div>`} ${i && u(i)} </div> </header> </article> ` )} </div> </section> `; } function p(t, i) { return i === t ? u(i) : e`<time-duration>${u(t)}${i ? u(i) : "Present"}</time-duration>`; } function q(t = []) { return t.length > 0 && e` <section id="education"> <h3>Education</h3> <div class="stack"> ${t.map( ({ area: i, courses: o = [], institution: n, startDate: a, endDate: c, studyType: r, url: l }) => e` <article> <header> <h4>${g(l, n)}</h4> <div class="meta"> ${i && e`<strong>${i}</strong>`} ${a && e`<div>${p(a, c)}</div>`} </div> </header> ${r && s(r)} ${o.length > 0 && e` <h5>Courses</h5> <ul> ${o.map((d) => e`<li>${s(d)}</li>`)} </ul> `} </article> ` )} </div> </section> `; } function v(t, i) { return (k.icons[ /** @type {FeatherIconNames} */ t.toLowerCase() ] || i && k.icons[ /** @type {FeatherIconNames} */ i.toLowerCase() ])?.toSvg({ width: 16, height: 16 }); } const E = (t) => Intl.DisplayNames ? new Intl.DisplayNames(["en"], { type: "region" }).of(t) : t; function S(t = {}) { const { email: i, image: o, label: n, location: a, name: c, phone: r, profiles: l = [], summary: d, url: m } = t; return e` <header class="masthead"> ${o && e`<img src="${o}" alt="" />`} <div>${c && e`<h1>${c}</h1>`} ${n && e`<h2>${n}</h2>`}</div> ${d && e`<article>${s(d)}</article>`} <ul class="icon-list"> ${a?.city && e` <li> ${v("map-pin")} ${a.city}${a.countryCode && e`, ${E(a.countryCode)}`} </li> `} ${i && e` <li> ${v("mail")} <a href="mailto:${i}">${i}</a> </li> `} ${r && e` <li> ${v("phone")} <a href="tel:${r.replace(/\s/g, "")}">${r}</a> </li> `} ${m && e`<li>${v("link")} ${g(m)}</li>`} ${l.map( ({ network: h, url: $, username: f }) => e` <li> ${h && v(h, "user")} ${g($, f)} ${h && e`<span class="network">(${h})</span>`} </li> ` )} </ul> </header> `; } function A(t = []) { return t.length > 0 && e` <section id="interests"> <h3>Interests</h3> <div class="grid-list"> ${t.map( ({ keywords: i = [], name: o }) => e` <div> ${o && e`<h4>${o}</h4>`} ${i.length > 0 && e` <ul class="tag-list"> ${i.map((n) => e`<li>${n}</li>`)} </ul> `} </div> ` )} </div> </section> `; } function F(t = []) { return t.length > 0 && e` <section id="languages"> <h3>Languages</h3> <div class="grid-list"> ${t.map( ({ fluency: i, language: o }) => e`<div>${o && e`<h4>${o}</h4>`} ${i}</div>` )} </div> </section> `; } function M(t = {}) { const { name: i, summary: o } = t; return e` ${i && e`<title>${i}</title>`} ${o && e`<meta name="description" content="${s(o, !0)}" />`} `; } const O = (t) => Intl.ListFormat ? new Intl.ListFormat("en").format(t) : t.join(", "); function W(t = []) { return t.length > 0 && e` <section id="projects"> <h3>Projects</h3> <div class="stack"> ${t.map( ({ description: i, entity: o, highlights: n = [], keywords: a = [], name: c, startDate: r, endDate: l, roles: d = [], type: m, url: h }) => e` <article> <header> <h4>${g(h, c)}</h4> <div class="meta"> <div> ${d.length > 0 && e`<strong>${O(d)}</strong>`} ${o && e`at <strong>${o}</strong>`} </div> ${r && e`<div>${p(r, l)}</div>`} ${m && e`<div>${m}</div>`} </div> </header> ${i && s(i)} ${n.length > 0 && e` <ul> ${n.map(($) => e`<li>${s($)}</li>`)} </ul> `} ${a.length > 0 && e` <ul class="tag-list"> ${a.map(($) => e`<li>${$}</li>`)} </ul> `} </article> ` )} </div> </section> `; } function B(t = []) { return t.length > 0 && e` <section id="publications"> <h3>Publications</h3> <div class="stack"> ${t.map( ({ name: i, publisher: o, releaseDate: n, summary: a, url: c }) => e` <article> <header> <h4>${g(c, i)}</h4> <div class="meta"> ${o && e`<div>Published by <strong>${o}</strong></div>`} ${n && u(n)} </div> </header> ${a && s(a)} </article> ` )} </div> </section> `; } function H(t = []) { return t.length > 0 && e` <section id="references"> <h3>References</h3> <div class="stack"> ${t.map( ({ name: i, reference: o }) => e` <blockquote> ${o && s(o)} ${i && e` <p> <cite>${i}</cite> </p> `} </blockquote> ` )} </div> </section> `; } function N(t = []) { return t.length > 0 && e` <section id="skills"> <h3>Skills</h3> <div class="grid-list"> ${t.map( ({ keywords: i = [], name: o }) => e` <div> ${o && e`<h4>${o}</h4>`} ${i.length > 0 && e` <ul class="tag-list"> ${i.map((n) => e`<li>${n}</li>`)} </ul> `} </div> ` )} </div> </section> `; } function U(t = []) { return t.length > 0 && e` <section id="volunteer"> <h3>Volunteer</h3> <div class="stack"> ${t.map( ({ highlights: i = [], organization: o, position: n, startDate: a, endDate: c, summary: r, url: l }) => e` <article> <header> <h4>${g(l, o)}</h4> <div class="meta"> <strong>${n}</strong> ${a && e`<div>${p(a, c)}</div>`} </div> </header> ${r && s(r)} ${i.length > 0 && e` <ul> ${i.map((d) => e`<li>${s(d)}</li>`)} </ul> `} </article> ` )} </div> </section> `; } function V(t = []) { const i = t.reduce( (o, { description: n, name: a, url: c, ...r }) => { const l = o[o.length - 1]; return l && l.name === a && l.description === n && l.url === c ? l.items.push(r) : o.push({ description: n, name: a, url: c, items: [r] }), o; }, /** @type {NestedWork[]} */ [] ); return t.length > 0 && e` <section id="work"> <h3>Work</h3> <div class="stack"> ${i.map( ({ description: o, name: n, url: a, items: c = [] }) => e` <article> <header> <h4>${g(a, n)}</h4> <div class="meta">${o && e`<div>${o}</div>`}</div> </header> <div class="timeline"> ${c.map( ({ highlights: r = [], location: l, position: d, startDate: m, endDate: h, summary: $ }) => e` <div> <div> <h5>${d}</h5> <div class="meta"> ${m && e`<div>${p(m, h)}</div>`} ${l && e`<div>${l}</div>`} </div> </div> ${$ && s($)} ${r.length > 0 && e` <ul> ${r.map((f) => e`<li>${s(f)}</li>`)} </ul> `} </div> ` )} </div> </article> ` )} </div> </section> `; } var x; function Y(t, { css: i, js: o } = {}) { return e`<!doctype html> <html lang="en" style="${j(t.meta)}"> <head> <meta charset="utf-8" /> ${M(t.basics)} <meta name="viewport" content="width=device-width, initial-scale=1" /> <link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Lato:400,700&display=swap" /> ${i && e`<style> ${i} </style>`} ${o && e(x || (x = b([`<script type="module"> `, ` <\/script>`])), o)} </head> <body> ${S(t.basics)} ${V(t.work)} ${U(t.volunteer)} ${q(t.education)} ${W(t.projects)} ${T(t.awards)} ${R(t.certificates)} ${B(t.publications)} ${N(t.skills)} ${F(t.languages)} ${A(t.interests)} ${H(t.references)} </body> </html>`; } const X = { mediaType: "print", printBackground: !0 }, _ = (t) => Y(t, { css: C, js: z }); export { X as pdfRenderOptions, _ as render };