From 51fd7f8754bc7f1989a116f73318797075df4316 Mon Sep 17 00:00:00 2001 From: Reorx Date: Tue, 14 Feb 2023 15:44:21 +0800 Subject: [PATCH] add color-picker in editor and support primary color in CV HTML --- index.html | 6 ++++++ src/editor/index.html | 7 +++++++ src/editor/main.js | 25 ++++++++++++++++++++++--- src/lib/store.js | 18 +++++++++++++++++- src/preview/main.js | 3 ++- src/scss/editor/index.scss | 36 +++++++++++++++++++++++++++++++++++- src/themes/data.js | 2 ++ src/themes/index.js | 9 +++++++-- src/themes/reorx/index.scss | 14 +++++++++----- vite.config.js | 9 ++++++++- 10 files changed, 115 insertions(+), 14 deletions(-) diff --git a/index.html b/index.html index f5ae6b8..83afc25 100644 --- a/index.html +++ b/index.html @@ -16,6 +16,12 @@ +
diff --git a/src/editor/index.html b/src/editor/index.html index 0a4d305..50ab87a 100644 --- a/src/editor/index.html +++ b/src/editor/index.html @@ -19,6 +19,13 @@
+ diff --git a/src/editor/main.js b/src/editor/main.js index 20e70ed..02511c1 100644 --- a/src/editor/main.js +++ b/src/editor/main.js @@ -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) diff --git a/src/lib/store.js b/src/lib/store.js index 82482b9..90a7c0f 100644 --- a/src/lib/store.js +++ b/src/lib/store.js @@ -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 +} diff --git a/src/preview/main.js b/src/preview/main.js index d986b9b..86b85c3 100644 --- a/src/preview/main.js +++ b/src/preview/main.js @@ -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 diff --git a/src/scss/editor/index.scss b/src/scss/editor/index.scss index 46e532f..f70ee77 100644 --- a/src/scss/editor/index.scss +++ b/src/scss/editor/index.scss @@ -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 { diff --git a/src/themes/data.js b/src/themes/data.js index d91d136..3f27dbd 100644 --- a/src/themes/data.js +++ b/src/themes/data.js @@ -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, diff --git a/src/themes/index.js b/src/themes/index.js index 8ed124c..d61cace 100644 --- a/src/themes/index.js +++ b/src/themes/index.js @@ -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) } diff --git a/src/themes/reorx/index.scss b/src/themes/reorx/index.scss index 54cb0ea..01cc555 100644 --- a/src/themes/reorx/index.scss +++ b/src/themes/reorx/index.scss @@ -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); } } diff --git a/vite.config.js b/vite.config.js index d83935c..a19a69d 100644 --- a/vite.config.js +++ b/vite.config.js @@ -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,