Skip to content

Commit

Permalink
feat: pane support maximize and minimize
Browse files Browse the repository at this point in the history
  • Loading branch information
liihuu committed Sep 25, 2024
1 parent 8c658bf commit a14489f
Show file tree
Hide file tree
Showing 9 changed files with 222 additions and 106 deletions.
104 changes: 69 additions & 35 deletions src/Chart.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ import XAxisPane from './pane/XAxisPane'
import type DrawPane from './pane/DrawPane'
import SeparatorPane from './pane/SeparatorPane'

import { type PaneOptions, PanePosition, PANE_DEFAULT_HEIGHT, PaneIdConstants } from './pane/types'
import { type PaneOptions, PanePosition, PANE_DEFAULT_HEIGHT, PaneIdConstants, PaneState, PANE_MIN_HEIGHT } from './pane/types'

import type AxisImp from './component/Axis'
import { AxisPosition, type Axis } from './component/Axis'
Expand Down Expand Up @@ -152,12 +152,14 @@ export default class ChartImp implements Chart {
this._chartContainer = createDom('div', {
position: 'relative',
width: '100%',
height: '100%',
outline: 'none',
borderStyle: 'none',
cursor: 'crosshair',
boxSizing: 'border-box',
userSelect: 'none',
webkitUserSelect: 'none',
overflow: 'hidden',
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-expect-error
msUserSelect: 'none',
Expand All @@ -170,8 +172,8 @@ export default class ChartImp implements Chart {
}

_cacheChartBounding (): void {
this._chartBounding.width = Math.floor(this._container.clientWidth)
this._chartBounding.height = Math.floor(this._container.clientHeight)
this._chartBounding.width = Math.floor(this._chartContainer.clientWidth)
this._chartBounding.height = Math.floor(this._chartContainer.clientHeight)
}

private _initPanes (options?: Options): void {
Expand Down Expand Up @@ -303,42 +305,70 @@ export default class ChartImp implements Chart {
const totalHeight = this._chartBounding.height
const separatorSize = this._chartStore.getStyles().separator.size
const xAxisHeight = this._xAxisPane.getAxisComponent().getAutoSize()
let paneExcludeXAxisHeight = totalHeight - xAxisHeight - this._separatorPanes.size * separatorSize
if (paneExcludeXAxisHeight < 0) {
paneExcludeXAxisHeight = 0
let remainingHeight = totalHeight - xAxisHeight
if (remainingHeight < 0) {
remainingHeight = 0
}
let indicatorPaneTotalHeight = 0

this._drawPanes.forEach(pane => {
if (pane.getId() !== PaneIdConstants.CANDLE && pane.getId() !== PaneIdConstants.X_AXIS) {
let paneHeight = pane.getBounding().height
const paneMinHeight = pane.getOptions().minHeight
if (paneHeight < paneMinHeight) {
paneHeight = paneMinHeight
const maximizePane = this._drawPanes.find(pane => pane.getId() !== PaneIdConstants.X_AXIS && pane.getOptions().state === PaneState.Maximize)
if (isValid(maximizePane)) {
maximizePane.setBounding({ height: remainingHeight })
this._separatorPanes.get(maximizePane)?.setVisible(false)
this._drawPanes.forEach(pane => {
const paneId = pane.getId()
if (paneId !== maximizePane.getId() && paneId !== PaneIdConstants.X_AXIS) {
pane.setVisible(false)
this._separatorPanes.get(pane)?.setVisible(false)
}
if (indicatorPaneTotalHeight + paneHeight > paneExcludeXAxisHeight) {
indicatorPaneTotalHeight = paneExcludeXAxisHeight
paneHeight = Math.max(paneExcludeXAxisHeight - indicatorPaneTotalHeight, 0)
} else {
indicatorPaneTotalHeight += paneHeight
})
} else {
this._drawPanes.forEach(pane => {
pane.setVisible(true)
this._separatorPanes.get(pane)?.setVisible(true)
const paneId = pane.getId()
if (paneId !== PaneIdConstants.CANDLE && paneId !== PaneIdConstants.X_AXIS) {
if (isValid(this._separatorPanes.get(pane))) {
remainingHeight -= separatorSize
}
let paneHeight = PANE_MIN_HEIGHT
if (pane.getOptions().state !== PaneState.Minimize) {
paneHeight = pane.getOriginalBounding().height
const paneMinHeight = pane.getOptions().minHeight
if (paneHeight < paneMinHeight) {
paneHeight = paneMinHeight
}
}
if (paneHeight > remainingHeight) {
paneHeight = Math.max(remainingHeight, 0)
remainingHeight = 0
} else {
remainingHeight -= paneHeight
}
pane.setBounding({ height: paneHeight })
}
pane.setBounding({ height: paneHeight })
})
if (isValid(this._candlePane) && isValid(this._separatorPanes.get(this._candlePane))) {
remainingHeight -= separatorSize
}
})
const candlePaneHeight = paneExcludeXAxisHeight - indicatorPaneTotalHeight
this._candlePane?.setBounding({ height: candlePaneHeight })
this._xAxisPane.setBounding({ height: xAxisHeight })

let top = 0
this._drawPanes.forEach(pane => {
const separatorPane = this._separatorPanes.get(pane)
if (isValid(separatorPane)) {
separatorPane.setBounding({ height: separatorSize, top })
top += separatorSize
let candlePaneHeight = PANE_MIN_HEIGHT
if (this._candlePane?.getOptions().state !== PaneState.Minimize) {
candlePaneHeight = Math.max(remainingHeight, 0)
this._candlePane?.setOriginalBounding({ height: candlePaneHeight })
}
pane.setBounding({ top })
top += pane.getBounding().height
})
this._candlePane?.setBounding({ height: candlePaneHeight })
this._xAxisPane.setBounding({ height: xAxisHeight })

let top = 0
this._drawPanes.forEach(pane => {
const separatorPane = this._separatorPanes.get(pane)
if (isValid(separatorPane)) {
separatorPane.setBounding({ height: separatorSize, top })
top += separatorSize
}
pane.setBounding({ top })
top += pane.getBounding().height
})
}
}

private _measurePaneWidth (): void {
Expand Down Expand Up @@ -412,10 +442,14 @@ export default class ChartImp implements Chart {
if (options.id !== PaneIdConstants.CANDLE && isNumber(options.height) && options.height > 0) {
const minHeight = Math.max(options.minHeight ?? pane.getOptions().minHeight, 0)
const height = Math.max(minHeight, options.height)
pane.setBounding({ height })
pane.setOriginalBounding({ height })
shouldAdjust = true
shouldMeasureHeight = true
}
if (isValid(options.state)) {
shouldMeasureHeight = true
shouldAdjust = true
}
if (isValid(options.axis)) {
shouldAdjust = true
}
Expand Down Expand Up @@ -704,7 +738,7 @@ export default class ChartImp implements Chart {
paneId ??= createId(PaneIdConstants.INDICATOR)
const pane = this._createPane(IndicatorPane, paneId, paneOptions ?? {})
const height = paneOptions?.height ?? PANE_DEFAULT_HEIGHT
pane.setBounding({ height })
pane.setOriginalBounding({ height })
const result = this._chartStore.getIndicatorStore().addInstance(indicator, paneId, isStack ?? false)
if (result) {
this.adjustPaneViewport(true, true, true, true, true)
Expand Down
9 changes: 7 additions & 2 deletions src/pane/DrawPane.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ import type DrawWidget from '../widget/DrawWidget'
import type YAxisWidget from '../widget/YAxisWidget'

import Pane from './Pane'
import { type PaneOptions, PANE_MIN_HEIGHT, PaneIdConstants } from './types'
import { type PaneOptions, PANE_MIN_HEIGHT, PaneIdConstants, PaneState } from './types'

import type Chart from '../Chart'

Expand All @@ -40,7 +40,12 @@ export default abstract class DrawPane<C extends Axis = Axis> extends Pane {

private _axis: C

private readonly _options: PickPartial<DeepRequired<Omit<PaneOptions, 'id' | 'height'>>, 'position'> = { minHeight: PANE_MIN_HEIGHT, dragEnabled: true, axis: { name: 'normal', scrollZoomEnabled: true } }
private readonly _options: PickPartial<DeepRequired<Omit<PaneOptions, 'id' | 'height'>>, 'position'> = {
minHeight: PANE_MIN_HEIGHT,
dragEnabled: true,
state: PaneState.Normal,
axis: { name: 'normal', scrollZoomEnabled: true }
}

constructor (rootContainer: HTMLElement, afterElement: Nullable<HTMLElement>, chart: Chart, id: string, options: Omit<PaneOptions, 'id' | 'height'>) {
super(rootContainer, afterElement, chart, id)
Expand Down
24 changes: 22 additions & 2 deletions src/pane/Pane.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,17 +16,22 @@ import type Updater from '../common/Updater'
import { UpdateLevel } from '../common/Updater'
import type Bounding from '../common/Bounding'
import { createDefaultBounding } from '../common/Bounding'
import { createDom } from '../common/utils/dom'
import { merge } from '../common/utils/typeChecks'

import type Chart from '../Chart'

import { createDom } from '../common/utils/dom'
export default abstract class Pane implements Updater {
private _rootContainer: HTMLElement
private _container: HTMLElement
private readonly _id: string
private readonly _chart: Chart

private readonly _bounding: Bounding = createDefaultBounding()
private readonly _bounding = createDefaultBounding()

private readonly _originalBounding = createDefaultBounding()

private _visible = true

constructor (rootContainer: HTMLElement, afterElement: Nullable<HTMLElement>, chart: Chart, id: string) {
this._chart = chart
Expand Down Expand Up @@ -55,6 +60,13 @@ export default abstract class Pane implements Updater {
return this._container
}

setVisible (visible: boolean): void {
if (this._visible !== visible) {
this._container.style.display = visible ? 'block' : 'none'
this._visible = visible
}
}

getId (): string {
return this._id
}
Expand All @@ -67,6 +79,14 @@ export default abstract class Pane implements Updater {
return this._bounding
}

setOriginalBounding (bounding: Partial<Bounding>): void {
merge(this._originalBounding, bounding)
}

getOriginalBounding (): Bounding {
return this._originalBounding
}

update (level?: UpdateLevel): void {
if (this._bounding.height !== this._container.clientHeight) {
this._container.style.height = `${this._bounding.height}px`
Expand Down
7 changes: 7 additions & 0 deletions src/pane/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,19 @@ export const enum PanePosition {
Bottom = 'bottom'
}

export const enum PaneState {
Normal = 'normal',
Maximize = 'maximize',
Minimize = 'minimize'
}

export interface PaneOptions {
id?: string
height?: number
minHeight?: number
dragEnabled?: boolean
position?: PanePosition
state?: PaneState,
axis?: Partial<AxisCreate>
}

Expand Down
37 changes: 20 additions & 17 deletions src/view/AxisView.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ import type { AxisTick, Axis } from '../component/Axis'
import View from './View'

export default abstract class AxisView<C extends Axis = Axis> extends View<C> {
override drawImp (ctx: CanvasRenderingContext2D): void {
override drawImp (ctx: CanvasRenderingContext2D, extend: unknown[]): void {
const widget = this.getWidget()
const pane = widget.getPane()
const bounding = widget.getBounding()
Expand All @@ -37,25 +37,28 @@ export default abstract class AxisView<C extends Axis = Axis> extends View<C> {
styles: styles.axisLine
})?.draw(ctx)
}
const ticks = axis.getTicks()
if (styles.tickLine.show) {
const lines = this.createTickLines(ticks, bounding, styles)
lines.forEach(line => {
if (!(extend[0] as boolean)) {
const ticks = axis.getTicks()
if (styles.tickLine.show) {
const lines = this.createTickLines(ticks, bounding, styles)
lines.forEach(line => {
this.createFigure({
name: 'line',
attrs: line,
styles: styles.tickLine
})?.draw(ctx)
})
}
if (styles.tickText.show) {
const texts = this.createTickTexts(ticks, bounding, styles)
this.createFigure({
name: 'line',
attrs: line,
styles: styles.tickLine
name: 'text',
attrs: texts,
styles: styles.tickText
})?.draw(ctx)
})
}
if (styles.tickText.show) {
const texts = this.createTickTexts(ticks, bounding, styles)
this.createFigure({
name: 'text',
attrs: texts,
styles: styles.tickText
})?.draw(ctx)
}
}

}
}

Expand Down
6 changes: 3 additions & 3 deletions src/view/View.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,10 +58,10 @@ export default abstract class View<C extends Axis = Axis> extends Eventful {
return null
}

draw (ctx: CanvasRenderingContext2D): void {
draw (ctx: CanvasRenderingContext2D, ...extend: unknown[]): void {
this.clear()
this.drawImp(ctx)
this.drawImp(ctx, extend)
}

protected abstract drawImp (ctx: CanvasRenderingContext2D): void
protected abstract drawImp (ctx: CanvasRenderingContext2D, ...extend: unknown[]): void
}
15 changes: 10 additions & 5 deletions src/widget/IndicatorWidget.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import type DrawPane from '../pane/DrawPane'

import { WidgetNameConstants } from './types'
import DrawWidget from './DrawWidget'
import { PaneState } from '../pane/types'

import type { YAxis } from '../component/YAxis'

Expand Down Expand Up @@ -48,9 +49,11 @@ export default class IndicatorWidget extends DrawWidget<DrawPane<YAxis>> {
}

protected updateMain (ctx: CanvasRenderingContext2D): void {
this.updateMainContent(ctx)
this._indicatorView.draw(ctx)
this._gridView.draw(ctx)
if (this.getPane().getOptions().state !== PaneState.Minimize) {
this.updateMainContent(ctx)
this._indicatorView.draw(ctx)
this._gridView.draw(ctx)
}
}

protected createTooltipView (): IndicatorTooltipView {
Expand All @@ -61,8 +64,10 @@ export default class IndicatorWidget extends DrawWidget<DrawPane<YAxis>> {
protected updateMainContent (_ctx: CanvasRenderingContext2D): void {}

override updateOverlay (ctx: CanvasRenderingContext2D): void {
this._overlayView.draw(ctx)
this._crosshairLineView.draw(ctx)
if (this.getPane().getOptions().state !== PaneState.Minimize) {
this._overlayView.draw(ctx)
this._crosshairLineView.draw(ctx)
}
this._tooltipView.draw(ctx)
}
}
Loading

0 comments on commit a14489f

Please sign in to comment.