Compare commits
1 Commits
| Author | SHA1 | Date |
|---|---|---|
|
|
0c4cec14e1 |
|
|
@ -250,7 +250,6 @@ See [Does JavaScript guarantee object property order? - Stack Overflow](https://
|
||||||
- [x] Allows customizing primary color for the current theme
|
- [x] Allows customizing primary color for the current theme
|
||||||
- [x] Export PDF directly (using browser's print feature)
|
- [x] Export PDF directly (using browser's print feature)
|
||||||
- [x] Supports responsive style for themes, so that the CV site is friendly to view on mobile devices.
|
- [x] Supports responsive style for themes, so that the CV site is friendly to view on mobile devices.
|
||||||
- [ ] i18n and language switcher
|
|
||||||
- [ ] Add more themes.
|
- [ ] Add more themes.
|
||||||
- [ ] Allows switching themes in Editor
|
- [ ] Allows switching themes in Editor
|
||||||
- [ ] Add more sample data. By clicking the "Load Sample" button, a dialog will open, allowing the user to select from various languages
|
- [ ] Add more sample data. By clicking the "Load Sample" button, a dialog will open, allowing the user to select from various languages
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -31,7 +31,6 @@
|
||||||
"ejs": "^3.1.8",
|
"ejs": "^3.1.8",
|
||||||
"iconify-icon": "^1.0.3",
|
"iconify-icon": "^1.0.3",
|
||||||
"markdown-it": "^13.0.1",
|
"markdown-it": "^13.0.1",
|
||||||
"node-polyglot": "^2.5.0",
|
|
||||||
"object-path": "^0.11.8"
|
"object-path": "^0.11.8"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -280,8 +280,6 @@
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"meta": {
|
"meta": {
|
||||||
"name": "JSONCV Sample Resume",
|
|
||||||
"locale": "en",
|
|
||||||
"canonical": "https://raw.githubusercontent.com/reorx/jsoncv/master/schema/jsoncv.schema.json",
|
"canonical": "https://raw.githubusercontent.com/reorx/jsoncv/master/schema/jsoncv.schema.json",
|
||||||
"version": "v2.0.0",
|
"version": "v2.0.0",
|
||||||
"lastModified": "2023-02-12T22:26:00"
|
"lastModified": "2023-02-12T22:26:00"
|
||||||
|
|
|
||||||
|
|
@ -509,10 +509,6 @@
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"description": "The name of this JSONCV data, will be used as part of the filename when downloading."
|
"description": "The name of this JSONCV data, will be used as part of the filename when downloading."
|
||||||
},
|
},
|
||||||
"locale": {
|
|
||||||
"type": "string",
|
|
||||||
"description": "Locale of the CV, used by the theme to localize the CV. The value must be a valid IETF language tag. Default to 'en' if not specified. A full list of locale tag can be found at: https://cdn.jsdelivr.net/npm/dayjs@1/locale.json"
|
|
||||||
},
|
|
||||||
"canonical": {
|
"canonical": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"description": "URL (as per RFC 3986) to latest version of this document",
|
"description": "URL (as per RFC 3986) to latest version of this document",
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@ import { getCVTitle } from '../themes/data';
|
||||||
import { registerIconLib } from './je-iconlib';
|
import { registerIconLib } from './je-iconlib';
|
||||||
import { registerTheme } from './je-theme';
|
import { registerTheme } from './je-theme';
|
||||||
|
|
||||||
const propertiesInOrder = ['meta', 'basics', 'education', 'work', 'projects', 'sideProjects', 'skills', 'languages', 'interests', 'references', 'awards', 'publications', 'volunteer', 'certificates']
|
const propertiesInOrder = ['basics', 'education', 'work', 'projects', 'sideProjects', 'skills', 'languages', 'interests', 'references', 'awards', 'publications', 'volunteer', 'certificates', 'meta']
|
||||||
const basicsPropertiesInOrder = ['name', 'label', 'email', 'phone', 'url', 'summary', 'image', 'location', 'profiles']
|
const basicsPropertiesInOrder = ['name', 'label', 'email', 'phone', 'url', 'summary', 'image', 'location', 'profiles']
|
||||||
|
|
||||||
// toc elements
|
// toc elements
|
||||||
|
|
@ -90,6 +90,7 @@ const keyFormatMap = {
|
||||||
'projects.items.properties.description': 'textarea',
|
'projects.items.properties.description': 'textarea',
|
||||||
'projects.items.properties.highlights.items': 'textarea',
|
'projects.items.properties.highlights.items': 'textarea',
|
||||||
'sideProjects.items.properties.description': 'textarea',
|
'sideProjects.items.properties.description': 'textarea',
|
||||||
|
'skills.items.properties.summary': 'textarea',
|
||||||
'languages.items.properties.summary': 'textarea',
|
'languages.items.properties.summary': 'textarea',
|
||||||
'references.items.properties.reference': 'textarea',
|
'references.items.properties.reference': 'textarea',
|
||||||
'awards.items.properties.summary': 'textarea',
|
'awards.items.properties.summary': 'textarea',
|
||||||
|
|
|
||||||
|
|
@ -4,11 +4,9 @@ import { renderMarkdown } from '../lib/markdown';
|
||||||
|
|
||||||
export const varNamePrimaryColor = '--color-primary'
|
export const varNamePrimaryColor = '--color-primary'
|
||||||
|
|
||||||
export function getRenderData(cvData, locale, polyglot) {
|
export function getRenderData(cvData) {
|
||||||
return {
|
return {
|
||||||
cv: cvData,
|
cv: cvData,
|
||||||
locale,
|
|
||||||
t: polyglot.t.bind(polyglot),
|
|
||||||
fn: {
|
fn: {
|
||||||
getCVTitle,
|
getCVTitle,
|
||||||
reformatDate,
|
reformatDate,
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,4 @@
|
||||||
import dayjs from 'dayjs';
|
|
||||||
import ejs from 'ejs';
|
import ejs from 'ejs';
|
||||||
import Polyglot from 'node-polyglot';
|
|
||||||
|
|
||||||
import { upsertStyleTag } from '../lib/utils';
|
import { upsertStyleTag } from '../lib/utils';
|
||||||
import {
|
import {
|
||||||
|
|
@ -17,17 +15,13 @@ const themeNames = ['reorx']
|
||||||
// cannot be used because we need vite to transform scss into css
|
// cannot be used because we need vite to transform scss into css
|
||||||
const styleMoudules = import.meta.glob("./*/index.scss", { "query": "?inline" })
|
const styleMoudules = import.meta.glob("./*/index.scss", { "query": "?inline" })
|
||||||
|
|
||||||
console.log('themes index')
|
|
||||||
for (const name of themeNames) {
|
for (const name of themeNames) {
|
||||||
const templateModule = await import(`./${name}/index.ejs`)
|
const templateModule = await import(`./${name}/index.ejs`)
|
||||||
const themeModule = await import(`./${name}/index.js`)
|
|
||||||
console.log(`theme supported locales: ${themeModule.locales}`)
|
|
||||||
|
|
||||||
// https://vitejs.dev/guide/features.html#glob-import
|
// https://vitejs.dev/guide/features.html#glob-import
|
||||||
const styleModule = await styleMoudules[`./${name}/index.scss`]()
|
const styleModule = await styleMoudules[`./${name}/index.scss`]()
|
||||||
|
|
||||||
themes[name] = {
|
themes[name] = {
|
||||||
index: themeModule,
|
|
||||||
template: templateModule.default,
|
template: templateModule.default,
|
||||||
style: styleModule.default,
|
style: styleModule.default,
|
||||||
}
|
}
|
||||||
|
|
@ -40,31 +34,17 @@ export function getTheme(name) {
|
||||||
return themes[name]
|
return themes[name]
|
||||||
}
|
}
|
||||||
|
|
||||||
export function renderTheme(theme, cvData, options) {
|
export function renderTheme(template, cvData, options) {
|
||||||
const locale = cvData.meta.locale || 'en'
|
return ejs.render(template, getRenderData(cvData), options)
|
||||||
const messages = theme.index.localeMessages[locale]
|
|
||||||
if (!messages) {
|
|
||||||
return `Error: locale '${locale}' is not supported, please use one of: ${theme.index.locales}`
|
|
||||||
}
|
|
||||||
const polyglot = new Polyglot({
|
|
||||||
phrases: messages,
|
|
||||||
locale,
|
|
||||||
})
|
|
||||||
dayjs.locale(locale)
|
|
||||||
return ejs.render(theme.template, getRenderData(cvData, locale, polyglot), options)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const cvStyleId = 'cv-style'
|
const cvStyleId = 'cv-style'
|
||||||
|
|
||||||
export function renderThemeOn(name, el, data, primaryColor) {
|
export function renderThemeOn(name, el, data, primaryColor) {
|
||||||
const theme = getTheme(name)
|
const theme = getTheme(name)
|
||||||
el.innerHTML = renderTheme(theme, data)
|
el.innerHTML = renderTheme(theme.template, data)
|
||||||
|
|
||||||
upsertStyleTag(cvStyleId, theme.style)
|
upsertStyleTag(cvStyleId, theme.style)
|
||||||
|
|
||||||
document.documentElement.style.setProperty(varNamePrimaryColor, primaryColor)
|
document.documentElement.style.setProperty(varNamePrimaryColor, primaryColor)
|
||||||
}
|
}
|
||||||
|
|
||||||
export function loadThemeData(name) {
|
|
||||||
require(`./${name}/index.js`)
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -64,10 +64,10 @@ function dateRange(item, level) {
|
||||||
// level: 1: year, 2: month, 3: day
|
// level: 1: year, 2: month, 3: day
|
||||||
switch (level) {
|
switch (level) {
|
||||||
case 1:
|
case 1:
|
||||||
format = t('format.year')
|
format = 'YYYY'
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 2:
|
||||||
format = t('format.year_month')
|
format = 'MMM YYYY'
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (format) {
|
if (format) {
|
||||||
|
|
@ -90,7 +90,7 @@ function dateRange(item, level) {
|
||||||
<% if (hasItems(cv.education)) { %>
|
<% if (hasItems(cv.education)) { %>
|
||||||
<section class="education-section">
|
<section class="education-section">
|
||||||
<div class="section-title">
|
<div class="section-title">
|
||||||
<h2><%= t('title.education') %></h2>
|
<h2>Educations</h2>
|
||||||
<div class="line"></div>
|
<div class="line"></div>
|
||||||
</div>
|
</div>
|
||||||
<% for (const item of cv.education) { %>
|
<% for (const item of cv.education) { %>
|
||||||
|
|
@ -105,10 +105,10 @@ function dateRange(item, level) {
|
||||||
</div>
|
</div>
|
||||||
<%- dateRange(item, 2) %>
|
<%- dateRange(item, 2) %>
|
||||||
</div>
|
</div>
|
||||||
<% if (item.score) { %><div class="score row"><%= t('overall_gpa') %>: <%= item.score %></div><% } %>
|
<% if (item.score) { %><div class="score row">Overall GPA: <%= item.score %></div><% } %>
|
||||||
<% if (item.courses && item.courses.length > 0) { %>
|
<% if (item.courses && item.courses.length > 0) { %>
|
||||||
<div class="courses row">
|
<div class="courses row">
|
||||||
<%= t('courses') %>: <%= item.courses.join('; ') %>
|
Courses: <%= item.courses.join('; ') %>
|
||||||
</div>
|
</div>
|
||||||
<% } %>
|
<% } %>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -120,7 +120,7 @@ function dateRange(item, level) {
|
||||||
<% if (hasItems(cv.work)) { %>
|
<% if (hasItems(cv.work)) { %>
|
||||||
<section class="work-section">
|
<section class="work-section">
|
||||||
<div class="section-title">
|
<div class="section-title">
|
||||||
<h2><%= t('title.work') %></h2>
|
<h2>Work</h2>
|
||||||
<div class="line"></div>
|
<div class="line"></div>
|
||||||
</div>
|
</div>
|
||||||
<% for (const item of cv.work) { %>
|
<% for (const item of cv.work) { %>
|
||||||
|
|
@ -152,7 +152,7 @@ function dateRange(item, level) {
|
||||||
<% if (hasItems(cv.projects)) { %>
|
<% if (hasItems(cv.projects)) { %>
|
||||||
<section class="projects-section">
|
<section class="projects-section">
|
||||||
<div class="section-title">
|
<div class="section-title">
|
||||||
<h2><%= t('title.projects') %></h2>
|
<h2>Projects</h2>
|
||||||
<div class="line"></div>
|
<div class="line"></div>
|
||||||
</div>
|
</div>
|
||||||
<% for (const item of cv.projects) { %>
|
<% for (const item of cv.projects) { %>
|
||||||
|
|
@ -195,7 +195,7 @@ function dateRange(item, level) {
|
||||||
<% if (hasItems(cv.sideProjects)) { %>
|
<% if (hasItems(cv.sideProjects)) { %>
|
||||||
<section class="sideprojects-section">
|
<section class="sideprojects-section">
|
||||||
<div class="section-title">
|
<div class="section-title">
|
||||||
<h2><%= t('title.side_projects') %></h2>
|
<h2>Side-projects</h2>
|
||||||
<div class="line"></div>
|
<div class="line"></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="two-columns">
|
<div class="two-columns">
|
||||||
|
|
@ -228,7 +228,7 @@ function dateRange(item, level) {
|
||||||
<% if (hasItems(cv.skills)) { %>
|
<% if (hasItems(cv.skills)) { %>
|
||||||
<section class="skills-section">
|
<section class="skills-section">
|
||||||
<div class="section-title">
|
<div class="section-title">
|
||||||
<h2><%= t('title.skills') %></h2>
|
<h2>Skills</h2>
|
||||||
<div class="line"></div>
|
<div class="line"></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="two-columns">
|
<div class="two-columns">
|
||||||
|
|
@ -256,7 +256,7 @@ function dateRange(item, level) {
|
||||||
<% if (hasItems(cv.languages)) { %>
|
<% if (hasItems(cv.languages)) { %>
|
||||||
<section class="languages-section page-unit">
|
<section class="languages-section page-unit">
|
||||||
<div class="section-title">
|
<div class="section-title">
|
||||||
<h2><%= t('title.languages') %></h2>
|
<h2>Languages</h2>
|
||||||
<div class="line"></div>
|
<div class="line"></div>
|
||||||
</div>
|
</div>
|
||||||
<% for (const item of cv.languages) { %>
|
<% for (const item of cv.languages) { %>
|
||||||
|
|
@ -283,10 +283,10 @@ function dateRange(item, level) {
|
||||||
<% if (cv.meta) { %>
|
<% if (cv.meta) { %>
|
||||||
<footer>
|
<footer>
|
||||||
<% if (cv.meta.version) { %>
|
<% if (cv.meta.version) { %>
|
||||||
<div class="version"><%= t('version') %>: <%= cv.meta.version %></div>
|
<div class="version">Version: <%= cv.meta.version %></div>
|
||||||
<% } %>
|
<% } %>
|
||||||
<% if (cv.meta.lastModified) { %>
|
<% if (cv.meta.lastModified) { %>
|
||||||
<div class="version"><%= t('last_modified') %>: <%= cv.meta.lastModified.slice(0, 10) %></div>
|
<div class="version">Last modified: <%= cv.meta.lastModified.slice(0, 10) %></div>
|
||||||
<% } %>
|
<% } %>
|
||||||
</footer>
|
</footer>
|
||||||
<% } %>
|
<% } %>
|
||||||
|
|
|
||||||
|
|
@ -1,11 +0,0 @@
|
||||||
import 'dayjs/locale/zh-cn';
|
|
||||||
|
|
||||||
export const locales = ['en', 'zh-cn']
|
|
||||||
|
|
||||||
export const localeMessages = {}
|
|
||||||
|
|
||||||
for (const locale of locales) {
|
|
||||||
localeMessages[locale] = (await import(`./locales/${locale}.js`)).default
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log('load theme reorx', localeMessages)
|
|
||||||
|
|
@ -1,18 +0,0 @@
|
||||||
export default {
|
|
||||||
'title': {
|
|
||||||
'education': 'Educations',
|
|
||||||
'work': 'Work Experiences',
|
|
||||||
'projects': 'Projects',
|
|
||||||
'side_projects': 'Side-projects',
|
|
||||||
'skills': 'Skills',
|
|
||||||
'languages': 'Languages',
|
|
||||||
},
|
|
||||||
'overall_gpa': 'Overall GPA',
|
|
||||||
'courses': 'Courses',
|
|
||||||
'version': 'Version',
|
|
||||||
'last_modified': 'Last Modified',
|
|
||||||
'format': {
|
|
||||||
'year': 'YYYY',
|
|
||||||
'year_month': 'MMM YYYY',
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
@ -1,9 +0,0 @@
|
||||||
export default {
|
|
||||||
'title': {
|
|
||||||
'education': '教育经历',
|
|
||||||
},
|
|
||||||
'format': {
|
|
||||||
'year': 'YYYY',
|
|
||||||
'year_month': 'YYYY年MMM',
|
|
||||||
},
|
|
||||||
}
|
|
||||||
Loading…
Reference in New Issue