add color-picker in editor and support primary color in CV HTML

This commit is contained in:
Reorx 2023-02-14 15:44:21 +08:00
parent 4e2c1f0eca
commit 51fd7f8754
10 changed files with 115 additions and 14 deletions

View File

@ -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">

View File

@ -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>

View File

@ -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)

View File

@ -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
}

View File

@ -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

View File

@ -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 {

View File

@ -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,

View File

@ -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)
}

View File

@ -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);
}
}

View File

@ -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,