Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[FEATURE] [5.0] time axis label formatter #12859

Merged
merged 13 commits into from Jul 29, 2020
Merged

[FEATURE] [5.0] time axis label formatter #12859

merged 13 commits into from Jul 29, 2020

Conversation

Ovilia
Copy link
Contributor

@Ovilia Ovilia commented Jun 28, 2020

Brief Information

This pull request is in the type of:

  • bug fixing
  • new feature
  • others

What does this PR do?

Improves label formatting for time axes.

Fixed issues

Details

Before & After

The "After" parts are at proposals stage by now.

1. Default time axis labels don't have nice format and display information not so important

Before:

屏幕快照 2020-06-29 13 50 11

    xAxis: {
        type: 'time',
        interval: DAY / 4,
        axisLabel: {} // default
    },

For example, in the above case, labels of each day are all in the format of MM-DD\nYYYY, but the months and years are all the same so they should not be displayed so many times to distract users for the real important information: date.

After:

The default result of the above case have the interval of 6 hours, thus default hour formatter (may be 'hh:mm') should be used to display labels in hours and day formatter ('YYYY-MM-DD') be used when day changes. This is the expected result:

屏幕快照 2020-06-29 13 50 11副本

And the default style of the primary level (like day in this case) and secondary level (hour in this case) are different (maybe we can use a lighter text color for secondary level labels) and can be customized though axisLabel.rich.primaryStyle and secondaryStyle.

2. Multiple labels with the same text may be displayed by default

Before:

Also with the above example, we can see that there are three 05-12 2012 labels, which is confusing. Especially, this should not be the default behavior of labels.

After:

There won't be labels with the same date and hour labels will be displayed in the above case.

3. Custom style is too complex to write with rich text

Before:

屏幕快照 2020-06-29 14 05 07

To get the above result, the user has to write the following long code of formatter. Users may not know they could do this, even with clear documentation.

formatter: function (value) {
    if (value % DAY === 0) {
        var month = echarts.format.formatTime('M月', value)
        var dayOfM = echarts.format.formatTime('d日', value)
        return '{strong|' + dayOfM + '}\n{lite|' + month + '}';
    }
    else {
        var formatted = echarts.format.formatTime('h点', value);
        return formatted;
    }
},
rich: {
    strong: {
        color: '#2784e8',
        fontSize: 14,
        fontWeight: 700
    },
    lite: {
        color: '#999',
        lineHeight: 15,
        fontSize: 10
    }
}
After:

Current callback form of formatter will also be available, but we also provide a much simpler way to write if the logic is not so complex.

We will support formatters for varied levels like year, month, day, hour, minute and etc., and each of them supports the formatter in either forms of {hh}:{mm}:{ss} for simple plain text or {xxx | {hh}:{mm}:ss} for rich text, whose style can be configured in axisLabel.rich.

4. Cannot use different formatters for different intervals

