add color-picker in editor and support primary color in CV HTML
This commit is contained in:
parent
4e2c1f0eca
commit
51fd7f8754
|
|
@ -16,6 +16,12 @@
|
|||
</style>
|
||||
<link rel="stylesheet" href="/src/scss/cv-base.css">
|
||||
<link rel="stylesheet" href="/src/themes/<%= theme %>/index.scss">
|
||||
<style>
|
||||
:root {
|
||||
/* override primary color */
|
||||
<%= primaryColor.var %>: <%= primaryColor.value %>;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="cv-container">
|
||||
|
|
|
|||
|
|
@ -19,6 +19,13 @@
|
|||
</div>
|
||||
<div class="editor-toc"></div>
|
||||
<div class="app-actions">
|
||||
<label for="fn-color-picker" class="color-picker">
|
||||
<div>Primary color</div>
|
||||
<div class="color-area">
|
||||
<input type="color" id="fn-color-picker">
|
||||
<span class="value">#aaaaaa</span>
|
||||
</div>
|
||||
</label>
|
||||
<button id="fn-toggle-preview">Preview/JSON</button>
|
||||
<button id="fn-download-json">Download JSON</button>
|
||||
<button id="fn-download-html">Download HTML</button>
|
||||
|
|
|
|||
|
|
@ -10,7 +10,9 @@ import * as sampleModule from '../../sample.cv.json';
|
|||
import * as jsoncvSchemaModule from '../../schema/jsoncv.schema.json';
|
||||
import {
|
||||
getCVData,
|
||||
getPrimaryColor,
|
||||
saveCVJSON,
|
||||
savePrimaryColor,
|
||||
} from '../lib/store';
|
||||
import {
|
||||
createElement,
|
||||
|
|
@ -140,6 +142,7 @@ function getEditorData() {
|
|||
|
||||
const $outputJSON = $('.output-json')
|
||||
const $outputHTML = $('.output-html')
|
||||
const outputHTMLIframe = $outputHTML.get(0)
|
||||
|
||||
// listen to change
|
||||
editor.on('change', () => {
|
||||
|
|
@ -160,11 +163,13 @@ const $btnDownloadJSON = $('#fn-download-json')
|
|||
const $btnDownloadHTML = $('#fn-download-html')
|
||||
const $btnLoadSample = $('#fn-load-sample')
|
||||
const $btnPrintPreview = $('#fn-print-preview')
|
||||
const $inputColorPicker = $('#fn-color-picker')
|
||||
const $colorValue = $('.color-picker .value')
|
||||
|
||||
const isElementHidden = elt =>
|
||||
! (elt.offsetWidth || elt.offsetHeight || elt.getClientRects().length);
|
||||
$btnTogglePreview.on('click', () => {
|
||||
if (isElementHidden($outputHTML.get(0))) {
|
||||
if (isElementHidden(outputHTMLIframe)) {
|
||||
$outputJSON.hide()
|
||||
$outputHTML.show()
|
||||
} else {
|
||||
|
|
@ -220,7 +225,7 @@ function downloadCV(contentType) {
|
|||
downloadContent(filename, JSON.stringify(data, null, 2))
|
||||
} else if (contentType === 'html') {
|
||||
let filename = `${title}.html`
|
||||
downloadIframeHTML(filename, $outputHTML.get(0))
|
||||
downloadIframeHTML(filename, outputHTMLIframe)
|
||||
}
|
||||
|
||||
// update editor value
|
||||
|
|
@ -242,5 +247,19 @@ $btnLoadSample.on('click', () => {
|
|||
})
|
||||
|
||||
$btnPrintPreview.on('click', () => {
|
||||
$outputHTML.get(0).contentWindow.print()
|
||||
outputHTMLIframe.contentWindow.print()
|
||||
})
|
||||
|
||||
|
||||
// primary color
|
||||
|
||||
$inputColorPicker.on('change', (e) => {
|
||||
const color = e.target.value
|
||||
console.log('color', color)
|
||||
$colorValue.text(color)
|
||||
savePrimaryColor(color)
|
||||
})
|
||||
|
||||
const primaryColor = getPrimaryColor()
|
||||
$colorValue.text(primaryColor)
|
||||
$inputColorPicker.val(primaryColor)
|
||||
|
|
|
|||
|
|
@ -1,11 +1,18 @@
|
|||
export const storeKeys = {
|
||||
cvJSON: 'cvJSON',
|
||||
cvSavedTime: 'cvSavedTime',
|
||||
primaryColor: 'primary-color',
|
||||
}
|
||||
|
||||
const defaultPrimaryColor = '#2A3FFB'
|
||||
|
||||
function updateSavedTime() {
|
||||
localStorage.setItem(storeKeys.cvSavedTime, Date.now())
|
||||
}
|
||||
|
||||
export function saveCVJSON(str) {
|
||||
localStorage.setItem(storeKeys.cvJSON, str)
|
||||
localStorage.setItem(storeKeys.cvSavedTime, Date.now())
|
||||
updateSavedTime()
|
||||
}
|
||||
|
||||
export function getCVData() {
|
||||
|
|
@ -17,3 +24,12 @@ export function getCVData() {
|
|||
export function getCVSavedTime() {
|
||||
return localStorage.getItem(storeKeys.cvSavedTime)
|
||||
}
|
||||
|
||||
export function savePrimaryColor(color) {
|
||||
localStorage.setItem(storeKeys.primaryColor, color)
|
||||
updateSavedTime()
|
||||
}
|
||||
|
||||
export function getPrimaryColor() {
|
||||
return localStorage.getItem(storeKeys.primaryColor) || defaultPrimaryColor
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import {
|
||||
getCVData,
|
||||
getCVSavedTime,
|
||||
getPrimaryColor,
|
||||
} from '../lib/store';
|
||||
import { renderThemeOn } from '../themes';
|
||||
import { getCVTitle } from '../themes/data';
|
||||
|
|
@ -33,7 +34,7 @@ const restoreScrollPosition = () => {
|
|||
// Render CV
|
||||
const data = getCVData()
|
||||
if (data) {
|
||||
renderThemeOn(themeName, elCV, data)
|
||||
renderThemeOn(themeName, elCV, data, getPrimaryColor())
|
||||
// change document title
|
||||
document.title = getCVTitle(data)
|
||||
// restore scroll position
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
@use '../basic';
|
||||
@use '../vars';
|
||||
@use 'json-editor';
|
||||
|
||||
button {
|
||||
|
|
@ -72,9 +73,42 @@ button {
|
|||
}
|
||||
|
||||
.app-actions {
|
||||
button {
|
||||
> * {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.color-picker {
|
||||
@include vars.button-base;
|
||||
border: 1px solid var(--grey-2);
|
||||
border-radius: 2px;
|
||||
padding: 3px 8px;
|
||||
font-size: 13px;
|
||||
display: block;
|
||||
width: 85px;
|
||||
|
||||
&:active {
|
||||
border-color: #555;
|
||||
}
|
||||
|
||||
.color-area {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
input[type=color] {
|
||||
background: transparent;
|
||||
padding: 0;
|
||||
border: 0;
|
||||
outline: 0;
|
||||
width: 20px;
|
||||
height: 24px;
|
||||
@supports (-moz-appearance:none) {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.output-html {
|
||||
|
|
|
|||
|
|
@ -2,6 +2,8 @@ import { reformatDate } from '../lib/date';
|
|||
import { getIconSVG } from '../lib/icons';
|
||||
import { renderMarkdown } from '../lib/markdown';
|
||||
|
||||
export const primaryColorVarName = '--color-primary'
|
||||
|
||||
export function getRenderData(cvData) {
|
||||
return {
|
||||
cv: cvData,
|
||||
|
|
|
|||
|
|
@ -1,6 +1,9 @@
|
|||
import ejs from 'ejs';
|
||||
|
||||
import { getRenderData } from './data';
|
||||
import {
|
||||
getRenderData,
|
||||
primaryColorVarName,
|
||||
} from './data';
|
||||
|
||||
const themes = {}
|
||||
|
||||
|
|
@ -36,7 +39,7 @@ export function renderTheme(template, cvData, options) {
|
|||
|
||||
const cvStyleId = 'cv-style'
|
||||
|
||||
export function renderThemeOn(name, el, data) {
|
||||
export function renderThemeOn(name, el, data, primaryColor) {
|
||||
const theme = getTheme(name)
|
||||
el.innerHTML = renderTheme(theme.template, data)
|
||||
|
||||
|
|
@ -46,4 +49,6 @@ export function renderThemeOn(name, el, data) {
|
|||
document.head.appendChild(elStyle)
|
||||
}
|
||||
elStyle.innerHTML = theme.style
|
||||
|
||||
document.documentElement.style.setProperty(primaryColorVarName, primaryColor)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
/* Naming convention: https://ricostacruz.com/rscss/ */
|
||||
|
||||
$color-signature: #2A3FFB;
|
||||
$color-text-dim: #777;
|
||||
$color-text-dimmer: #999;
|
||||
$color-border-dim: #aaa;
|
||||
|
|
@ -11,6 +10,11 @@ $fz-3: 18px;
|
|||
$fz-4: 16px;
|
||||
$lh-p: 1.4;
|
||||
|
||||
:root {
|
||||
// overide `--color-primary` in your own css
|
||||
--color-primary: #aaa;
|
||||
}
|
||||
|
||||
.cv-container {
|
||||
font-size: 14px;
|
||||
font-family: system-ui, sans-serif;
|
||||
|
|
@ -31,11 +35,11 @@ $lh-p: 1.4;
|
|||
}
|
||||
|
||||
a, a:visited, a:active {
|
||||
color: $color-signature;
|
||||
color: var(--color-primary);
|
||||
text-decoration: none;
|
||||
}
|
||||
a:hover {
|
||||
color: $color-signature;
|
||||
color: var(--color-primary);
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
|
|
@ -47,14 +51,14 @@ section {
|
|||
h2 {
|
||||
font-size: $fz-2;
|
||||
font-weight: 600;
|
||||
color: $color-signature;
|
||||
color: var(--color-primary);
|
||||
margin: 0;
|
||||
}
|
||||
.line {
|
||||
flex-grow: 1;
|
||||
margin: 14px 0 0 1em;
|
||||
height: 2px;
|
||||
background-color: $color-signature;
|
||||
background-color: var(--color-primary);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -4,7 +4,10 @@ import { ViteEjsPlugin } from 'vite-plugin-ejs';
|
|||
import { viteSingleFile } from 'vite-plugin-singlefile';
|
||||
|
||||
import { TransformEjs } from './src/lib/vite-plugins';
|
||||
import { getRenderData } from './src/themes/data';
|
||||
import {
|
||||
getRenderData,
|
||||
primaryColorVarName,
|
||||
} from './src/themes/data';
|
||||
|
||||
const dataFilename = process.env.DATA_FILENAME || './sample.cv.json'
|
||||
const outDir = process.env.OUT_DIR || 'dist'
|
||||
|
|
@ -12,6 +15,10 @@ const outDir = process.env.OUT_DIR || 'dist'
|
|||
const data = require(dataFilename)
|
||||
const renderData = getRenderData(data)
|
||||
renderData.theme = process.env.THEME || 'reorx'
|
||||
renderData.primaryColor = {
|
||||
var: primaryColorVarName,
|
||||
value: process.env.PRIMARY_COLOR || '#2A3FFB'
|
||||
}
|
||||
renderData.isProduction = process.env.NODE_ENV === 'production'
|
||||
renderData.meta = {
|
||||
title: data.basics.name,
|
||||
|
|
|
|||
Loading…
Reference in New Issue