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]

Reply via email to