This is an automated email from the ASF dual-hosted git repository. ovilia pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/echarts-theme-builder.git
commit d90cc4159f86bbe9639c66e9fe2eaddf36727fa4 Author: Ovilia <[email protected]> AuthorDate: Fri Sep 19 14:11:03 2025 +0800 feat: support grid and legend location --- public/themes/v5.json | 6 ++- src/components/ThemePanel.vue | 110 +++++++++++++++++++++++++++++++++++++++++- src/locales/en.json | 7 +++ src/locales/zh.json | 7 +++ src/stores/theme.ts | 21 ++------ src/types/theme.ts | 6 +++ src/utils/chartConfigs.ts | 24 +++------ src/utils/themeGenerator.ts | 8 ++- 8 files changed, 154 insertions(+), 35 deletions(-) diff --git a/public/themes/v5.json b/public/themes/v5.json index 78e10bf..14695ea 100644 --- a/public/themes/v5.json +++ b/public/themes/v5.json @@ -160,6 +160,10 @@ "timelineControlColor": "#A4B1D7", "timelineControlBorderColor": "#A4B1D7", "timelineControlBorderWidth": 1, - "timelineLabelColor": "#A4B1D7" + "timelineLabelColor": "#A4B1D7", + "gridLeft": "10%", + "gridTop": 60, + "gridBottom": 70, + "gridRight": "" } } diff --git a/src/components/ThemePanel.vue b/src/components/ThemePanel.vue index d941a13..2e641af 100644 --- a/src/components/ThemePanel.vue +++ b/src/components/ThemePanel.vue @@ -122,6 +122,32 @@ </div> </van-collapse-item> + <!-- Grid Layout --> + <van-collapse-item :title="$t('panel.grid')" name="grid"> + <div class="panel-content"> + <van-field + v-model="gridLeft" + :label="$t('grid.left')" + @blur="validateGridValue('left')" + /> + <van-field + v-model="gridRight" + :label="$t('grid.right')" + @blur="validateGridValue('right')" + /> + <van-field + v-model="gridTop" + :label="$t('grid.top')" + @blur="validateGridValue('top')" + /> + <van-field + v-model="gridBottom" + :label="$t('grid.bottom')" + @blur="validateGridValue('bottom')" + /> + </div> + </van-collapse-item> + <!-- Coordinate Axis --> <van-collapse-item :title="$t('panel.axis')" name="axis"> <div class="panel-content"> @@ -431,7 +457,7 @@ interface Props { const props = defineProps<Props>() // Component state -const activeNames = ref(['functions']) // Functions panel expanded by default +const activeNames = ref(['functions', 'basic', 'visualMap', 'grid']) // Panels expanded by default const fileInput = ref<HTMLInputElement>() // Theme store @@ -441,6 +467,88 @@ const { theme, themeName } = themeStore // Predefined themes const preDefinedThemes = PRE_DEFINED_THEMES +// Grid layout reactive properties +const gridLeft = ref(String(theme.gridLeft)) +const gridRight = ref(String(theme.gridRight)) +const gridTop = ref(String(theme.gridTop)) +const gridBottom = ref(String(theme.gridBottom)) + +// Grid value validation function +const validateGridValue = (position: 'left' | 'right' | 'top' | 'bottom') => { + let valueRef; + + switch (position) { + case 'left': + valueRef = gridLeft; + break; + case 'right': + valueRef = gridRight; + break; + case 'top': + valueRef = gridTop; + break; + case 'bottom': + valueRef = gridBottom; + break; + } + + const inputValue = valueRef.value.trim(); + + // Check if the value is a number or a percentage string + const isValid = + // Empty string - will be converted to undefined in setOption + inputValue === '' || + // Valid number + /^[0-9]+$/.test(inputValue) || + // Valid percentage (e.g. 10%, 10.5%) + /^[0-9]+(\.[0-9]+)?%$/.test(inputValue); + + if (isValid) { + // Update the theme property with the validated value + // For numeric values, convert to number if it's a pure number + // Empty strings will use default values defined in themeGenerator.ts + const finalValue = inputValue === '' ? + (position === 'left' || position === 'right' ? '10%' : 60) : + (/^[0-9]+$/.test(inputValue)) ? parseInt(inputValue, 10) : inputValue; + + // Update the corresponding theme property + switch (position) { + case 'left': + theme.gridLeft = finalValue; + break; + case 'right': + theme.gridRight = finalValue; + break; + case 'top': + theme.gridTop = finalValue; + break; + case 'bottom': + theme.gridBottom = finalValue; + break; + } + } else { + // If invalid, reset to default values + const defaultValue = position === 'left' || position === 'right' ? '10%' : 60; + valueRef.value = String(defaultValue); + + // Set the theme property to default value + switch (position) { + case 'left': + theme.gridLeft = '10%'; + break; + case 'right': + theme.gridRight = '10%'; + break; + case 'top': + theme.gridTop = 60; + break; + case 'bottom': + theme.gridBottom = 60; + break; + } + } +} + // Methods const downloadTheme = async () => { try { diff --git a/src/locales/en.json b/src/locales/en.json index ea72719..efe3453 100644 --- a/src/locales/en.json +++ b/src/locales/en.json @@ -16,6 +16,7 @@ "functions": "Functions", "basicConfig": "Basic Configuration", "visualMap": "Visual Map", + "grid": "Grid Layout", "axis": "Axes", "legend": "Legend", "toolbox": "Toolbox", @@ -48,6 +49,12 @@ "toolboxEmphasis": "Toolbox Hover", "visualMapColor": "Visual Map" }, + "grid": { + "left": "Left", + "right": "Right", + "top": "Top", + "bottom": "Bottom" + }, "axis": { "categoryAxis": "Category Axis", "valueAxis": "Value Axis", diff --git a/src/locales/zh.json b/src/locales/zh.json index 127f5d0..314b62c 100644 --- a/src/locales/zh.json +++ b/src/locales/zh.json @@ -16,6 +16,7 @@ "functions": "功能", "basicConfig": "基本配置", "visualMap": "视觉映射", + "grid": "Grid 布局", "axis": "坐标轴", "legend": "图例", "toolbox": "工具箱", @@ -48,6 +49,12 @@ "toolboxEmphasis": "悬停", "visualMapColor": "视觉映射" }, + "grid": { + "left": "左边距", + "right": "右边距", + "top": "上边距", + "bottom": "下边距" + }, "axis": { "categoryAxis": "类目轴", "valueAxis": "数值轴", diff --git a/src/stores/theme.ts b/src/stores/theme.ts index 91865b4..2c6a444 100644 --- a/src/stores/theme.ts +++ b/src/stores/theme.ts @@ -205,7 +205,11 @@ export const createDefaultTheme = (): ThemeData => { timelineControlColor: '#A4B1D7', timelineControlBorderColor: '#A4B1D7', timelineControlBorderWidth: 1, - timelineLabelColor: '#A4B1D7' + timelineLabelColor: '#A4B1D7', + gridLeft: '15%', + gridRight: '10%', + gridTop: 65, + gridBottom: 80 } } @@ -245,21 +249,6 @@ const createThemeStore = () => { if (themeData.theme) { // Convert string numbers to actual numbers const loadedTheme = { ...themeData.theme } - if (typeof loadedTheme.seriesCnt === 'string') { - loadedTheme.seriesCnt = parseInt(loadedTheme.seriesCnt, 10) - } - if (typeof loadedTheme.borderWidth === 'string') { - loadedTheme.borderWidth = parseFloat(loadedTheme.borderWidth) - } - if (typeof loadedTheme.lineWidth === 'string') { - loadedTheme.lineWidth = parseFloat(loadedTheme.lineWidth) - } - if (typeof loadedTheme.symbolSize === 'string') { - loadedTheme.symbolSize = parseFloat(loadedTheme.symbolSize) - } - if (typeof loadedTheme.symbolBorderWidth === 'string') { - loadedTheme.symbolBorderWidth = parseFloat(loadedTheme.symbolBorderWidth) - } // Apply the complete theme configuration property by property to ensure reactivity // This ensures Vue's reactive system properly detects changes diff --git a/src/types/theme.ts b/src/types/theme.ts index 708e09f..1c1ed5a 100644 --- a/src/types/theme.ts +++ b/src/types/theme.ts @@ -90,6 +90,12 @@ export interface ThemeData { timelineControlBorderColor: string timelineControlBorderWidth: number timelineLabelColor: string + + // Grid layout + gridLeft: number | string + gridRight: number | string + gridTop: number | string + gridBottom: number | string } export interface PreDefinedTheme { diff --git a/src/utils/chartConfigs.ts b/src/utils/chartConfigs.ts index cd31ceb..643c43a 100644 --- a/src/utils/chartConfigs.ts +++ b/src/utils/chartConfigs.ts @@ -47,8 +47,7 @@ export function getChartConfigs(seriesCnt: number = 4): ChartConfig[] { subtext: 'Basic line chart example' }, legend: { - data: legendData, - right: 0 + data: legendData }, tooltip: commonTooltip, toolbox: commonToolbox, @@ -81,8 +80,7 @@ export function getChartConfigs(seriesCnt: number = 4): ChartConfig[] { subtext: 'Stacked area chart example' }, legend: { - data: legendData, - right: 0 + data: legendData }, tooltip: commonTooltip, toolbox: commonToolbox, @@ -115,8 +113,7 @@ export function getChartConfigs(seriesCnt: number = 4): ChartConfig[] { subtext: 'Basic bar chart example' }, legend: { - data: legendData, - right: 0 + data: legendData }, tooltip: commonTooltip, toolbox: commonToolbox, @@ -149,8 +146,7 @@ export function getChartConfigs(seriesCnt: number = 4): ChartConfig[] { subtext: 'Stacked bar chart example' }, legend: { - data: legendData, - right: 0 + data: legendData }, tooltip: commonTooltip, toolbox: commonToolbox, @@ -181,8 +177,7 @@ export function getChartConfigs(seriesCnt: number = 4): ChartConfig[] { subtext: 'Basic scatter chart example' }, legend: { - data: legendData, - right: 0 + data: legendData }, tooltip: { trigger: 'item' as const @@ -216,8 +211,7 @@ export function getChartConfigs(seriesCnt: number = 4): ChartConfig[] { subtext: 'Basic pie chart example' }, legend: { - data: legendData, - right: 0 + data: legendData }, tooltip: { trigger: 'item', @@ -254,8 +248,7 @@ export function getChartConfigs(seriesCnt: number = 4): ChartConfig[] { subtext: 'Basic radar chart example' }, legend: { - data: legendData, - right: 0 + data: legendData }, tooltip: commonTooltip, toolbox: commonToolbox, @@ -437,8 +430,7 @@ export function getChartConfigs(seriesCnt: number = 4): ChartConfig[] { layout: 'force', roam: true, label: { - show: true, - fontSize: 12 + show: true }, force: { repulsion: 400, diff --git a/src/utils/themeGenerator.ts b/src/utils/themeGenerator.ts index 86d3a3e..20b33c9 100644 --- a/src/utils/themeGenerator.ts +++ b/src/utils/themeGenerator.ts @@ -248,7 +248,13 @@ export function generateEChartsTheme(themeData: ThemeData, isToExport: boolean = visualMap: { color: themeData.visualMapColor }, - markPoint: markPointConfig + markPoint: markPointConfig, + grid: { + left: themeData.gridLeft === '' ? '10%' : themeData.gridLeft, + right: themeData.gridRight === '' ? '10%' : themeData.gridRight, + top: themeData.gridTop === '' ? 60 : themeData.gridTop, + bottom: themeData.gridBottom === '' ? 60 : themeData.gridBottom + } } return themeConfig --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
