Skip to content

Commit

Permalink
feat: support decimal fold
Browse files Browse the repository at this point in the history
  • Loading branch information
liihuu committed Feb 28, 2024
1 parent 5844a05 commit 50341d3
Show file tree
Hide file tree
Showing 21 changed files with 133 additions and 56 deletions.
10 changes: 9 additions & 1 deletion docs/en-US/guide/chart-api.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,9 @@
customApi?: {
formatDate?: (dateTimeFormat: Intl.DateTimeFormat, timestamp: number, format: string, type: number) => string
formatBigNumber?: (value: string | number) => string
},
}
thousandsSeparator?: string
decimalFoldThreshold?: number
}
) => Chart
```
Expand All @@ -46,6 +47,7 @@ Initialize a chart and return the chart instance.
- `formatDate` formats a date.
- `formatBigNumber` format big numbers, such as 1000 into 1k, 1000000 into 1M, etc.\
- `thousandsSeparator` thousands separator
- `decimalFoldThreshold` decimal fold threshold


## dispose(dcs)
Expand Down Expand Up @@ -462,6 +464,12 @@ Format date. `format`, such as 'YYYY-MM-DD HH:mm:ss'.
```
Format thousands separator.

### utils.foldDecimal(value, threshold)
```typescript
(value: string | number, threshold: number) => string
```
Fold decimal.


### utils.calcTextWidth(text, size, weight, family)
```typescript
Expand Down
2 changes: 2 additions & 0 deletions docs/en-US/guide/overlay.md
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,8 @@ Customize an overlay, then add it globally through `klinecharts.registerOverlay`
},
// thousands separator
thousandsSeparator: string,
// decimal fold threshold
decimalFoldThreshold: number
// Constructor for objects that format date and time, see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/DateTimeFormat for details
dateTimeFormat: Intl. DateTimeFormat
// The default style, that is, the overlay in the global style configuration, the type participates in the overlay in [style]
Expand Down
8 changes: 8 additions & 0 deletions docs/guide/chart-api.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
formatBigNumber?: (value: string | number) => string
}
thousandsSeparator?: string
decimalFoldThreshold?: number
}
) => Chart
```
Expand All @@ -46,6 +47,7 @@
- `formatDate` 格式化日期。
- `formatBigNumber` 格式化大的数字,如1000转换成1k,1000000转换为1M等。
- `thousandsSeparator` 千分符
- `decimalFoldThreshold` 小数折叠阈值


## dispose(dcs)
Expand Down Expand Up @@ -465,6 +467,12 @@
```
格式化日期千分符。

### utils.foldDecimal(value, threshold)
```typescript
(value: string | number, threshold: number) => string
```
折叠小数。


### utils.calcTextWidth(text, size, weight, family)
```typescript
Expand Down
2 changes: 2 additions & 0 deletions docs/guide/overlay.md
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,8 @@
}
// 千分符
thousandsSeparator: string
// 小数折叠阈值
decimalFoldThreshold: number
// 格式化日期和时间的对象的构造器,详情参阅 https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Intl/DateTimeFormat
dateTimeFormat: Intl.DateTimeFormat
// 默认样式,即全局样式配置中的overlay,类型参与[样式]中的overlay
Expand Down
1 change: 1 addition & 0 deletions src/Options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,4 +74,5 @@ export interface Options {
styles?: string | DeepPartial<Styles>
customApi?: Partial<CustomApi>
thousandsSeparator?: string
decimalFoldThreshold?: number
}
12 changes: 12 additions & 0 deletions src/common/utils/format.ts
Original file line number Diff line number Diff line change
Expand Up @@ -116,3 +116,15 @@ export function formatThousands (value: string | number, sign: string): string {
}
return vl.replace(/(\d)(?=(\d{3})+$)/g, $1 => `${$1}${sign}`)
}

