Skip to content

Commit

Permalink
fix(axes): improve package types
Browse files Browse the repository at this point in the history
  • Loading branch information
wyze committed Apr 30, 2021
1 parent 7d01a53 commit f7fcc75
Show file tree
Hide file tree
Showing 6 changed files with 48 additions and 37 deletions.
21 changes: 13 additions & 8 deletions packages/axes/src/canvas.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { degreesToRadians, CompleteTheme } from '@nivo/core'
import { computeCartesianTicks, getFormatter, computeGridLines } from './compute'
import { TicksSpec, AnyScale, AxisLegendPosition, CanvasAxisProp, ValueFormatter } from './types'

export const renderAxisToCanvas = <Value>(
export const renderAxisToCanvas = <Value extends string | number | Date>(
ctx: CanvasRenderingContext2D,
{
axis,
Expand Down Expand Up @@ -34,7 +34,7 @@ export const renderAxisToCanvas = <Value>(
tickSize?: number
tickPadding?: number
tickRotation?: number
format?: ValueFormatter
format?: string | ValueFormatter<Value>
legend?: string
legendPosition?: AxisLegendPosition
legendOffset?: number
Expand Down Expand Up @@ -88,7 +88,7 @@ export const renderAxisToCanvas = <Value>(
ctx.stroke()
}

const value = format !== undefined ? format(String(tick.value)) : (tick.value as string)
const value = typeof format === 'function' ? format(tick.value) : (tick.value as string)

ctx.save()
ctx.translate(tick.x + tick.textX, tick.y + tick.textY)
Expand All @@ -98,7 +98,7 @@ export const renderAxisToCanvas = <Value>(
ctx.fillStyle = theme.axis.ticks.text.fill
}

ctx.fillText(value, 0, 0)
ctx.fillText(String(value), 0, 0)
ctx.restore()
})

Expand Down Expand Up @@ -153,7 +153,10 @@ export const renderAxisToCanvas = <Value>(

const positions = ['top', 'right', 'bottom', 'left'] as const

export const renderAxesToCanvas = <X, Y>(
export const renderAxesToCanvas = <
X extends string | number | Date,
Y extends string | number | Date
>(
ctx: CanvasRenderingContext2D,
{
xScale,
Expand Down Expand Up @@ -182,7 +185,9 @@ export const renderAxesToCanvas = <X, Y>(
const axes = { top, right, bottom, left }

positions.forEach(position => {
const axis = axes[position]
const axis = axes[position] as typeof position extends 'bottom' | 'top'
? CanvasAxisProp<X> | undefined
: CanvasAxisProp<Y> | undefined

if (!axis) return null

Expand All @@ -191,7 +196,7 @@ export const renderAxesToCanvas = <X, Y>(
const scale = isXAxis ? xScale : yScale
const format = getFormatter(axis.format, scale)

renderAxisToCanvas<unknown>(ctx, {
renderAxisToCanvas(ctx, {
...axis,
axis: isXAxis ? 'x' : 'y',
x: position === 'right' ? width : 0,
Expand All @@ -205,7 +210,7 @@ export const renderAxesToCanvas = <X, Y>(
})
}

export const renderGridLinesToCanvas = <Value>(
export const renderGridLinesToCanvas = <Value extends string | number | Date>(
ctx: CanvasRenderingContext2D,
{
width,
Expand Down
4 changes: 3 additions & 1 deletion packages/axes/src/components/Axes.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,9 @@ export const Axes = <X extends number | string | Date, Y extends number | string
return (
<>
{positions.map(position => {
const axis = axes[position]
const axis = axes[position] as typeof position extends 'bottom' | 'top'
? AxisProp<X> | undefined
: AxisProp<Y> | undefined

if (!axis) return null

Expand Down
8 changes: 4 additions & 4 deletions packages/axes/src/components/Axis.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ import { useSpring, useTransition, animated } from 'react-spring'
import { useTheme, useMotionConfig } from '@nivo/core'
import { computeCartesianTicks, getFormatter } from '../compute'
import { AxisTick } from './AxisTick'
import { AxisProps, AxisTickProps } from '../types'
import { AxisProps } from '../types'

export const Axis = <Scale,>({
export const Axis = <Value extends string | number | Date>({
axis,
scale,
x = 0,
Expand All @@ -23,14 +23,14 @@ export const Axis = <Scale,>({
legendOffset = 0,
onClick,
ariaHidden,
}: AxisProps) => {
}: AxisProps<Value>) => {
const theme = useTheme()

const formatValue = useMemo(() => getFormatter(format, scale), [format, scale])

const { ticks, textAlign, textBaseline } = computeCartesianTicks({
axis,
scale: scale as any,
scale,
ticksPosition,
tickValues,
tickSize,
Expand Down
2 changes: 1 addition & 1 deletion packages/axes/src/components/AxisTick.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ export const AxisTick = <Value extends string | number | Date>({
const theme = useTheme()

let value = _value
if (format !== undefined) {
if (typeof format === 'function') {
value = format(value)
}

Expand Down
20 changes: 12 additions & 8 deletions packages/axes/src/compute.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,10 @@ const isInteger = (value: unknown): value is number =>

const isArray = <T>(value: unknown): value is T[] => Array.isArray(value)

export const getScaleTicks = <Value>(scale: AnyScale, spec?: TicksSpec<Value>) => {
export const getScaleTicks = <Value extends string | number | Date>(
scale: AnyScale,
spec?: TicksSpec<Value>
) => {
// specific values
if (Array.isArray(spec)) {
return spec
Expand Down Expand Up @@ -122,7 +125,7 @@ export const getScaleTicks = <Value>(scale: AnyScale, spec?: TicksSpec<Value>) =
return scale.domain()
}

export const computeCartesianTicks = <Value>({
export const computeCartesianTicks = <Value extends string | number | Date>({
axis,
scale,
ticksPosition,
Expand Down Expand Up @@ -209,21 +212,22 @@ export const computeCartesianTicks = <Value>({
}
}

export const getFormatter = (
format: string | ValueFormatter,
export const getFormatter = <Value extends string | number | Date>(
format: string | ValueFormatter<Value> | undefined,
scale: AnyScale
): ValueFormatter | undefined => {
): ValueFormatter<Value> | undefined => {
if (typeof format === 'undefined' || typeof format === 'function') return format

if (scale.type === 'time') {
const formatter = timeFormat(format)
return (d: number | string) => formatter(new Date(d))

return (d => formatter(d instanceof Date ? d : new Date(d))) as ValueFormatter<Value>
}

return d3Format(format) as ValueFormatter
return (d3Format(format) as unknown) as ValueFormatter<Value>
}

export const computeGridLines = <Value>({
export const computeGridLines = <Value extends string | number | Date>({
width,
height,
scale,
Expand Down
30 changes: 15 additions & 15 deletions packages/axes/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,6 @@ import {
} from 'd3-scale'
import React from 'react'

// export type AxisValue = string | number | Date

export type GridValuesBuilder<T> = T extends number
? number[]
: T extends string
Expand Down Expand Up @@ -39,7 +37,7 @@ export type AnyScale =
| (ScaleLogarithmic<any, any> & { type: 'log' })
| ScaleWithBandwidth

export type TicksSpec<Value> =
export type TicksSpec<Value extends string | number | Date> =
// exact number of ticks, please note that
// depending on the current range of values,
// you might not get this exact count
Expand All @@ -53,46 +51,50 @@ export type TicksSpec<Value> =

export type AxisLegendPosition = 'start' | 'middle' | 'end'

export interface AxisProp<Value> {
export type ValueFormatter<Value extends string | number | Date> = (value: Value) => Value

export interface AxisProp<Value extends string | number | Date> {
ticksPosition?: 'before' | 'after'
tickValues?: TicksSpec<Value>
tickSize?: number
tickPadding?: number
tickRotation?: number
format?: any
renderTick?: any
format?: string | ValueFormatter<Value>
renderTick?: (props: AxisTickProps<Value>) => JSX.Element
legend?: React.ReactNode
legendPosition?: AxisLegendPosition
legendOffset?: number
}

export interface CanvasAxisProp<Value> extends Omit<AxisProp<Value>, 'legend'> {
export interface CanvasAxisProp<Value extends string | number | Date>
extends Omit<AxisProp<Value>, 'legend'> {
legend?: string
}

export interface AxisProps {
export interface AxisProps<Value extends number | string | Date = any> {
axis: 'x' | 'y'
scale: AnyScale
x?: number
y?: number
length: number
ticksPosition: 'before' | 'after'
tickValues?: TicksSpec<number | string | Date>
tickValues?: TicksSpec<Value>
tickSize?: number
tickPadding?: number
tickRotation?: number
format?: any
renderTick?: any
format?: string | ValueFormatter<Value>
renderTick?: (props: AxisTickProps<Value>) => JSX.Element
legend?: React.ReactNode
legendPosition?: 'start' | 'middle' | 'end'
legendOffset?: number
onClick?: any
onClick?: (event: React.MouseEvent<SVGGElement, MouseEvent>, value: Value) => void
ariaHidden?: boolean
}

export interface AxisTickProps<Value extends number | string | Date> {
tickIndex: number
value: Value
format?: any
format?: string | ValueFormatter<Value>
x: number
y: number
lineX: number
Expand All @@ -111,8 +113,6 @@ export interface AxisTickProps<Value extends number | string | Date> {
onClick?: (event: React.MouseEvent<SVGGElement, MouseEvent>, value: Value) => void
}

export type ValueFormatter = (value: number | string) => string

export type Line = {
key: string
x1: number
Expand Down

0 comments on commit f7fcc75

Please sign in to comment.