@antv/g2
Version:
the Grammar of Graphics in Javascript
118 lines (107 loc) • 3.33 kB
text/typescript
import { Slider as SliderComponent } from '@antv/component';
import { format } from 'd3-format';
import { DisplayObject } from '@antv/g';
import { isArray } from '@antv/util';
import { isTranspose } from '../utils/coordinate';
import {
GuideComponentComponent as GCC,
GuideComponentContext,
} from '../runtime';
import { invert } from '../utils/scale';
export type SliderOptions = {
orientation: 'horizontal' | 'vertical';
[key: string]: any;
};
function inferPosition(bbox, position, trackSize) {
const { x, y, width, height } = bbox;
if (position === 'left') return [x + width - trackSize, y];
if (position === 'right') return [x, y];
if (position === 'bottom') return [x, y];
if (position === 'top') return [x, y + height - trackSize];
}
/**
* Slider component.
*/
export const Slider: GCC<SliderOptions> = (options) => {
// do not pass size.
const {
orientation,
labelFormatter,
size,
style = {},
position,
...rest
} = options;
return (context) => {
const {
scales: [scale],
value,
theme,
coordinate,
} = context;
const { bbox } = value;
const { width, height } = bbox;
const { slider: sliderTheme = {} } = theme;
const defaultFormatter = scale.getFormatter?.() || ((v) => v + '');
const formatter =
typeof labelFormatter === 'string'
? format(labelFormatter)
: labelFormatter;
const isHorizontal = orientation === 'horizontal';
const reverse = isTranspose(coordinate) && isHorizontal;
const { trackSize = sliderTheme.trackSize } = style;
const [x0, y0] = inferPosition(bbox, position, trackSize);
return new SliderComponent({
className: 'slider',
style: Object.assign({}, sliderTheme, {
x: x0,
y: y0,
trackLength: isHorizontal ? width : height,
orientation,
formatter: (v) => {
const f = formatter || defaultFormatter;
const v1 = reverse ? 1 - v : v;
const tick = invert(scale, v1, true);
return f(tick);
},
sparklineData: inferSparklineData(options, context),
...style,
...rest,
}),
}) as unknown as DisplayObject;
};
};
function markValue(markState, channels: string[]) {
const [value] = Array.from(markState.entries())
.filter(([mark]) => mark.type === 'line' || mark.type === 'area')
.filter(([mark]) => mark.slider)
.map(([mark]) => {
const { encode, slider } = mark;
if (slider?.x) {
const channel = (name) => {
const channel = encode[name];
return [name, channel ? channel.value : undefined];
};
return Object.fromEntries(channels.map(channel));
}
});
if (!value?.series) return value?.y;
const result = value.series.reduce((acc, curr, index) => {
acc[curr] = acc[curr] || [];
acc[curr].push(value.y[index]);
return acc;
}, {});
return Object.values(result);
}
function inferSparklineData(options, context: GuideComponentContext) {
const { markState } = context;
if (isArray(options.sparklineData)) return options.sparklineData;
return markValue(markState, ['y', 'series']);
}
Slider.props = {
defaultPosition: 'bottom',
defaultSize: 24,
defaultOrder: 1,
defaultCrossPadding: [12, 12],
defaultPadding: [12, 12],
};