export function foldDecimal (value: string | number, threshold: number): string {
const vl = `${value}`
const match = vl.match(/\.0*(\d+)/)
if (isValid(match) && parseInt(match[1]) > 0) {
const count = match[0].length - 1 - match[1].length
if (count >= threshold) {
return vl.replace(/\.0*/, `.0{${count}}`)
}
}
return vl
}
1 change: 1 addition & 0 deletions src/component/Overlay.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ export interface OverlayCreateFiguresCallbackParams {
barSpace: BarSpace
precision: Precision
thousandsSeparator: string
decimalFoldThreshold: number
dateTimeFormat: Intl.DateTimeFormat
defaultStyles: OverlayStyle
xAxis: Nullable<XAxis>
Expand Down
5 changes: 3 additions & 2 deletions src/component/YAxis.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import type Bounding from '../common/Bounding'
import { isNumber, isValid } from '../common/utils/typeChecks'
import { index10, log10 } from '../common/utils/number'
import { calcTextWidth } from '../common/utils/canvas'
import { formatPrecision, formatThousands } from '../common/utils/format'
import { formatPrecision, formatThousands, foldDecimal } from '../common/utils/format'

import AxisImp, { type AxisTemplate, type Axis, type AxisRange, type AxisTick, type AxisCreateTicksParams } from './Axis'

Expand Down Expand Up @@ -255,6 +255,7 @@ export default abstract class YAxisImp extends AxisImp implements YAxis {
const type = this.getType()
const indicators = chartStore.getIndicatorStore().getInstances(pane.getId())
const thousandsSeparator = chartStore.getThousandsSeparator()
const decimalFoldThreshold = chartStore.getDecimalFoldThreshold()
let precision = 0
let shouldFormatBigNumber = false
if (this.isInCandle()) {
Expand Down Expand Up @@ -290,7 +291,7 @@ export default abstract class YAxisImp extends AxisImp implements YAxis {
break
}
}
v = formatThousands(v, thousandsSeparator)
v = foldDecimal(formatThousands(v, thousandsSeparator), decimalFoldThreshold)
const validYNumber = isNumber(validY)
if (
y > textHeight &&
Expand Down
6 changes: 3 additions & 3 deletions src/extension/overlay/fibonacciLine.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
* limitations under the License.
*/

import { formatThousands } from '../../common/utils/format'
import { formatThousands, foldDecimal } from '../../common/utils/format'
import { isNumber } from '../../common/utils/typeChecks'

import { type OverlayTemplate } from '../../component/Overlay'
Expand All @@ -26,7 +26,7 @@ const fibonacciLine: OverlayTemplate = {
needDefaultPointFigure: true,
needDefaultXAxisFigure: true,
needDefaultYAxisFigure: true,
createPointFigures: ({ coordinates, bounding, overlay, precision, thousandsSeparator }) => {
createPointFigures: ({ coordinates, bounding, overlay, precision, thousandsSeparator, decimalFoldThreshold }) => {
const points = overlay.points
if (coordinates.length > 0) {
const lines: LineAttrs[] = []
Expand All @@ -39,7 +39,7 @@ const fibonacciLine: OverlayTemplate = {
const valueDif = points[0].value - points[1].value
percents.forEach(percent => {
const y = coordinates[1].y + yDif * percent
const value = formatThousands(((points[1].value ?? 0) + valueDif * percent).toFixed(precision.price), thousandsSeparator)
const value = foldDecimal(formatThousands(((points[1].value ?? 0) + valueDif * percent).toFixed(precision.price), thousandsSeparator), decimalFoldThreshold)
lines.push({ coordinates: [{ x: startX, y }, { x: endX, y }] })
texts.push({
x: startX,
Expand Down
6 changes: 3 additions & 3 deletions src/extension/overlay/priceLine.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,15 @@

import { type OverlayTemplate } from '../../component/Overlay'

import { formatThousands } from '../../common/utils/format'
import { formatThousands, foldDecimal } from '../../common/utils/format'

const priceLine: OverlayTemplate = {
name: 'priceLine',
totalStep: 2,
needDefaultPointFigure: true,
needDefaultXAxisFigure: true,
needDefaultYAxisFigure: true,
createPointFigures: ({ coordinates, bounding, precision, overlay, thousandsSeparator }) => {
createPointFigures: ({ coordinates, bounding, precision, overlay, thousandsSeparator, decimalFoldThreshold }) => {
const { value = 0 } = (overlay.points)[0]
return [
{
Expand All @@ -35,7 +35,7 @@ const priceLine: OverlayTemplate = {
attrs: {
x: coordinates[0].x,
y: coordinates[0].y,
text: formatThousands(value.toFixed(precision.price), thousandsSeparator),
text: foldDecimal(formatThousands(value.toFixed(precision.price), thousandsSeparator), decimalFoldThreshold),
baseline: 'bottom'
}
}
Expand Down
3 changes: 2 additions & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ import { logError, logTag, logWarn } from './common/utils/logger'
import {
clone, merge, isString, isNumber, isValid, isObject, isArray, isFunction, isBoolean
} from './common/utils/typeChecks'
import { formatValue, formatPrecision, formatBigNumber, formatDate, formatThousands } from './common/utils/format'
import { formatValue, formatPrecision, formatBigNumber, formatDate, formatThousands, foldDecimal } from './common/utils/format'
import { calcTextWidth } from './common/utils/canvas'
import { ActionType } from './common/Action'
import { IndicatorSeries } from './component/Indicator'
Expand Down Expand Up @@ -143,6 +143,7 @@ const utils = {
formatBigNumber,
formatDate,
formatThousands,
foldDecimal,
calcTextWidth,
getLinearSlopeIntercept,
getLinearYFromSlopeIntercept,
Expand Down
14 changes: 12 additions & 2 deletions src/store/ChartStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import type KLineData from '../common/KLineData'
import type Precision from '../common/Precision'
import type VisibleData from '../common/VisibleData'
import { getDefaultStyles, type Styles } from '../common/Styles'
import { isArray, isString, isValid, merge } from '../common/utils/typeChecks'
import { isArray, isNumber, isString, isValid, merge } from '../common/utils/typeChecks'

import { getDefaultCustomApi, type CustomApi, defaultLocale, type Options } from '../Options'

Expand Down Expand Up @@ -61,6 +61,9 @@ export default class ChartStore {
*/
private _thousandsSeparator: string = ','

// Decimal fold threshold
private _decimalFoldThreshold = 3

/**
* Data source
*/
Expand Down Expand Up @@ -121,7 +124,7 @@ export default class ChartStore {

setOptions (options?: Options): this {
if (isValid(options)) {
const { locale, timezone, styles, customApi, thousandsSeparator } = options
const { locale, timezone, styles, customApi, thousandsSeparator, decimalFoldThreshold } = options
if (isString(locale)) {
this._locale = locale
}
Expand All @@ -141,6 +144,9 @@ export default class ChartStore {
if (isString(thousandsSeparator)) {
this._thousandsSeparator = thousandsSeparator
}
if (isNumber(decimalFoldThreshold)) {
this._decimalFoldThreshold = decimalFoldThreshold
}
}
return this
}
Expand All @@ -161,6 +167,10 @@ export default class ChartStore {
return this._thousandsSeparator
}

getDecimalFoldThreshold (): number {
return this._decimalFoldThreshold
}

getPrecision (): Precision {
return this._precision
}
Expand Down
7 changes: 4 additions & 3 deletions src/view/CandleHighLowPriceView.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import { type CandleHighLowPriceMarkStyle } from '../common/Styles'

import ChildrenView from './ChildrenView'

import { formatPrecision, formatThousands } from '../common/utils/format'
import { formatPrecision, formatThousands, foldDecimal } from '../common/utils/format'
import { isValid } from '../common/utils/typeChecks'

export default class CandleHighLowPriceView extends ChildrenView {
Expand All @@ -31,6 +31,7 @@ export default class CandleHighLowPriceView extends ChildrenView {
const lowPriceMarkStyles = priceMarkStyles.low
if (priceMarkStyles.show && (highPriceMarkStyles.show || lowPriceMarkStyles.show)) {
const thousandsSeparator = chartStore.getThousandsSeparator()
const decimalFoldThreshold = chartStore.getDecimalFoldThreshold()
const precision = chartStore.getPrecision()
const yAxis = pane.getAxisComponent()
let high = Number.MIN_SAFE_INTEGER
Expand All @@ -55,7 +56,7 @@ export default class CandleHighLowPriceView extends ChildrenView {
if (highPriceMarkStyles.show && high !== Number.MIN_SAFE_INTEGER) {
this._drawMark(
ctx,
formatThousands(formatPrecision(high, precision.price), thousandsSeparator),
foldDecimal(formatThousands(formatPrecision(high, precision.price), thousandsSeparator), decimalFoldThreshold),
{ x: highX, y: highY },
highY < lowY ? [-2, -5] : [2, 5],
highPriceMarkStyles
Expand All @@ -64,7 +65,7 @@ export default class CandleHighLowPriceView extends ChildrenView {
if (lowPriceMarkStyles.show && low !== Number.MAX_SAFE_INTEGER) {
this._drawMark(
ctx,
formatThousands(formatPrecision(low, precision.price), thousandsSeparator),
foldDecimal(formatThousands(formatPrecision(low, precision.price), thousandsSeparator), decimalFoldThreshold),
{ x: lowX, y: lowY },
highY < lowY ? [2, 5] : [-2, -5],
lowPriceMarkStyles
Expand Down
4 changes: 2 additions & 2 deletions src/view/CandleLastPriceLabelView.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
*/

import { YAxisType } from '../common/Styles'
import { formatPrecision, formatThousands } from '../common/utils/format'
import { formatPrecision, formatThousands, foldDecimal } from '../common/utils/format'
import { isValid } from '../common/utils/typeChecks'

import View from './View'
Expand Down Expand Up @@ -54,7 +54,7 @@ export default class CandleLastPriceLabelView extends View {
} else {
text = formatPrecision(close, precision.price)
}
text = formatThousands(text, chartStore.getThousandsSeparator())
text = foldDecimal(formatThousands(text, chartStore.getThousandsSeparator()), chartStore.getDecimalFoldThreshold())
let x: number
let textAlgin: CanvasTextAlign
if (yAxis.isFromZero()) {
Expand Down
Loading

0 comments on commit 50341d3

Please sign in to comment.