UNPKG

@bluesialia/jsonresume-theme-bluetime

Version:

A modern, clean TypeScript-based theme for JSON Resume with responsive design and professional styling

654 lines (629 loc) 24.4 kB
var HTMLUtils; (function (HTMLUtils) { function escapeHtml(text) { return text .replace(/&/g, "&amp;") .replace(/</g, "&lt;") .replace(/>/g, "&gt;") .replace(/"/g, "&quot;") .replace(/'/g, "&#39;"); } HTMLUtils.escapeHtml = escapeHtml; function safeText(text) { return text ? escapeHtml(text) : ""; } HTMLUtils.safeText = safeText; function createElement(tagName, className = "", textContent = "", attributes = {}) { let element = `<${tagName}`; if (className) element += ` class="${escapeHtml(className)}"`; for (const [key, value] of Object.entries(attributes)) { element += ` ${escapeHtml(key)}="${escapeHtml(value)}"`; } element += `>${textContent}</${tagName}>`; return element; } HTMLUtils.createElement = createElement; function createSection(title) { let sectionDiv = `<div class="section">`; if (title) { sectionDiv += createElement("h2", "", safeText(title)); } return sectionDiv; } HTMLUtils.createSection = createSection; })(HTMLUtils || (HTMLUtils = {})); var ContentUtils; (function (ContentUtils) { function addContactInfo(email, phone, url, profiles = []) { let contactInfo = `<div class="contact-info">`; if (email) { contactInfo += HTMLUtils.createElement("span", "contact-item", `<i class="fas fa-envelope"></i> ${HTMLUtils.createElement("a", "", HTMLUtils.safeText(email), { href: "mailto:" + email })}`); } if (phone) { contactInfo += HTMLUtils.createElement("span", "contact-item", `<i class="fas fa-phone"></i> ${HTMLUtils.createElement("a", "", HTMLUtils.safeText(phone), { href: "tel:" + phone })}`); } if (url) { contactInfo += HTMLUtils.createElement("span", "contact-item", `<i class="fas fa-globe"></i> ${HTMLUtils.createElement("a", "", HTMLUtils.safeText(url.replace(/^https?:\/\//i, "")), { href: url })}`); } for (const profile of profiles) { if (profile.network && profile.url) { contactInfo += HTMLUtils.createElement("span", "contact-item", `<i class="fa-brands fa-${HTMLUtils.escapeHtml(profile.network.toLowerCase())}"></i> ${HTMLUtils.createElement("a", "", HTMLUtils.safeText(profile.url.replace(/^https?:\/\//i, "")), { href: profile.url })}`); } } contactInfo += `</div>`; return contactInfo; } ContentUtils.addContactInfo = addContactInfo; function addLocation(location) { let locationLine = `<div class="location">`; const locationParts = []; if (location.address) locationParts.push(HTMLUtils.safeText(location.address)); if (location.city) locationParts.push(HTMLUtils.safeText(location.city)); if (location.region) locationParts.push(HTMLUtils.safeText(location.region)); if (location.postalCode) locationParts.push(HTMLUtils.safeText(location.postalCode)); if (location.countryCode) locationParts.push(HTMLUtils.safeText(location.countryCode)); locationLine += locationParts.join(", "); locationLine += `</div>`; return locationLine; } ContentUtils.addLocation = addLocation; function addChips(keywords) { let keywordChips = `<div class="chips">`; for (const keyword of keywords) { keywordChips += HTMLUtils.createElement("span", "chip", HTMLUtils.safeText(keyword)); } keywordChips += `</div>`; return keywordChips; } ContentUtils.addChips = addChips; function addHighlights(highlights) { if (!highlights || !highlights.length) return ``; let highlightList = `<ul class="highlights">`; for (const highlight of highlights) { highlightList += HTMLUtils.createElement("li", "", HTMLUtils.safeText(highlight)); } highlightList += `</ul>`; return highlightList; } ContentUtils.addHighlights = addHighlights; })(ContentUtils || (ContentUtils = {})); const THEME_STYLES = ` <style> @import url('https://fonts.googleapis.com/css2?family=Lato:ital,wght@0,100;0,300;0,400;0,700;0,900;1,100;1,300;1,400;1,700;1,900&display=swap'); @import url('https://cdnjs.cloudflare.com/ajax/libs/font-awesome/7.0.0/css/all.min.css'); .resume-container { display: flex; padding: 20px; max-width: 1600px; margin: auto; font-family: "Lato", sans-serif; font-size: 14px; h1, h2, h3, h4 { margin-top: 0; margin-bottom: 10px; } p { margin: 5px 0 0 0; } .left-column { flex: 1; padding: 10px; background-color: #f4f4f9; border-radius: 12px; } .right-column { flex: 2; padding: 10px 10px 10px 0; } .section { position: relative; margin: 10px; break-inside: avoid; .section-item { margin: 5px; break-inside: avoid; h3 { display: inline-block; margin-right: 10px; } h4 { display: inline-block; color: #666; } } .timeline-section-item { position: relative; display: flex; align-items: flex-start; margin-top: 10px; break-inside: avoid; h3 { display: inline-block; margin: 0; } h4 { margin: 10px 0; } .timeline { position: absolute; width: 65px; text-align: right; font-size: 12px; } .timeline::before { content: ""; position: absolute; left: 70px; /* Adjusted to align with the timeline */ top: 5px; width: 16px; height: 16px; background-color: #007BFF; border-radius: 50%; } .timeline-section-details { position: relative; margin-left: 92px; } .description { color: #666; font-size: 12px; display: inline-block; margin-left: 5px; } .title-anex { color: #666; font-size: 12px; margin-top: 5px; } } .timeline-section-item::before { content: ""; position: absolute; left: 75px; /* Adjusted to align with the timeline */ top: 30px; width: 6px; height: calc(100% - 30px); background-color: #ccc; border-radius: 3px; } .highlights, .courses { padding-left: 15px; margin-top: 5px; margin-bottom: 5px; } .highlights li, .courses li { margin-bottom: 5px; } .chips { display: flex; flex-wrap: wrap; gap: 5px; margin-bottom: 10px; } .chip { background-color: #007BFF; color: white; padding: 4px 8px; border-radius: 20px; font-size: 12px; } } .profile-picture { float: left; margin-right: 20px; width: 100px; height: 100px; border-radius: 50%; object-fit: cover; } @media only screen and (max-width: 1000px) { .profile-picture { display: none; } } @media only print { .profile-picture { display: none; } } .contact-info { display: flex; flex-wrap: wrap; gap: 10px; margin-top: 10px; } .contact-item { text-decoration: none; color: #007BFF; } } </style> `; var SectionCreators; (function (SectionCreators) { function createBasicsSection(basics) { if (!basics) return ""; const { name, label, image, email, phone, url, location = {}, profiles = [], summary, } = basics; let basicsSection = HTMLUtils.createSection(); if (image) { basicsSection += HTMLUtils.createElement("img", "", "", { class: "profile-picture", src: image, alt: "Profile Picture", }); } if (name) { basicsSection += HTMLUtils.createElement("h1", "resume-name", HTMLUtils.safeText(name)); } if (label) { basicsSection += HTMLUtils.createElement("h2", "job-title", HTMLUtils.safeText(label)); } basicsSection += ContentUtils.addLocation(location); basicsSection += ContentUtils.addContactInfo(email, phone, url, profiles); if (summary) { basicsSection += HTMLUtils.createElement("p", "", HTMLUtils.safeText(summary)); } basicsSection += `</div>`; return basicsSection; } SectionCreators.createBasicsSection = createBasicsSection; function createLanguagesSection(languages) { if (!languages || languages.length === 0) return ""; let languagesSection = HTMLUtils.createSection("Languages"); for (const language of languages) { const { language: lang, fluency } = language; let languageItem = `<div class="section-item">`; if (lang) { languageItem += HTMLUtils.createElement("h3", "", HTMLUtils.safeText(lang)); } if (fluency) { languageItem += HTMLUtils.createElement("h4", "subtitle", HTMLUtils.safeText(fluency)); } languageItem += `</div>`; languagesSection += languageItem; } languagesSection += `</div>`; return languagesSection; } SectionCreators.createLanguagesSection = createLanguagesSection; function createSkillsSection(skills) { if (!skills || skills.length === 0) return ""; let skillsSection = HTMLUtils.createSection("Skills"); for (const skill of skills) { const { name, level, keywords } = skill; let skillItem = `<div class="section-item">`; if (name) { skillItem += HTMLUtils.createElement("h3", "", HTMLUtils.safeText(name)); } if (level) { skillItem += HTMLUtils.createElement("h4", "subtitle", HTMLUtils.safeText(level)); } if (keywords && keywords.length > 0) { skillItem += ContentUtils.addChips(keywords); } skillItem += `</div>`; skillsSection += skillItem; } skillsSection += `</div>`; return skillsSection; } SectionCreators.createSkillsSection = createSkillsSection; function createInterestsSection(interests) { if (!interests || interests.length === 0) return ""; let interestsSection = HTMLUtils.createSection("Interests"); for (const interest of interests) { const { name, keywords } = interest; let interestItem = `<div class="section-item">`; if (name) { interestItem += HTMLUtils.createElement("h3", "", HTMLUtils.safeText(name)); } if (keywords && keywords.length > 0) { interestItem += ContentUtils.addChips(keywords); } interestItem += `</div>`; interestsSection += interestItem; } interestsSection += `</div>`; return interestsSection; } SectionCreators.createInterestsSection = createInterestsSection; function createReferencesSection(references) { if (!references || references.length === 0) return ""; let referencesSection = `<div class="section references">`; referencesSection += HTMLUtils.createElement("h2", "", "References"); for (const ref of references) { const { name, reference } = ref; let referenceItem = `<div class="section-item">`; if (name) { referenceItem += HTMLUtils.createElement("h3", "", HTMLUtils.safeText(name)); } if (reference) { try { new URL(reference); referenceItem += HTMLUtils.createElement("a", "reference-link", HTMLUtils.safeText(reference.replace(/^https?:\/\//i, "")), { href: reference }); } catch (e) { referenceItem += HTMLUtils.createElement("p", "", HTMLUtils.safeText(reference)); } } referenceItem += `</div>`; referencesSection += referenceItem; } referencesSection += `</div>`; return referencesSection; } SectionCreators.createReferencesSection = createReferencesSection; function createExperienceSection(experiences) { if (!experiences || experiences.length === 0) return ""; let experienceSection = HTMLUtils.createSection("Experience"); for (const experience of experiences) { const { name, description, location, url, position, summary, highlights, startDate, endDate, } = experience; let experienceItem = `<div class="timeline-section-item">`; if (startDate || endDate) { let timeline = `<div class="timeline">`; if (endDate) { timeline += HTMLUtils.createElement("span", "date", HTMLUtils.safeText(endDate)); } else { timeline += HTMLUtils.createElement("span", "date", "Present"); } if (startDate) { timeline += ` <br> `; timeline += HTMLUtils.createElement("span", "date", HTMLUtils.safeText(startDate)); } timeline += `</div>`; experienceItem += timeline; } experienceItem += `<div class="timeline-section-details">`; if (name) { experienceItem += HTMLUtils.createElement("h3", "", HTMLUtils.safeText(name)); } if (description) { experienceItem += HTMLUtils.createElement("span", "description", HTMLUtils.safeText(description)); } if (location || url) { let locationUrl = `<div class="title-anex">`; if (location) { locationUrl += HTMLUtils.createElement("span", "", HTMLUtils.safeText(location)); } if (url) { locationUrl += ` &bull; `; locationUrl += HTMLUtils.createElement("a", "url", HTMLUtils.safeText(url.replace(/^https?:\/\//i, "")), { href: url }); } locationUrl += `</div>`; experienceItem += locationUrl; } if (position) { experienceItem += HTMLUtils.createElement("h4", "subtitle", HTMLUtils.safeText(position)); } if (summary) { experienceItem += HTMLUtils.createElement("p", "", HTMLUtils.safeText(summary)); } if (highlights && highlights.length > 0) { experienceItem += ContentUtils.addHighlights(highlights); } experienceItem += `</div>`; experienceItem += `</div>`; experienceSection += experienceItem; } experienceSection += `</div>`; return experienceSection; } SectionCreators.createExperienceSection = createExperienceSection; function createProjectsSection(projects) { if (!projects || projects.length === 0) return ""; let projectsSection = `<div class="section">`; projectsSection += HTMLUtils.createElement("h2", "", "Projects"); for (const project of projects) { const { name, url, description, highlights, keywords, startDate, endDate, } = project; let projectItem = `<div class="timeline-section-item">`; if (startDate || endDate) { let timeline = `<div class="timeline">`; if (endDate) { timeline += HTMLUtils.createElement("span", "date", HTMLUtils.safeText(endDate)); } else { timeline += HTMLUtils.createElement("span", "date", "Present"); } if (startDate) { timeline += ` <br> `; timeline += HTMLUtils.createElement("span", "date", HTMLUtils.safeText(startDate)); } timeline += `</div>`; projectItem += timeline; } projectItem += `<div class="timeline-section-details">`; if (name) { projectItem += HTMLUtils.createElement("h3", "", HTMLUtils.safeText(name)); } if (url) { projectItem += ` &bull; `; projectItem += HTMLUtils.createElement("a", "description", HTMLUtils.safeText(url.replace(/^https?:\/\//i, "")), { href: url, }); } if (description) { projectItem += HTMLUtils.createElement("p", "", HTMLUtils.safeText(description)); } if (highlights && highlights.length > 0) { let highlightList = `<ul class="highlights">`; for (const highlight of highlights) { highlightList += HTMLUtils.createElement("li", "", HTMLUtils.safeText(highlight)); } highlightList += `</ul>`; projectItem += highlightList; } if (keywords && keywords.length > 0) { projectItem += ContentUtils.addChips(keywords); } projectItem += `</div>`; projectItem += `</div>`; projectsSection += projectItem; } projectsSection += `</div>`; return projectsSection; } SectionCreators.createProjectsSection = createProjectsSection; function createVolunteerSection(volunteer) { if (!volunteer || volunteer.length === 0) return ""; let volunteerSection = HTMLUtils.createSection("Volunteer"); for (const vol of volunteer) { const { organization, url, position, summary, highlights, startDate, endDate, } = vol; let volItem = `<div class="timeline-section-item">`; if (startDate || endDate) { let timeline = `<div class="timeline">`; if (endDate) { timeline += HTMLUtils.createElement("span", "date", HTMLUtils.safeText(endDate)); } else { timeline += HTMLUtils.createElement("span", "date", "Present"); } if (startDate) { timeline += ` <br> `; timeline += HTMLUtils.createElement("span", "date", HTMLUtils.safeText(startDate)); } timeline += `</div>`; volItem += timeline; } volItem += `<div class="timeline-section-details">`; if (organization) { volItem += HTMLUtils.createElement("h3", "", HTMLUtils.safeText(organization)); } if (url) { volItem += HTMLUtils.createElement("a", "description", HTMLUtils.safeText(url.replace(/^https?:\/\//i, "")), { href: url }); } if (position) { volItem += HTMLUtils.createElement("div", "title-anex", HTMLUtils.safeText(position)); } if (summary) { volItem += HTMLUtils.createElement("p", "", HTMLUtils.safeText(summary)); } if (highlights && highlights.length > 0) { volItem += ContentUtils.addHighlights(highlights); } volItem += `</div>`; volItem += `</div>`; volunteerSection += volItem; } volunteerSection += `</div>`; return volunteerSection; } SectionCreators.createVolunteerSection = createVolunteerSection; function createEducationSection(education) { if (!education || education.length === 0) return ""; let educationSection = HTMLUtils.createSection("Education"); for (const edu of education) { const { institution, url, area, studyType, courses, startDate, endDate } = edu; let eduItem = `<div class="timeline-section-item">`; if (startDate || endDate) { let timeline = `<div class="timeline">`; if (endDate) { timeline += HTMLUtils.createElement("span", "date", HTMLUtils.safeText(endDate)); } else { timeline += HTMLUtils.createElement("span", "date", "Present"); } if (startDate) { timeline += ` <br> `; timeline += HTMLUtils.createElement("span", "date", HTMLUtils.safeText(startDate)); } timeline += `</div>`; eduItem += timeline; } eduItem += `<div class="timeline-section-details">`; if (institution) { eduItem += HTMLUtils.createElement("h3", "", HTMLUtils.safeText(institution)); } if (url) { eduItem += HTMLUtils.createElement("a", "description", HTMLUtils.safeText(url.replace(/^https?:\/\//i, "")), { href: url }); } if (area || studyType) { let areaStudy = `<div class="title-anex">`; if (area) { areaStudy += HTMLUtils.createElement("span", "", HTMLUtils.safeText(area)); } if (studyType) { areaStudy += HTMLUtils.createElement("span", "description", HTMLUtils.safeText(studyType)); } areaStudy += `</div>`; eduItem += areaStudy; } if (courses && courses.length > 0) { let courseList = `<ul class="courses">`; for (const course of courses) { courseList += HTMLUtils.createElement("li", "", HTMLUtils.safeText(course)); } courseList += `</ul>`; eduItem += courseList; } eduItem += `</div>`; eduItem += `</div>`; educationSection += eduItem; } educationSection += `</div>`; return educationSection; } SectionCreators.createEducationSection = createEducationSection; })(SectionCreators || (SectionCreators = {})); function createLeftColumn(data) { let leftColumn = `<div class="left-column">`; if (data.basics) { leftColumn += SectionCreators.createBasicsSection(data.basics); } if (data.languages) { leftColumn += SectionCreators.createLanguagesSection(data.languages); } if (data.skills) { leftColumn += SectionCreators.createSkillsSection(data.skills); } if (data.interests) { leftColumn += SectionCreators.createInterestsSection(data.interests); } if (data.references) { leftColumn += SectionCreators.createReferencesSection(data.references); } leftColumn += `</div>`; return leftColumn; } function createRightColumn(data) { let rightColumn = `<div class="right-column">`; if (data.work) { rightColumn += SectionCreators.createExperienceSection(data.work); } if (data.projects) { rightColumn += SectionCreators.createProjectsSection(data.projects); } if (data.volunteer) { rightColumn += SectionCreators.createVolunteerSection(data.volunteer); } if (data.education) { rightColumn += SectionCreators.createEducationSection(data.education); } rightColumn += `</div>`; return rightColumn; } export function render(data) { let resumeContainer = `<div class="resume-container">`; resumeContainer += THEME_STYLES; resumeContainer += createLeftColumn(data); resumeContainer += createRightColumn(data); resumeContainer += `</div>`; return resumeContainer; } //# sourceMappingURL=index.js.map