diff --git a/.github/workflows/.scripts/update-notice-year.js b/.github/workflows/.scripts/update-notice-year.js new file mode 100644 index 0000000000..2fbb6b7b82 --- /dev/null +++ b/.github/workflows/.scripts/update-notice-year.js @@ -0,0 +1,93 @@ +/** + * @typedef {import('@octokit/rest').Octokit} Octokit + * @typedef {import('@actions/github')['context']} Context + */ + +module.exports = async function updateNoticeYear( + /** @type {{ octokit: Octokit, context: Context }} */ + { octokit, context } +) { + const now = new Date() + // Change to UTC+8 + now.setHours(now.getHours() + 8) + const newYear = now.getFullYear() + console.log('Prepare to update notice year to', newYear) + + const noticeContent = `Apache ECharts +Copyright 2017-${newYear} The Apache Software Foundation + +This product includes software developed at +The Apache Software Foundation (https://www.apache.org/).` + + const repoCtx = context.repo + + const repoInfo = (await octokit.rest.repos.get(repoCtx)).data + const defaultBranchName = repoInfo.default_branch + const remoteNoticeFile = (await octokit.rest.repos.getContent({ + ...repoCtx, + path: 'NOTICE', + ref: defaultBranchName + })).data + const remoteNoticeContent = base64ToUtf8(remoteNoticeFile.content) + if (remoteNoticeContent === noticeContent) { + console.log('NOTICE year is already updated.') + return + } + + console.log('Ready to update the NOTICE file:\n' + noticeContent) + + const defaultBranch = (await octokit.rest.repos.getBranch({ + ...repoCtx, + branch: defaultBranchName + })).data + + const newBranchName = `bot/update-notice-year/${newYear}` + await octokit.rest.git.createRef({ + ...repoCtx, + ref: `refs/heads/${newBranchName}`, + sha: defaultBranch.commit.sha + }) + console.log('Created a new branch:', newBranchName) + + await octokit.rest.repos.createOrUpdateFileContents({ + ...repoCtx, + path: 'NOTICE', + message: `chore: update NOTICE year to ${newYear}`, + content: utf8ToBase64(noticeContent), + sha: remoteNoticeFile.sha, + branch: newBranchName + }) + + console.log('Updated the NOTICE file on the new branch') + + const pr = (await octokit.rest.pulls.create({ + ...repoCtx, + head: newBranchName, + base: defaultBranchName, + maintainer_can_modify: true, + title: `chore: update NOTICE year to ${newYear}`, + body: `## Brief Information + +This pull request is in the type of: + +- [ ] bug fixing +- [ ] new feature +- [x] others + +### What does this PR do? + +Update notice year to ${newYear}. 💖 + +Happy new year! 祝大家新年快乐!🎇` + })).data + + console.log(`Opened PR #${pr.number} for updating the NOTICE file`) +} + +function utf8ToBase64(data) { + return Buffer.from(data, 'utf-8').toString('base64') +} + +function base64ToUtf8(data) { + return Buffer.from(data, 'base64').toString('utf-8') +} diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d11e9faa6e..0a15bd6348 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -25,12 +25,12 @@ jobs: run: | echo "FETCH_DEPTH=$(($PR_COMMIT_COUNT + 1))" >> $GITHUB_ENV - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: fetch-depth: ${{ env.FETCH_DEPTH }} - name: Use Node.js ${{ matrix.node-version }} - uses: actions/setup-node@v3 + uses: actions/setup-node@v4 with: node-version: ${{ matrix.node-version }} @@ -67,10 +67,10 @@ jobs: node-version: [18.x] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Use Node.js ${{ matrix.node-version }} - uses: actions/setup-node@v3 + uses: actions/setup-node@v4 with: node-version: ${{ matrix.node-version }} diff --git a/.github/workflows/nightly-next.yml b/.github/workflows/nightly-next.yml index bf40c6e71e..1de3715395 100644 --- a/.github/workflows/nightly-next.yml +++ b/.github/workflows/nightly-next.yml @@ -18,12 +18,12 @@ jobs: node-version: [18.x] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: ref: next - name: Use Node.js ${{ matrix.node-version }} - uses: actions/setup-node@v3 + uses: actions/setup-node@v4 with: registry-url: https://registry.npmjs.org/ node-version: ${{ matrix.node-version }} diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml index 085b80d306..cf65881730 100644 --- a/.github/workflows/nightly.yml +++ b/.github/workflows/nightly.yml @@ -18,10 +18,10 @@ jobs: node-version: [18.x] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Use Node.js ${{ matrix.node-version }} - uses: actions/setup-node@v3 + uses: actions/setup-node@v4 with: registry-url: https://registry.npmjs.org/ node-version: ${{ matrix.node-version }} diff --git a/.github/workflows/source-release.yml b/.github/workflows/source-release.yml index eab4413df9..5719131170 100644 --- a/.github/workflows/source-release.yml +++ b/.github/workflows/source-release.yml @@ -14,10 +14,10 @@ jobs: node-version: [18.x] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Use Node.js ${{ matrix.node-version }} - uses: actions/setup-node@v3 + uses: actions/setup-node@v4 with: node-version: ${{ matrix.node-version }} @@ -59,10 +59,10 @@ jobs: node-version: [18.x] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Use Node.js ${{ matrix.node-version }} - uses: actions/setup-node@v3 + uses: actions/setup-node@v4 with: node-version: ${{ matrix.node-version }} @@ -114,7 +114,7 @@ jobs: steps: - name: Use Node.js ${{ matrix.node-version }} - uses: actions/setup-node@v3 + uses: actions/setup-node@v4 with: node-version: ${{ matrix.node-version }} diff --git a/.github/workflows/update-notice-year.yml b/.github/workflows/update-notice-year.yml new file mode 100644 index 0000000000..8aa325327b --- /dev/null +++ b/.github/workflows/update-notice-year.yml @@ -0,0 +1,24 @@ +name: Update NOTICE year + +on: + schedule: + # 1/1 00:00 UTC+8 + - cron: '0 16 31 12 *' + workflow_dispatch: + +jobs: + update-notice-year: + if: ${{ github.repository_owner == 'apache' }} + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + with: + sparse-checkout: | + .github/workflows/.scripts + + - uses: actions/github-script@v7 + with: + script: | + const updateNoticeYear = require('.github/workflows/.scripts/update-notice-year.js') + await updateNoticeYear({ octokit: github, context }) diff --git a/NOTICE b/NOTICE index 0877faab81..6f7322151d 100644 --- a/NOTICE +++ b/NOTICE @@ -1,5 +1,5 @@ Apache ECharts -Copyright 2017-2023 The Apache Software Foundation +Copyright 2017-2024 The Apache Software Foundation This product includes software developed at -The Apache Software Foundation (https://www.apache.org/). +The Apache Software Foundation (https://www.apache.org/). \ No newline at end of file diff --git a/build/config.js b/build/config.js index 092f7b23a5..9d639e476d 100644 --- a/build/config.js +++ b/build/config.js @@ -108,7 +108,7 @@ exports.createECharts = function (opt = {}) { opt, { name: 'echarts', - // Ignore default exports, which is only for compitable code like: + // Ignore default exports, which is only for compatible code like: // import echarts from 'echarts/lib/echarts'; exports: 'named', format: format diff --git a/build/pre-publish.js b/build/pre-publish.js index 6975c2744b..cd99e65793 100644 --- a/build/pre-publish.js +++ b/build/pre-publish.js @@ -19,7 +19,7 @@ /** * [Create CommonJS files]: - * Compatible with prevoius folder structure: `echarts/lib` exists in `node_modules` + * Compatible with previous folder structure: `echarts/lib` exists in `node_modules` * (1) Build all files to CommonJS to `echarts/lib`. * (2) Remove __DEV__. * (3) Mount `echarts/src/export.js` to `echarts/lib/echarts.js`. @@ -87,7 +87,7 @@ const compileWorkList = [ module: 'ES2015', rootDir: ecDir, outDir: tmpDir, - // Generate types when buidling esm + // Generate types when building esm declaration: true, declarationDir: typesDir }, @@ -206,7 +206,7 @@ module.exports = async function () { }; async function runTsCompile(localTs, compilerOptions, srcPathList) { - // Must do it. becuase the value in tsconfig.json might be different from the inner representation. + // Must do it, because the value in tsconfig.json might be different from the inner representation. // For example: moduleResolution: "NODE" => moduleResolution: 2 const {options, errors} = localTs.convertCompilerOptionsFromJson(compilerOptions, ecDir); diff --git a/src/component/axis/AxisBuilder.ts b/src/component/axis/AxisBuilder.ts index c7b48dcafd..1644d50246 100644 --- a/src/component/axis/AxisBuilder.ts +++ b/src/component/axis/AxisBuilder.ts @@ -17,7 +17,9 @@ * under the License. */ -import {retrieve, defaults, extend, each, isObject, map, isString, isNumber, isFunction} from 'zrender/src/core/util'; +import { + retrieve, defaults, extend, each, isObject, map, isString, isNumber, isFunction, retrieve2 +} from 'zrender/src/core/util'; import * as graphic from '../../util/graphic'; import {getECData} from '../../util/innerStore'; import {createTextStyle} from '../../label/labelStyle'; @@ -68,7 +70,7 @@ export interface AxisBuilderCfg { tickDirection?: number labelDirection?: number /** - * Usefull when onZero. + * Useful when onZero. */ labelOffset?: number /** @@ -776,6 +778,29 @@ function buildAxisLabel( const tickCoord = axis.dataToCoord(tickValue); + const align = itemLabelModel.getShallow('align', true) + || labelLayout.textAlign; + const alignMin = retrieve2( + itemLabelModel.getShallow('alignMinLabel', true), + align + ); + const alignMax = retrieve2( + itemLabelModel.getShallow('alignMaxLabel', true), + align + ); + + const verticalAlign = itemLabelModel.getShallow('verticalAlign', true) + || itemLabelModel.getShallow('baseline', true) + || labelLayout.textVerticalAlign; + const verticalAlignMin = retrieve2( + itemLabelModel.getShallow('verticalAlignMinLabel', true), + verticalAlign + ); + const verticalAlignMax = retrieve2( + itemLabelModel.getShallow('verticalAlignMaxLabel', true), + verticalAlign + ); + const textEl = new graphic.Text({ x: tickCoord, y: opt.labelOffset + opt.labelDirection * labelMargin, @@ -784,11 +809,12 @@ function buildAxisLabel( z2: 10 + (labelItem.level || 0), style: createTextStyle(itemLabelModel, { text: formattedLabel, - align: itemLabelModel.getShallow('align', true) - || labelLayout.textAlign, - verticalAlign: itemLabelModel.getShallow('verticalAlign', true) - || itemLabelModel.getShallow('baseline', true) - || labelLayout.textVerticalAlign, + align: index === 0 + ? alignMin + : index === labels.length - 1 ? alignMax : align, + verticalAlign: index === 0 + ? verticalAlignMin + : index === labels.length - 1 ? verticalAlignMax : verticalAlign, fill: isFunction(textColor) ? textColor( // (1) In category axis with data zoom, tick is not the original diff --git a/src/coord/axisCommonTypes.ts b/src/coord/axisCommonTypes.ts index bb4d1b7bf1..aa92868e3e 100644 --- a/src/coord/axisCommonTypes.ts +++ b/src/coord/axisCommonTypes.ts @@ -17,6 +17,7 @@ * under the License. */ +import { TextAlign, TextVerticalAlign } from 'zrender/src/core/types'; import { TextCommonOption, LineStyleOption, OrdinalRawValue, ZRColor, AreaStyleOption, ComponentOption, ColorString, @@ -223,6 +224,14 @@ interface AxisLabelBaseOption extends Omit { showMinLabel?: boolean, // true | false | null/undefined (auto) showMaxLabel?: boolean, + // 'left' | 'center' | 'right' | null/undefined (auto) + alignMinLabel?: TextAlign, + // 'left' | 'center' | 'right' | null/undefined (auto) + alignMaxLabel?: TextAlign, + // 'top' | 'middle' | 'bottom' | null/undefined (auto) + verticalAlignMinLabel?: TextVerticalAlign, + // 'top' | 'middle' | 'bottom' | null/undefined (auto) + verticalAlignMaxLabel?: TextVerticalAlign, margin?: number, rich?: Dictionary /** diff --git a/src/core/echarts.ts b/src/core/echarts.ts index aaf538c72e..69978fd34b 100644 --- a/src/core/echarts.ts +++ b/src/core/echarts.ts @@ -429,10 +429,14 @@ class ECharts extends Eventful { zrender.registerSSRDataGetter(el => { const ecData = getECData(el); + const dataIndex = ecData.dataIndex; + if (dataIndex == null) { + return; + } const hashMap = createHashMap(); hashMap.set('series_index', ecData.seriesIndex); - hashMap.set('data_index', ecData.dataIndex); - hashMap.set('ssr_type', ecData.ssrType); + hashMap.set('data_index', dataIndex); + ecData.ssrType && hashMap.set('ssr_type', ecData.ssrType); return hashMap; }); diff --git a/src/data/helper/sourceHelper.ts b/src/data/helper/sourceHelper.ts index f0277d3a1d..d1aa89d5ef 100644 --- a/src/data/helper/sourceHelper.ts +++ b/src/data/helper/sourceHelper.ts @@ -314,7 +314,7 @@ export function querySeriesUpstreamDatasetModel( export function queryDatasetUpstreamDatasetModels( datasetModel: DatasetModel ): DatasetModel[] { - // Only these attributes declared, we by defualt reference to `datasetIndex: 0`. + // Only these attributes declared, we by default reference to `datasetIndex: 0`. // Otherwise, no reference. if (!datasetModel.get('transform', true) && !datasetModel.get('fromTransformResult', true) diff --git a/src/util/innerStore.ts b/src/util/innerStore.ts index a1ecb2a78c..cf9ede71a6 100644 --- a/src/util/innerStore.ts +++ b/src/util/innerStore.ts @@ -75,7 +75,7 @@ export const setCommonECData = (seriesIndex: number, dataType: SeriesDataType, d childECData.seriesIndex = seriesIndex; childECData.dataIndex = dataIdx; childECData.dataType = dataType; - childECData.ssrType === 'chart'; + childECData.ssrType = 'chart'; }); } } diff --git a/test/axis-align-lastLabel.html b/test/axis-align-lastLabel.html new file mode 100644 index 0000000000..559f3b8365 --- /dev/null +++ b/test/axis-align-lastLabel.html @@ -0,0 +1,150 @@ + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ + + + + + + + + + + + + + + + diff --git a/test/svg-ssr.html b/test/svg-ssr.html index af9a4dab77..c08e02fa7d 100644 --- a/test/svg-ssr.html +++ b/test/svg-ssr.html @@ -41,7 +41,6 @@ - +