From e775bce2265c8aa2968311e239ec9ac2c6d1c42e Mon Sep 17 00:00:00 2001 From: Kapil Sachdev Date: Tue, 16 Jun 2026 22:43:55 +0530 Subject: [PATCH 1/5] feat: Add Navigation to Editor and refactor ExampleCard and Explore components - Introduced Navigation to editor for ease of traversal between examples. - Refactored Explore component to reutilize utility in Navigation component. - Added exampleCatalogUtils for shared logic between Explore and Navigation. - Modified ExampleCard to be flexible with new navigation structure. --- src/common/exampleCatalogUtils.js | 143 +++++ src/editor/Editor.vue | 839 ++++++++++++++++-------------- src/editor/Navigation.vue | 84 +++ src/explore/ExampleCard.vue | 66 ++- src/explore/Explore.vue | 153 +----- 5 files changed, 727 insertions(+), 558 deletions(-) create mode 100644 src/common/exampleCatalogUtils.js create mode 100644 src/editor/Navigation.vue diff --git a/src/common/exampleCatalogUtils.js b/src/common/exampleCatalogUtils.js new file mode 100644 index 00000000..676a95a4 --- /dev/null +++ b/src/common/exampleCatalogUtils.js @@ -0,0 +1,143 @@ +import LazyLoad from 'vanilla-lazyload/dist/lazyload.esm'; +import { EXAMPLE_CATEGORIES, BLACK_MAP } from './config'; + +const icons = {}; +[ + 'line', + 'bar', + 'scatter', + 'pie', + 'radar', + 'funnel', + 'gauge', + 'map', + 'graph', + 'treemap', + 'parallel', + 'sankey', + 'candlestick', + 'boxplot', + 'heatmap', + 'pictorialBar', + 'themeRiver', + 'calendar', + 'matrix', + 'chord', + 'custom', + 'sunburst', + 'tree', + 'dataset', + 'geo', + 'lines', + 'dataZoom', + 'rich', + 'graphic' +].forEach(function (category) { + icons[category] = require('../asset/icon/' + category + '.svg'); +}); + +const glIcon = require('../asset/icon/gl.svg'); +[ + 'globe', + 'bar3D', + 'scatter3D', + 'surface', + 'map3D', + 'lines3D', + 'line3D', + 'scatterGL', + 'linesGL', + 'flowGL', + 'graphGL', + 'geo3D' +].forEach(function (category) { + icons[category] = glIcon; +}); + +function defaultErrorCallback(img) { + const fallbackSrc = img.src; + const children = img.parentElement.children; + for (let i = 0, len = children.length; i < len; i++) { + const el = children[i]; + if (el !== img) { + el.srcset = fallbackSrc; + } + } +} + +export function createLazyLoader(options = {}) { + return new LazyLoad({ + // Container should be the scroll viewport. + // container: this.$el.querySelector('#explore-container .example-list-panel') + elements_selector: '.chart-area', + load_delay: 400, + class_loaded: 'ec-shot-loaded', + callback_error: defaultErrorCallback, + ...options + }); +} + +function buildExampleListByCategory(chartList, chartListGL) { + const exampleListByCategory = {}; + + function addExamples(list, isGL) { + let categoryOrder = 0; + + do { + let added = false; + for (let i = 0; i < list.length; i++) { + const example = list[i]; + if (BLACK_MAP.hasOwnProperty(example.id)) { + continue; + } + if (example.noExplore) { + continue; + } + if (typeof example.category === 'string') { + example.category = [example.category]; + } + + const categoryStr = (example.category || [])[categoryOrder]; + if (categoryStr) { + added = true; + let categoryObj = exampleListByCategory[categoryStr]; + if (!categoryObj) { + categoryObj = { + category: categoryStr, + examples: [] + }; + exampleListByCategory[categoryStr] = categoryObj; + } + example.isGL = isGL; + categoryObj.examples.push(example); + } + } + + if (!added) { + break; + } + } while (++categoryOrder && categoryOrder < 4); + } + + addExamples(chartList, false); + addExamples(chartListGL, true); + + return exampleListByCategory; +} + +function createExampleList(exampleListByCategory) { + const list = []; + for (let i = 0, len = EXAMPLE_CATEGORIES.length; i < len; i++) { + const category = EXAMPLE_CATEGORIES[i]; + const categoryObj = exampleListByCategory[category]; + if (categoryObj && categoryObj.examples.length > 0) { + list.push({ + category, + examples: categoryObj.examples + }); + } + } + return list; +} + +export { icons, buildExampleListByCategory, createExampleList }; diff --git a/src/editor/Editor.vue b/src/editor/Editor.vue index cc627b9d..d5ce2a6b 100644 --- a/src/editor/Editor.vue +++ b/src/editor/Editor.vue @@ -1,417 +1,434 @@ + + diff --git a/src/explore/ExampleCard.vue b/src/explore/ExampleCard.vue index e5e1797b..7b82246b 100644 --- a/src/explore/ExampleCard.vue +++ b/src/explore/ExampleCard.vue @@ -1,23 +1,27 @@ @@ -27,7 +31,13 @@ import { store } from '../common/store'; import { URL_PARAMS } from '../common/config'; export default { - props: ['example'], + props: { + example: Object, + embedded: { + type: Boolean, + default: false + } + }, computed: { title() { @@ -103,21 +113,27 @@ export default { position: relative; .example-link { - margin-top: 10px; - border-radius: 5px; - box-shadow: 0 0 20px rgba(0, 0, 0, 0.05); - overflow: hidden; - display: block; - } + text-decoration: none; - .chart-area { - width: 100%; - height: 100%; - cursor: pointer; - transition: 0.3s ease-in-out; + &-image { + margin-top: 10px; + border-radius: 5px; + box-shadow: 0 0 20px rgba(0, 0, 0, 0.05); + overflow: hidden; + display: block; + + .chart-area { + width: 100%; + height: 100%; + cursor: pointer; + transition: 0.3s ease-in-out; + } + } &:hover { - transform: scale(1.2); + .chart-area { + transform: scale(1.2); + } } } diff --git a/src/explore/Explore.vue b/src/explore/Explore.vue index b3a21ef3..5eeb3175 100644 --- a/src/explore/Explore.vue +++ b/src/explore/Explore.vue @@ -68,66 +68,15 @@ @@ -71,13 +84,17 @@ export default { .example-link-image { margin-top: 0; - width: 72px; - flex: 0 0 72px; + width: 48px; + flex: 0 0 48px; } .example-info { flex: 1; min-width: 0; + + .example-version-since { + display: none; + } } } } diff --git a/src/explore/ExampleCard.vue b/src/explore/ExampleCard.vue index 7b82246b..fa8ec567 100644 --- a/src/explore/ExampleCard.vue +++ b/src/explore/ExampleCard.vue @@ -4,6 +4,7 @@ class="example-link" :target="embedded ? '_self' : '_blank'" :href="exampleLink" + @click="onLinkClick" > @@ -99,6 +100,16 @@ export default { screenshotURLPNG() { return `${store.cdnRoot}/${this.exampleThumbFilePath}.png?_v_=${store.version}`; } + }, + + methods: { + onLinkClick(event) { + if (!this.embedded) { + return; + } + event.preventDefault(); + this.$emit('select-example', this.example); + } } }; From 5eb87f9c2b348c017b7babe447ffd5a18f8faf1c Mon Sep 17 00:00:00 2001 From: Kapil Sachdev Date: Wed, 17 Jun 2026 23:36:34 +0530 Subject: [PATCH 3/5] feat: Make Navigation as collapsible accordion --- src/editor/Navigation.vue | 106 ++++++++++++++++++++++++++++++++++---- 1 file changed, 95 insertions(+), 11 deletions(-) diff --git a/src/editor/Navigation.vue b/src/editor/Navigation.vue index 9b6a7454..851f43f9 100644 --- a/src/editor/Navigation.vue +++ b/src/editor/Navigation.vue @@ -1,19 +1,24 @@ @@ -45,29 +50,40 @@ export default { data() { return { - exampleListByCategory: buildExampleListByCategory( - CHART_LIST, - CHART_LIST_GL - ) + rawCategoryData: null, + currentExampleId: '' }; }, computed: { exampleList() { - return createExampleList(this.exampleListByCategory); + if (!this.rawCategoryData) { + return []; + } + return createExampleList(this.rawCategoryData); } }, + created() { + this.rawCategoryData = buildExampleListByCategory( + CHART_LIST, + CHART_LIST_GL + ); + }, + mounted() { this._lazyload = createLazyLoader(); }, beforeDestroy() { - this._lazyload.destroy(); + if (this._lazyload && typeof this._lazyload.destroy === 'function') { + this._lazyload.destroy(); + } }, methods: { onSelectExample(example) { + this.currentExampleId = example.id; this.$emit('select-example', example); } } @@ -76,6 +92,74 @@ export default {