Consider the "2020 Feb" and "Feb" in the following example. The user want to use different formatters ('YYYY-MMM' in case A and 'MMM' in case B.

case A:

    |_____________|_____________|_____________|__ ...
  30th         2020 Feb        2nd            3rd


case B:

    |_____________|_____________|_____________|__ ...
  2020           Feb           Mar           Apr
Before:

It cannot be done.

After:

formatter.month defines formatters when the tick value is a month value (by checking if it's the first day of a month). And it can be in the form of an array of formatters like: ['YYYY-MMM', 'MMM'].

This means, if the label is a first-level (also called primary level) as in case A, where the levels of the labels are day, month, day, day, it uses the first element in the array for the second tick, which is 'YYYY-MMM' and gets "2020 Feb". Similarly, in case B, where the levels of the labels are year, month, month, month, it uses the second element in the array for the second tick and gets "Feb".

5. Cannot display ticks exactly on the first day of a month or week

Before:

There are different days in each month, e.g., 28/29 for Feb and 31 for July. There is no way to make sure the first day is displayed.

屏幕快照 2020-06-29 16 58 19

This looks a bit arbitrary when choosing the days to be displayed with ticks. And it cannot be changed even with configuration.

After:

If the months are different with min and max data (but years are the same), the primary level will be month and we make sure that the first day of each month is displayed with ticks. Note that in this case, the space between each ticks are not the same because days in months are not.

6. i18n

Before:

Using formatter in the callback form like echarts.format.formatTime('M月', value). It is quite challenging for developers not so familiar with JavaScript.

After:

We can provide frequently used time formats like YYYY for full year, MM for month number and so on. Moment.js and can be served as references.

i18n of the time axis should be considered along with other i18n requirements in ECharts, like toolbox and so on.

We are considering embed with English and Chinese language by default with ECharts and other languages can be used as extensions. We are still discuss about the details on this. But the bottom line is, it won't be necessary for developers to implement by using code as low level as echarts.format.formatTime.

Usage

Are there any API changes?

  • The API has been changed.

Before:

axisLabel: {
    formatter: string | function    // If it's a string, rich text is supported. If it's a function, it can return rich text.
    rich: { ... }  // For label rich text if formatter uses rich text. Default value is null.
},
interval: number  // The exact interval of ticks

After:

axisLabel: {
    formatter: string | function | object  // If it's an object, it's in the type of LevelFormats
    rich: {  // There are two rich styles defined in ECharts by default, which user may use in formatter
        primary: {
            color: '#000'  // We use a darker color for primary level text
        }
    }
},
interval: number | string[]  // If it's string[], it should be like ['month', 'day'] meaning the tick intervals may be month or day

LevelFormats is defined as:

type LevelFormats = {
    year?: string | string[],
    month?: string | string[],
    day?: string | string[],
    hour?: string | string[],
    ...
}

The string can either be a plain text like '{hh}:{mm}:{ss}' or a rich text like strong | '{hh}:{mm}' and define the rich style strong in formatter.

Well... I know this sounds a little complex right now. But it's actually turns easier when you see the real world examples.

Example of Usage

1. Default

No formatter is required. Label text will display as what we think work for general cases.

2. Custom primary and secondary styles

case A:

    |_____________|_____________|_____________|__ ...
  30th         2020 Feb        2nd            3rd


case B:

    |_____________|_____________|_____________|__ ...
  2020           Feb           Mar           Apr

If the user wants to use red color for 2020 Feb in case A and 2020 in case B, this is what required:

xAxis: {
    // no formatter is required
    rich: {
        primary: {
            color: 'red'
        }
    }
}

3. Display only weeks of year

xAxis: {
    interval: ['week'],
    axisLabel: {
        formatter: {
            week: 'Week w'
        }
    }
}

Related test cases or examples to use the new APIs

NA.

Others

Merging options

  • Please squash the commits into a single one when merge.

Other information

@echarts-bot
Copy link

echarts-bot bot commented Jun 28, 2020

Thanks for your contribution!
The community will review it ASAP. In the meanwhile, please checkout the coding standard and Wiki about How to make a pull request.

@Ovilia Ovilia changed the base branch from master to next June 28, 2020 07:11
@Ovilia Ovilia added this to the 5.0.0 milestone Jun 29, 2020
@100pah
Copy link
Member

100pah commented Jun 29, 2020

@Ovilia Add something that could be considered:

(I)

The callback of axisLabel.fomatter may be also need to add some params?
For example:

xAxis: {
    type: 'time'
    formatter: function (value, idx, params) {
        // need to know here:
        // (1) this label is on a primary level tick or a secondary level tick
        // (2) what the level is ('day' or 'month' or ...)
    }
}

(II)

The string can either be a plain text like '{hh}:{mm}:{ss}' or a rich text like strong | '{hh}:{mm}' and define the rich style strong in formatter.

Follow the current way of "reference the variable" like {strong|xxx{value}xxx},
I think the format would be: {strong|xxx{hh}:{mm}xxx} (no need quote mark)

(III)

We can provide frequently used time formats like YYYY for full year, MM for month number and so on. Moment.js and can be served as references.

If users intend to use Moment.js, do we prefer users to
(a) "use Moment in the formatter callback" ?
or (b) "use Moment as a plugin for formatter pattern" ?

If (a), nothing need to considered in this feature design.
If (b), that means users can use Moment notations in our "formatter patterns".
Thus we need to consider the conflicts between the notation from third party libs and our existing notations.
(for example, suppose the third party lib uses "a" to represent something, but in echarts "formatter patterns" {a} has been reserve to other meanings.)
If we want to avoid this conflict, may be other time-specific prefix need to considered in formatter pattern.
For example: {%MM}:{%dd}, where % is only used for time notation.

(IV)

For i18n, probably we could simply add some new built-in notations? (not sure)
For example:

  • {MMM}: represents: zh: 2月 , en: Feb
  • {MMMM}: represents: zh: 二月, en: February
  • {do}: represents: zh: 5日, en: 5th
  • {ee}: represents: zh: , en: Tu
  • {eee}: represents: zh: 周二, en: Tue
  • {eeee}: represents: zh: 星期二, en: Tuesday

@quillblue
Copy link
Contributor

@Ovilia Some additional thoughts based on this topic:

Extend to Major/Minor axis ticks instead of use Primary/Secondary style

This will help general customization on all geo coord axis ticks/labels not only simply specified to time axis. For time axis, user can use two formatter if they need. (more like the ways build charts in Microsoft Office)

For i18n

I would prefer to use any of external formatter (Moment for example) in callback, as various external time formatter have already provided enough functionality on culture/date preference/lanuange etc. This would also help seperate on format part and render part, which I think in axis component render is what need to be focused on.
For default formatter based on value, in my opinion may need to fully use of util to handle the transformation from date to text.

@Ovilia
Copy link
Contributor Author

Ovilia commented Jul 9, 2020

@quillblue Thanks for your comments. Here are my thoughts on this.

Primary & Secondary Ticks

I think secondary ticks may not be a minor tick. Consider the following case:

    |_____________|_____________|_____________|__ ...
  2020           Feb           Mar           Apr

By our definition, 2020 is a primary tick since it's in the year level, while others are secondary ticks because they are in the month level. But in most cases, the tick color of 2020 may not be different than others while text color may be different.

Also, primary and secondary are two default rich text styles we provide. There may also be a tertiary level like week info in the above case. So major and minor ticks may not deal with situations like this.

i18n

First, we have to provide some frequently used formats like 'HH:mm:ss'. It's unwise to ask users to install another library if they want simple time formats like this. On the other side, if we provide too many formats, it will increase package size while most people won't use it. So we should be prudent when deciding which formats to support.

Here are my proposed formatters supported:

Group Template Examples (EN) (ZH) Moment.js date-fns
Year yyyy 2020, 1990 YYYY SAME
yy 20, 90 YY SAME
Quarter Q 1, 2 SAME SAME
Month MMMM January, February 一月、二月 SAME SAME
MMM Jan, Feb 一、二 SAME SAME
MM 01, 02 SAME SAME
M 1, 2 SAME SAME
Week of Year ww 01, 02 SAME SAME
w 1, 2 SAME SAME
Day of Month dd 01, 02 DD SAME
d 1, 2 D SAME
Day of Week eeee Monday, Tuesday 周一、周二星期一、星期二 dddd SAME
eeeee M, T 一、二 ddd SAME
e 1, 2 d SAME
Hour (0-23) HH 00, 23 SAME SAME
H 0, 23 SAME SAME
Hour (1-12) hh 01, 12 SAME SAME
h 1, 12 SAME SAME
Minute mm 00, 01 SAME SAME
m 0, 1 SAME SAME
Second ss 00, 01 SAME SAME
s 0, 1 SAME SAME
Millisecond SSS 000, 001 SAME SAME
S 0, 1 SAME SAME

@100pah
Copy link
Member

100pah commented Jul 9, 2020

@Ovilia

(1)

In the column of (ZH)
Could
Monday, Tuesday <=> 星期一,星期二
Mon, Tue <=> 周一,周二
Mo, Tu <=> 一,二

(2)

How about the current h,hh in echarts.format.formatTime.
(A) Make it the same as H, HH, which will be different from date-fns and make it hard to modify the meaning in future.
(B) Break change it to represent 1~12, which might need to create a new util format method and deprecated echarts.format.formatTime.
(I am not sure)

@Ovilia
Copy link
Contributor Author

Ovilia commented Jul 9, 2020

@100pah I think that makes sense. 一、二 should take ee if we have future feature requests. Or do you think we should provide ee for Mo, Tu and 一,二 for now?

I would suggest break change it to 1~12 because it's more universal and it cannot provide extra help to HH.

@quillblue
Copy link
Contributor

@Ovilia for the case you mention I treat 2020 as a major tick on the level of year (though from straight understanding it is on the same monthly level of Feb, Mar), and minor tick on the level of month.

For i18n, I agree with that common and widely used date formatter is provided by ECharts self maybe in util since format will be used not only in axis but also maybe in series and etc.

@Ovilia Ovilia marked this pull request as ready for review July 21, 2020 09:46
@Ovilia
Copy link
Contributor Author

Ovilia commented Jul 21, 2020

Most of the functions above have been implemented, but not week of year. Leaving it for another PR.

'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'
],
dayOfWeek: [
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It should be Chinese month and monthAbbr?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes.

@pissang pissang changed the title feat: time axis label formatter [FEATURE] [5.0] time axis label formatter Jul 24, 2020
@pissang pissang merged commit fd5750b into next Jul 29, 2020
@Ovilia Ovilia deleted the time-formatter branch July 29, 2020 02:22
Ovilia added a commit that referenced this pull request Jul 29, 2020
@Ovilia Ovilia added the PR: awaiting doc Document changes is required for this PR. label Nov 9, 2020
@Ovilia Ovilia removed the PR: awaiting doc Document changes is required for this PR. label Dec 28, 2020
@Maskedman99
Copy link

Hi @Ovilia is interval: ['week'] supported ? I didn't find any documentation related to it. I also tried it in the latest version, but it didn't work.

@Ovilia
Copy link
Contributor Author

Ovilia commented Mar 25, 2022

Hi @Ovilia is interval: ['week'] supported ? I didn't find any documentation related to it. I also tried it in the latest version, but it didn't work.

Week of year is not supported. Please follow the doc rather than the PR description because the PR description is written before the implementation. If you wish to implement week of year, you can use third party libs like moment and then use callback formatter to return the value.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

5 participants