From 6eb582fef71f4215487cf81e4a1d9930ba496fad Mon Sep 17 00:00:00 2001 From: Cell <1024@lruihao.cn> Date: Mon, 28 Apr 2025 14:57:58 +0800 Subject: [PATCH] :sparkles: Feat: add `async` and `data` params for echarts shortcode (#592) --- assets/js/theme.js | 58 +++++++++++++++++++++------- assets/js/util.js | 10 ++--- layouts/partials/plugin/echarts.html | 22 +++++++++-- layouts/shortcodes/echarts.html | 4 +- 4 files changed, 70 insertions(+), 24 deletions(-) diff --git a/assets/js/theme.js b/assets/js/theme.js index 5c3739c7..c9600f5d 100644 --- a/assets/js/theme.js +++ b/assets/js/theme.js @@ -684,25 +684,53 @@ class FixIt { this._echartsArr = []; const stagingDOM = this.util.getStagingDOM() this.util.forEach(document.getElementsByClassName('echarts'), ($echarts) => { - if ($echarts.nextElementSibling.tagName === 'TEMPLATE') { - const chart = echarts.init($echarts, this.isDark ? 'dark' : 'light', { renderer: 'svg' }); - stagingDOM.stage($echarts.nextElementSibling.content.cloneNode(true)); - let option; - if ($echarts.nextElementSibling.dataset.fmt === 'js') { - // support JS object literal or JS code - const jsCodes = stagingDOM.contentAsText(); - option = new Function( - this.util.isObjectString(jsCodes) - ? `return ${jsCodes}` - : `${jsCodes} return option` - )(); - } else { - // support JSON - option = stagingDOM.contentAsJson(); + const $dataEl = $echarts.nextElementSibling; + if ($dataEl.tagName !== 'TEMPLATE') { + return; + } + const chart = echarts.init($echarts, this.isDark ? 'dark' : 'light', { renderer: 'svg' }); + chart.showLoading(); + stagingDOM.stage($dataEl.content.cloneNode(true)); + const _setOption = (option) => { + if (!option) { + chart.hideLoading(); + console.warn('ECharts option is missing or invalid. Chart disposed.', { + element: $echarts, + option: $dataEl, + }); + chart.dispose(); + $echarts.removeAttribute('style'); + return; } + chart.hideLoading(); chart.setOption(option); this._echartsArr.push(chart); + }; + // support JS object literal or JS code + if ($dataEl.dataset.fmt === 'js') { + try { + const jsCodes = stagingDOM.contentAsText(); + /** + * Get ECharts option + * @param {Object} fixit FixIt instance + * @param {Object} chart ECharts instance + * @returns {Object|Promise} ECharts option or Promise + */ + const _getOption = new Function('fixit', 'chart', + this.util.isObjectLiteral(jsCodes) ? `return ${jsCodes}` : jsCodes + ); + if ($dataEl.dataset.async === 'true') { + return Promise.resolve(_getOption(this, chart)).then(option => { + _setOption(option); + }); + } + return _setOption(_getOption(this, chart)); + } catch (err) { + return console.error(err); + } } + // support JSON + _setOption(stagingDOM.contentAsJson()); }); stagingDOM.destroy(); }); diff --git a/assets/js/util.js b/assets/js/util.js index af8bad43..5d0df239 100644 --- a/assets/js/util.js +++ b/assets/js/util.js @@ -114,15 +114,15 @@ export default class Util { /** * check if a string is a JS object string - * @example isObjectString("{a:1,b:2}") // true - * @param {String} str - * @returns + * @example isObjectLiteral("{a:1,b:2}") // true + * @param {String} str string to check + * @returns {Boolean} whether the string is a JS object string */ - isObjectString(str) { + isObjectLiteral(str) { if (typeof str !== 'string') { return false; } - str = str.replace(/\s+/g, ' ').trim(); + str = str.replace(/\s+/g, ' ').trim().replace(/;$/, '') if (str.startsWith('{') && str.endsWith('}')) { return true; } diff --git a/layouts/partials/plugin/echarts.html b/layouts/partials/plugin/echarts.html index 3be9e778..cf0008f4 100644 --- a/layouts/partials/plugin/echarts.html +++ b/layouts/partials/plugin/echarts.html @@ -2,14 +2,30 @@ {{- $width := .Options.width | default "100%" -}} {{- $height := .Options.height | default "30rem" -}} {{- $js := .Options.js | default false -}} +{{- $async := .Options.async | default false -}} +{{- $data := .Options.data -}} {{- $attrs := printf `style="width: %v; height: %v;"` $width $height -}}
+{{- $content := strings.TrimSpace .Inner -}} {{- if $js -}} - {{- $content := strings.TrimSpace .Inner -}} {{- if hugo.IsProduction -}} {{- $content = replaceRE `[\s]+` " " $content -}} {{- end -}} - {{ $content | safeJS }} + {{- if not $content -}} + {{- warnf "ECharts JS Code is empty, please check the content." -}} + {{- end -}} + {{ $content | safeJS }} {{- else -}} - {{ .Inner | transform.Unmarshal | jsonify }} + {{ with $data }} + {{- $content = index site.Data.echarts . -}} + {{- if not $content -}} + {{- warnf "ECharts data not found, please ensure the data file \"/data/echarts/%s.{json|yml|yaml|toml}\" exists." . -}} + {{- end -}} + {{- else -}} + {{- $content = $content | transform.Unmarshal -}} + {{- if not $content -}} + {{- warnf "ECharts option is empty, please check the content." -}} + {{- end -}} + {{- end -}} + {{ $content | jsonify }} {{- end -}} diff --git a/layouts/shortcodes/echarts.html b/layouts/shortcodes/echarts.html index d9fd7014..40a6a2cf 100644 --- a/layouts/shortcodes/echarts.html +++ b/layouts/shortcodes/echarts.html @@ -1,5 +1,7 @@ {{- $options := .Get "width" | default (.Get 0) | dict "width" -}} {{- $options = .Get "height" | default (.Get 1) | dict "height" | merge $options -}} -{{- $options = .Get "js" | default (.Get 2) | dict "js" | merge $options -}} +{{- $options = .Get "js" | default false | dict "js" | merge $options -}} +{{- $options = .Get "async" | default false | dict "async" | merge $options -}} +{{- $options = .Get "data" | dict "data" | merge $options -}} {{- dict "Options" $options "Inner" .Inner | partial "plugin/echarts.html" -}} {{- .Page.Store.Set "hasEcharts" true -}}