diff --git a/ember-apache-echarts/src/helpers/coalesce.ts b/ember-apache-echarts/src/helpers/coalesce.ts index e7066d57..e72086b9 100644 --- a/ember-apache-echarts/src/helpers/coalesce.ts +++ b/ember-apache-echarts/src/helpers/coalesce.ts @@ -3,12 +3,7 @@ import { helper } from '@ember/component/helper'; /** * Returns the first non-null defined value from the positional parameters, or * `undefined` if no non-null values were passed in. - * - * @param {array} positional - The positional parameters for this function - * - * @return {any} The first non-null defined element of `positional` or - * `undefined` if all the elements are null or undefined */ -export default helper(function coalesce(positional) { +export default helper(function coalesce(positional: unknown[]): unknown { return positional.find((param) => param != null); }); diff --git a/ember-apache-echarts/src/helpers/css-size.ts b/ember-apache-echarts/src/helpers/css-size.ts index 70e14392..af4d8954 100644 --- a/ember-apache-echarts/src/helpers/css-size.ts +++ b/ember-apache-echarts/src/helpers/css-size.ts @@ -2,22 +2,14 @@ import { helper } from '@ember/component/helper'; const numberRegEx = /^\d+$/; -const isNumeric = (value: unknown) => +const isNumeric = (value: unknown): boolean => typeof value === 'number' || (typeof value === 'string' && value.match(numberRegEx) != null); /** * Formats a value so it can be used as a size in CSS expressions. Automatically * uses `px` as the unit for nummeric values. - * - * @param {array} positional - The positional parameters for this function - * @param {string|number} positional[0] - The value to format as a CSS size - * @param {string|number} positional[1] - The default value if the value is null - * or undefined - * - * @return {string} The value formatted as a CSS size */ - -export default helper(function cssSize(positional: [string?, string?] /*, named*/): string { +export default helper(function cssSize(positional: [string?, string?]): string { const size = positional[0] != null && positional[0] !== '' ? positional[0] : positional[1]; return isNumeric(size) || size === '' diff --git a/ember-apache-echarts/src/modifiers/abstract-chart.ts b/ember-apache-echarts/src/modifiers/abstract-chart.ts index ab6619ad..7cf1f055 100644 --- a/ember-apache-echarts/src/modifiers/abstract-chart.ts +++ b/ember-apache-echarts/src/modifiers/abstract-chart.ts @@ -1,4 +1,3 @@ -// @ts-nocheck - will need to spend real time on this file with types. import { merge } from 'lodash-es'; import { transform } from 'lodash-es'; import { registerDestructor } from '@ember/destroyable'; @@ -14,8 +13,205 @@ import layoutCells from '../utils/layout/layout-cells.ts'; import resolveStyle from '../utils/style/resolve-style.ts'; import mergeAtPaths from '../utils/merge-at-paths.ts'; -// These should be composite properties so they can be overridden either by -// composite properties or individual constituent properties +type Layout = { + chartWidth: number; + chartHeight: number; + width: number; + height: number; + x: number; + y: number; + cell?: { + yOffset: number; + }; +}; + +type Style = { + font: string; + textAlign: string; + margin: number; + padding: number; + backgroundColor?: string; + borderTopWidth?: number; + borderTopColor?: string; + borderRadius?: number; + paddingTop?: number; + paddingRight?: number; + paddingBottom?: number; + paddingLeft?: number; + color?: string; + fontStyle?: string; + fontWeight?: string; + fontFamily?: string; + fontSize?: number; + marginTop?: number; + marginBottom?: number; + marginLeft?: number; + marginRight?: number; + borderLeftWidth?: number; + borderRightWidth?: number; + borderBottomWidth?: number; + borderLeftColor?: string; + borderRightColor?: string; + borderBottomColor?: string; + borderLeftStyle?: string; + borderRightStyle?: string; + borderBottomStyle?: string; + opacity?: number; + zIndex?: number; + verticalAlign?: string; +}; + +type Context = { + layout: Layout; + args: any; + chart: echarts.ECharts; + styles: Record; + data: any; + index?: number; +}; + +type Box = { + x: number; + y: number; + width: number; + height: number; + marginTop: number; + marginBottom: number; + marginLeft: number; + marginRight: number; + borderLeftWidth: number; + borderRightWidth: number; + borderTopWidth: number; + borderBottomWidth: number; + backgroundColor?: string; + borderTopColor?: string; +}; + +type TitleConfig = { + text: string; + top: number; + backgroundColor?: string; + borderWidth?: number; + borderColor?: string; + borderRadius?: number; + padding: number[]; + textStyle: { + color?: string; + fontStyle?: string; + fontWeight?: string; + fontFamily?: string; + fontSize?: number; + }; + left?: number; + textAlign?: string; + right?: number; +}; + +type LegendConfig = { + type: string; + data: { + name: string; + icon: string; + itemStyle: { + color?: string; + }; + }[]; + itemGap: number; + align: string; + width: number; + orient: string; + backgroundColor?: string; + borderWidth?: number; + borderColor?: string; + borderRadius?: number; + padding: number[]; + textStyle: { + color?: string; + fontStyle?: string; + fontWeight?: string; + fontFamily?: string; + fontSize?: number; + }; + top?: number | string; + bottom?: number | string; + left?: number | string; + right?: number | string; +}; + +type DataZoomConfig = { + type: string; + brushSelect?: boolean; + borderColor?: string; + show: boolean; + start: number; + end: number; + top?: number; + bottom?: number; + left?: number; + right?: number; + xAxisIndex?: number[]; + yAxisIndex?: number[]; +}; + +type TextConfig = { + type: string; + style: { + font: string; + fill: string; + text: string; + }; + silent: boolean; + z?: number; + left?: number; + right?: number; + top?: number; + bottom?: number; +}; + +type PlotConfig = { + grid: any[]; + yAxis: any[]; + xAxis: any[]; + series: any[]; + 'graphic.elements'?: any[]; +}; + +type Cell = { + width: number; + height: number; + innerX: number; + innerY: number; +}; + +type Series = { + label?: string; + name?: string; + data: any[]; +}; + +type ChartArgs = { + title?: string; + legend?: string; + legendOrientation?: string; + colorMap?: Record; + xAxisZoom?: string; + yAxisZoom?: string; + xAxisZoomBrush?: boolean; + yAxisZoomBrush?: boolean; + series?: Series[]; + data?: any[]; + [key: string]: any; +}; + +type ContextData = { + series: Series[]; +}; + +type LegendMetrics = { + width: number; + height: number; +}; + const baseStyle = { border: '0px solid #000', font: 'bold 16px Montserrat,sans-serif', @@ -24,12 +220,11 @@ const baseStyle = { padding: 0, }; -// The index for the overlay layer const Z_OVERLAY = 100; export default class AbstractChartModifier extends Modifier { - chart; - resizeObserver; + chart: echarts.ECharts | undefined; + resizeObserver: ResizeObserver | undefined; get defaultStyles() { return { @@ -71,8 +266,8 @@ export default class AbstractChartModifier extends Modifier { }, }; } - - constructor(...args) { + // TODO: This may be the wrong way to register the destructor + constructor(...args: any[]) { super(...args); registerDestructor(this, () => this.cleanup()); @@ -82,7 +277,7 @@ export default class AbstractChartModifier extends Modifier { } } - modify(element, [args], defaultArgs, count = 0) { + modify(element: HTMLElement, [args]: [ChartArgs], defaultArgs: ChartArgs, count = 0) { if (!element.clientHeight || !element.clientWidth) { // Escape hatch if the styling of this element doesn't allow it to have // a size within its parent layout @@ -105,22 +300,22 @@ export default class AbstractChartModifier extends Modifier { this.configureChart(chartArgs, this.chart, element); } - configureChart(_args, _chart, _element) { + configureChart(_args: ChartArgs, _chart: echarts.ECharts, _element: HTMLElement) { throw new Error('`configureChart` needs to be overridden. No implementation exists.'); } - createChart(element, chartArgs) { + createChart(element: HTMLElement, chartArgs: ChartArgs): echarts.ECharts { const chart = echarts.init(element, null, { renderer: 'canvas' }); // Initialize the chart model using default options so charts that need to // access the locale via the model while being built can do so chart.setOption({}); - // Add a `handle` method that ensures only one event listener can be - // attached at the same time. This prevents mistakes when coding new charts - // of forgetting to `off` an event during a reconfigure and then having - // multiple handlers attached to the chart. - chart.handle = (eventName, ...args) => { + chart.handle = (eventName: string, ...args: any[]) => { + // Add a `handle` method that ensures only one event listener can be + // attached at the same time. This prevents mistakes when coding new charts + // of forgetting to `off` an event during a reconfigure and then having + // multiple handlers attached to the chart. chart.off(eventName); chart.on(eventName, ...args); }; @@ -140,7 +335,7 @@ export default class AbstractChartModifier extends Modifier { return chart; } - /** + /** * Builds a basic layout for this chart, returning the `context` and `config` * that can be used to extend the layout further. * @@ -150,8 +345,8 @@ export default class AbstractChartModifier extends Modifier { * calculate their size and position based on how other components are laid * out. */ - buildLayout(args, chart) { - const config = {}; + buildLayout(args: ChartArgs, chart: echarts.ECharts) { + const config: any = {}; const context = this.createContext(args, chart); // These must be called in the order from outsidemost layout to innermost @@ -171,8 +366,8 @@ export default class AbstractChartModifier extends Modifier { } cleanup() { - this.resizeObserver.disconnect(); - this.chart.dispose(); + this.resizeObserver?.disconnect(); + this.chart?.dispose(); } // --------------------------------------------------------------------------- @@ -181,8 +376,8 @@ export default class AbstractChartModifier extends Modifier { /** * Creates the context used when laying out elements on this chart. */ - createContext(args, chart) { - const layout = { + createContext(args: ChartArgs, chart: echarts.ECharts): Context { + const layout: Layout = { chartWidth: chart.getWidth(), chartHeight: chart.getHeight(), width: chart.getWidth(), @@ -192,7 +387,7 @@ export default class AbstractChartModifier extends Modifier { }; const styles = transform( Object.keys(this.defaultStyles), - (styles, type) => + (styles: Record, type: string) => (styles[type] = { ...baseStyle, ...this.defaultStyles[type], @@ -213,7 +408,7 @@ export default class AbstractChartModifier extends Modifier { /** * Generates the `data` section of the context used to construct this chart. */ - createContextData(args /*, chart */) { + createContextData(args: ChartArgs /*, chart */): ContextData { const series = args.series ?? [{ data: args.data }]; return { @@ -224,7 +419,7 @@ export default class AbstractChartModifier extends Modifier { /** * Add the border and background of the chart. */ - addChartBox(context, config) { + addChartBox(context: Context, config: any): Layout { const style = resolveStyle(context.styles.chart, context.layout); mergeAtPaths( @@ -245,7 +440,7 @@ export default class AbstractChartModifier extends Modifier { * Adds the title to `config` if defined in `args` and returns the new * context. */ - addTitle(context, config) { + addTitle(context: Context, config: any): Layout { const { title } = context.args; if (!title) { @@ -269,7 +464,7 @@ export default class AbstractChartModifier extends Modifier { * Adds the legend to `config` if defined in `args` and returns the new * context. */ - addLegend(context, config) { + addLegend(context: Context, config: any): Layout { const { legend } = context.args; if (!legend || legend === 'none') { @@ -306,7 +501,7 @@ export default class AbstractChartModifier extends Modifier { * Adds the data zoom slider to `config` if defined in `args` and returns the * new context. */ - addDataZoom(context, config) { + addDataZoom(context: Context, config: any): Layout { const { args, layout, styles } = context; const { xAxisZoom, yAxisZoom } = args; @@ -352,10 +547,10 @@ export default class AbstractChartModifier extends Modifier { /** * Add the border and background of the cells. */ - addCellBoxes(context, config) { + addCellBoxes(context: Context, config: any): Layout { mergeAtPaths( config, - layoutCells(context, context.data.series, (info, cell) => this.generateBoxConfig(cell)) + layoutCells(context, context.data.series, (info: any, cell: Cell) => this.generateBoxConfig(cell)) ); return context.layout; @@ -364,7 +559,7 @@ export default class AbstractChartModifier extends Modifier { /** * Add the titles to individual cells. */ - addCellTitles(context, config) { + addCellTitles(context: Context, config: any): Layout { const series = context.data.series; if (series.length === 1 && !series[0].label && !series[0].name) { @@ -375,7 +570,7 @@ export default class AbstractChartModifier extends Modifier { mergeAtPaths( config, - layoutCells(context, context.data.series, (info, cell) => + layoutCells(context, context.data.series, (info: any, cell: Cell) => this.generateTitleConfig( info.label ?? info.name, { @@ -404,14 +599,14 @@ export default class AbstractChartModifier extends Modifier { /** * Add the plots to individual cells. */ - addCellPlots(context, config) { + addCellPlots(context: Context, config: any): Layout { // Ensure when using the `grid` config, the correct index is specified. This // differs from `context.index` when a cell has no data (and so, no grid) let gridIndex = 0; mergeAtPaths( config, - layoutCells(context, context.data.series, (info, cell) => { + layoutCells(context, context.data.series, (info: any, cell: Cell) => { const config = this.generatePlotConfig(info, cell, context, gridIndex); if (config) { @@ -428,7 +623,7 @@ export default class AbstractChartModifier extends Modifier { /** * Add any cell overlays on top of the chart. */ - addCellTextOverlays(context, config) { + addCellTextOverlays(context: Context, config: any): Layout { if (!this.generateTextOverlayConfig) { return context.layout; } @@ -437,7 +632,7 @@ export default class AbstractChartModifier extends Modifier { mergeAtPaths( config, - layoutCells(context, context.data.series, (info, cell) => + layoutCells(context, context.data.series, (info: any, cell: Cell) => this.generateTextOverlayConfig(info, context.args, cell, style) ) ); @@ -448,14 +643,14 @@ export default class AbstractChartModifier extends Modifier { /** * Returns the labels for the legend. */ - getLegendLabels(series, args) { + getLegendLabels(series: Series[], args: ChartArgs): string[] { return getUniqueDatasetValues(series, args.categoryProperty ?? 'name'); } /** * Returns the orientation of the legend as either `horizontal` or `vertical`. */ - getLegendOrientation(args) { + getLegendOrientation(args: ChartArgs): string { const { legend, legendOrientation } = args; return !['horizontal', 'vertical'].includes(legendOrientation) @@ -468,7 +663,7 @@ export default class AbstractChartModifier extends Modifier { /** * Generates the configuration for the background and border of a box element. */ - generateBoxConfig(box) { + generateBoxConfig(box: Box): any { return { 'graphic.elements': [ { @@ -509,8 +704,8 @@ export default class AbstractChartModifier extends Modifier { /** * Generates the configuration for a title element. */ - generateTitleConfig(text, layout, style) { - const config = { + generateTitleConfig(text: string, layout: Layout, style: Style): any { + const config: TitleConfig = { text, top: layout.y + style.marginTop, backgroundColor: style.backgroundColor, @@ -557,36 +752,33 @@ export default class AbstractChartModifier extends Modifier { /** * Generates the configuration for a legend element. */ - generateLegendConfig(series, args, layout, style) { + generateLegendConfig(series: Series[], args: ChartArgs, layout: Layout, style: Style): any { const { legend = 'topCenter' } = args; const isVertical = this.getLegendOrientation(args) === 'vertical'; - const config = { - legend: { - type: 'scroll', - data: this.getLegendLabels(series, args).map((label) => ({ - name: label, - icon: 'circle', - itemStyle: { - color: args?.colorMap?.[label], - }, - })), - itemGap: isVertical ? 15 : 40, - align: style.textAlign ?? 'left', - width: layout.width, - orient: isVertical ? 'vertical' : 'horizontal', - backgroundColor: style.backgroundColor, - // Safari only parses contituent values, so use "top" as a proxy for all - borderWidth: style.borderTopWidth, - borderColor: style.borderTopColor, - borderRadius: style.borderRadius, - padding: [style.paddingTop, style.paddingRight, style.paddingBottom, style.paddingLeft], - textStyle: { - color: style.color, - fontStyle: style.fontStyle, - fontWeight: style.fontWeight, - fontFamily: style.fontFamily, - fontSize: style.fontSize, + const config: LegendConfig = { + type: 'scroll', + data: this.getLegendLabels(series, args).map((label) => ({ + name: label, + icon: 'circle', + itemStyle: { + color: args?.colorMap?.[label], }, + })), + itemGap: isVertical ? 15 : 40, + align: style.textAlign ?? 'left', + width: layout.width, + orient: isVertical ? 'vertical' : 'horizontal', + backgroundColor: style.backgroundColor, + borderWidth: style.borderTopWidth, + borderColor: style.borderTopColor, + borderRadius: style.borderRadius, + padding: [style.paddingTop, style.paddingRight, style.paddingBottom, style.paddingLeft], + textStyle: { + color: style.color, + fontStyle: style.fontStyle, + fontWeight: style.fontWeight, + fontFamily: style.fontFamily, + fontSize: style.fontSize, }, }; @@ -654,7 +846,7 @@ export default class AbstractChartModifier extends Modifier { * Generates the configuration for the control that allows a user to zoom in * and out of the data. */ - generateXAxisDataZoomConfig(args, layout, style) { + generateXAxisDataZoomConfig(args: ChartArgs, layout: Layout, style: Style): any { const { xAxisZoom, xAxisZoomBrush } = args; if (!xAxisZoom) { @@ -701,7 +893,7 @@ export default class AbstractChartModifier extends Modifier { * Generates the configuration for the control that allows a user to zoom in * and out of the data. */ - generateYAxisDataZoomConfig(args, layout, style) { + generateYAxisDataZoomConfig(args: ChartArgs, layout: Layout, style: Style): any { const { yAxisZoom, yAxisZoomBrush } = args; if (!yAxisZoom) { @@ -744,7 +936,7 @@ export default class AbstractChartModifier extends Modifier { * Generates the base configuration for a single element in the `dataZoom` * configuration. */ - generateDataZoomConfigElement(style, brushSelect) { + generateDataZoomConfigElement(style: Style, brushSelect?: boolean): DataZoomConfig { return { type: 'slider', brushSelect, @@ -758,9 +950,9 @@ export default class AbstractChartModifier extends Modifier { /** * Generates the configuration for a text element. */ - generateTextConfig(text, layout, style) { + generateTextConfig(text: string, layout: Layout, style: Style): any { const metrics = computeTextMetrics(text, style); - const config = { + const config: TextConfig = { type: 'text', style: { font: `${style.fontStyle} ${style.fontWeight} ${style.fontSize}px ${style.fontFamily}`, @@ -835,7 +1027,7 @@ export default class AbstractChartModifier extends Modifier { * Computes the width and height of the legend, after the legend has been * added into the `config` using the compiled legend `style`. */ - computeLegendMetrics(context, config, style) { + computeLegendMetrics(context: Context, config: any, style: Style): LegendMetrics { const { layout, data, args } = context; const labels = this.getLegendLabels(data.series, args); const orientation = this.getLegendOrientation(args); @@ -844,7 +1036,7 @@ export default class AbstractChartModifier extends Modifier { const itemGap = config.legend.itemGap ?? 10; // Divide by 2 on border, since it appears to be drawn at the end of the // legend rather than inside or outside the legend - const metrics = { + const metrics: LegendMetrics = { width: style.paddingLeft + style.paddingRight + diff --git a/ember-apache-echarts/src/modifiers/bar-chart.ts b/ember-apache-echarts/src/modifiers/bar-chart.ts index 76b20310..a413f838 100644 --- a/ember-apache-echarts/src/modifiers/bar-chart.ts +++ b/ember-apache-echarts/src/modifiers/bar-chart.ts @@ -1,5 +1,4 @@ import { tracked } from '@glimmer/tracking'; -// @ts-expect-error import { compact, countBy, flatten } from 'lodash-es'; import * as echarts from 'echarts'; import mergeAtPaths from '../utils/merge-at-paths.ts'; @@ -37,8 +36,11 @@ const DEFAULT_VALUE_PROPERTY = 'value'; // TODO: Import only the required components to keep the bundle size small. See // https://echarts.apache.org/handbook/en/basics/import/ [twl 6.Apr.22] -// @ts-expect-error -const setItemColor = (colorMap, item, color) => +const setItemColor = ( + colorMap: Record | undefined, + item: Record, + color: string +) => !colorMap?.[color] ? item : { @@ -51,17 +53,19 @@ const setItemColor = (colorMap, item, color) => const isShowingAxisLabel = (axisConfig: AxisConfig, labelType: 'Min' | 'Max') => axisConfig.axisLabel?.[`show${labelType}Label`] === false || (axisConfig.type === 'time' && axisConfig.axisLabel?.[`show${labelType}Label`] !== true); -// @ts-expect-error -const computeData = (data, categories, args) => { + +const computeData = ( + data: Record[], + categories: string[], + args: ChartArgs +) => { const { categoryProperty = DEFAULT_CATEGORY_PROPERTY } = args; const { categoryAxisType, orientation } = args; - // @ts-expect-error - not sure what the 4th arg should be - const series = getSeriesData(data, categories, categoryProperty); + const series = getSeriesData(data, categories, categoryProperty, DEFAULT_VALUE_PROPERTY); return categoryAxisType !== 'time' ? series - : // @ts-expect-error - series.map((item) => ({ + : series.map((item) => ({ ...item, value: orientation === 'horizontal' ? [item.value, item.name] : [item.name, item.value], })); @@ -70,8 +74,8 @@ const computeData = (data, categories, args) => { type ChartArgs = { tooltipFormatter?: (params: unknown) => string; onSelect?: (name: string | null) => void; - data: unknown[]; - series?: unknown[]; + data: Record[]; + series?: { data: Record[]; label?: string; name?: string }[]; rotateData?: boolean; categoryProperty?: string; valueProperty?: string; @@ -240,24 +244,6 @@ type ChartArgs = { * it's active. Defaults to `false` * * - * ## Legend - * - * `legend` - * : Whether and where to display a legend: `none`, `top`, `bottom`, `left`, - * `right`, `topLeft`, `topRight`, `bottomLeft`, `bottomRight`, `leftTop`, - * `leftBottom`, `rightTop`, `rightBottom` - * - * `legendOrientation` - * : Which orientation to render the legend: `horizontal`, `vertical` or `auto` - * (default), where `auto` renders the legend horizontally when positioned - * on the top or bottom of the chart, and vertically when positioned on the - * left or right of the chart - * - * `legendStyle` - * : CSS properties for the chart legend including color, font, background - * color, border and alignment. - * - * * ## Data Zoom * * `xAxisZoom` @@ -304,7 +290,7 @@ type ChartArgs = { * : Called when an element on a chart is selected */ export default class BarChartModifier extends AbstractChartModifier { - @tracked drillPath = []; + @tracked drillPath: number[] = []; get defaultStyles() { const styles = super.defaultStyles; @@ -378,8 +364,7 @@ export default class BarChartModifier extends AbstractChartModifier { /** * Returns the categories used within the data series in render order. */ - // @ts-expect-error - getCategories(args, series) { + getCategories(args: ChartArgs, series: { data: Record[] }[]) { const { categoryAxisSort = 'firstSeries', categoryAxisType } = args; const { categoryProperty = DEFAULT_CATEGORY_PROPERTY } = args; const categories = getUniqueDatasetValues(series, categoryProperty); @@ -405,8 +390,11 @@ export default class BarChartModifier extends AbstractChartModifier { * Formats the `name` and `value` within `params` when a category or value * formatter are defined, respectively. */ - // @ts-expect-error - formatTooltipParams(args, params, elementType) { + formatTooltipParams( + args: ChartArgs, + params: Record, + elementType: 'axisTooltip' | 'itemTooltip' + ) { const { valueAxisFormatter = echarts.format.addCommas } = args; const { categoryAxisType, categoryAxisFormatter, orientation } = args; const { missingCategoryFormat, missingValueFormat } = args; @@ -426,14 +414,14 @@ export default class BarChartModifier extends AbstractChartModifier { !params.value && missingValueFormat != null ? missingValueFormat : valueAxisFormatter - ? valueAxisFormatter(params.value, elementType) - : params.value, + ? valueAxisFormatter(params.value, elementType) + : params.value, category: !params.name && missingCategoryFormat != null ? missingCategoryFormat : categoryAxisFormatter - ? categoryAxisFormatter(params.name, elementType) - : params.name, + ? categoryAxisFormatter(params.name, elementType) + : params.name, }; } @@ -453,7 +441,6 @@ export default class BarChartModifier extends AbstractChartModifier { params.length != null ? params.map((param) => this.formatTooltipParams(args, param, 'axisTooltip')) : this.formatTooltipParams(args, params, 'itemTooltip'), - // @ts-expect-error context.data.dataset ), }), @@ -479,13 +466,10 @@ export default class BarChartModifier extends AbstractChartModifier { // on how the axis is being rendered. [twl 20.Jul.22] const name = categoryAxisScale === 'shared' - ? // @ts-expect-error: until the abstract is typed this needs to wait - context.data.categories[dataIndex] - : // @ts-expect-error: TypeScript doesn't know about the structure of `series.data` - series.data[dataIndex] - ? // @ts-expect-error: TypeScript doesn't know about the structure of `series.data` - series.data[dataIndex][args.categoryProperty ?? DEFAULT_CATEGORY_PROPERTY] - : null; + ? context.data.categories[dataIndex] + : series.data[dataIndex] + ? series.data[dataIndex][args.categoryProperty ?? DEFAULT_CATEGORY_PROPERTY] + : null; if (name) { chart.dispatchAction({ @@ -514,10 +498,8 @@ export default class BarChartModifier extends AbstractChartModifier { // Handle the drill in action chart.on('dblclick', ({ seriesIndex }) => { - // @ts-expect-error if (context.data.dataset[seriesIndex].series) { - // @ts-expect-error - this.drillPath.pushObject(seriesIndex); + this.drillPath.push(seriesIndex); } }); } @@ -525,8 +507,7 @@ export default class BarChartModifier extends AbstractChartModifier { /** * Generates the `data` section of the context used to construct this chart. */ - // @ts-expect-error - createContextData(args) { + createContextData(args: ChartArgs) { const context = super.createContextData(args); const { rotateData, categoryAxisScale, valueAxisScale } = args; const { categoryProperty = DEFAULT_CATEGORY_PROPERTY } = args; @@ -548,8 +529,8 @@ export default class BarChartModifier extends AbstractChartModifier { categories: this.getCategories(args, context.series), }), ...(valueAxisScale === 'shared' && { - minValue: computeStatistic(context.series, 'min'), - maxValue: computeStatistic(context.series, 'max'), + minValue: computeStatistic(context.series, 'min', valueProperty), + maxValue: computeStatistic(context.series, 'max', valueProperty), }), // If grouped or stacked, render multple series on a single chart rather // than one chart per series @@ -566,8 +547,10 @@ export default class BarChartModifier extends AbstractChartModifier { * Adds the title to `config` as defined in the data or by `args` and returns * the new context layout. */ - // @ts-expect-error - addTitle(context, config) { + addTitle( + context: Context & { data: { title?: string } }, + config: { title?: { text: string; left: number; top: number }[] } + ) { const buttonLayout = this.addDrillUpButton(context, config); const buttonWidth = context.layout.width - buttonLayout.width; const buttonHeight = context.layout.height - buttonLayout.height; @@ -605,8 +588,10 @@ export default class BarChartModifier extends AbstractChartModifier { /** * Adds the drill up button to `config` and returns the new context layout. */ - // @ts-expect-error - addDrillUpButton(context, config) { + addDrillUpButton( + context: Context & { args: { drillUpButtonText?: string } }, + config: { 'graphic.elements'?: unknown[] } + ) { if (!this.drillPath.length) { return context.layout; } @@ -625,7 +610,6 @@ export default class BarChartModifier extends AbstractChartModifier { const buttonConfig = this.generateDrillUpButtonConfig(drillUpButtonText, layout, style); mergeAtPaths(config, [buttonConfig]); - // @ts-expect-error const buttonBox = buttonConfig['graphic.elements'][0].children[0].shape; return { @@ -640,8 +624,11 @@ export default class BarChartModifier extends AbstractChartModifier { /** * Generates the configuration for the drill up button. */ - // @ts-expect-error - generateDrillUpButtonConfig(text: string, layout, style) { + generateDrillUpButtonConfig( + text: string, + layout: Layout, + style: Style + ): { 'graphic.elements': unknown[] } { const textMetrics = computeTextMetrics(text, style); return { @@ -685,8 +672,7 @@ export default class BarChartModifier extends AbstractChartModifier { }, }, ], - // @ts-expect-error - onclick: () => this.drillPath.popObject(), + onclick: () => this.drillPath.pop(), }, ], }; @@ -695,22 +681,22 @@ export default class BarChartModifier extends AbstractChartModifier { /** * Returns the labels for the legend. */ - // @ts-expect-error - getLegendLabels(series, args) { + getLegendLabels(series: { data: { label?: string; name?: string }[] }[], args: ChartArgs) { if (!this.isStackedVariant(args.variant) && !this.isGroupedVariant(args.variant)) { return super.getLegendLabels(series, args); } // Grouped and stacked datasets may have a dummy root node - // @ts-expect-error return series[0].data.map((info) => info.label ?? info.name); } /** * Calculate the categories and range used for the category axis. */ - // @ts-expect-error - computeCategoryInfo(series, context) { + computeCategoryInfo( + series: { data: Record[] }, + context: Context + ): { categories: string[]; first: string; last: string; count: number } { const { args, data } = context; const { variant, categoryAxisScale } = args; const seriesData = @@ -729,22 +715,24 @@ export default class BarChartModifier extends AbstractChartModifier { /** * Calculate the values and stats used for the value axis. */ - // @ts-expect-error - computeValueInfo(series, context, categories) { + computeValueInfo( + series: { data: Record[] }, + context: Context, + categories: string[] + ): { values: number[]; minimum: number; maximum: number } { const { args, data } = context; const { variant, valueAxisScale } = args; const { categoryProperty = DEFAULT_CATEGORY_PROPERTY } = args; const { valueProperty = DEFAULT_VALUE_PROPERTY } = args; const isSharedScale = valueAxisScale === 'shared'; - let values; + let values: number[]; if (this.isStackedVariant(variant)) { values = getSeriesTotals(series.data, categories, categoryProperty, valueProperty); } else if (this.isGroupedVariant(variant)) { values = compact( flatten( - // @ts-expect-error series.data.map((group) => getSeriesData(group.data, categories, categoryProperty, valueProperty) ) @@ -775,12 +763,14 @@ export default class BarChartModifier extends AbstractChartModifier { * `position` * : A value from 0 to 1 indicating the position of the tick along the axis. */ - // @ts-expect-error - computeCategoryAxisTicks(context, categoryInfo, axisConfig) { + computeCategoryAxisTicks( + context: Context, + categoryInfo: { categories: string[]; first: string; last: string }, + axisConfig: AxisConfig + ) { const { categoryAxisFormatter } = context.args; const isTimeAxis = axisConfig.type === 'time'; const model = new echarts.Model({ - // defaults from `coord/axisDefault.ts` relevant to the scale ...(isTimeAxis && { splitNumber: 6, }), @@ -790,7 +780,6 @@ export default class BarChartModifier extends AbstractChartModifier { model.ecModel = this.chart.getModel(); if (!isTimeAxis) { - // @ts-expect-error model.getCategories = () => categoryInfo.categories; } @@ -798,17 +787,14 @@ export default class BarChartModifier extends AbstractChartModifier { [categoryInfo.first.valueOf(), categoryInfo.last.valueOf()], model ); - // @ts-expect-error const ticks = scale.getTicks(false).map((tick, index) => ({ - // prettier not formatting nested ternaries properly, so turn it off - // prettier-ignore - ...parseAxisLabel(isTimeAxis - // @ts-expect-error + ...parseAxisLabel( + isTimeAxis ? scale.getFormattedLabel(tick, index, categoryAxisFormatter) : categoryAxisFormatter - ? categoryAxisFormatter(scale.getLabel(tick)) - : scale.getLabel(tick) - ), + ? categoryAxisFormatter(scale.getLabel(tick)) + : scale.getLabel(tick) + ), position: scale.normalize(tick.value), })); @@ -826,28 +812,26 @@ export default class BarChartModifier extends AbstractChartModifier { /** * Calculate the labels used for the value axis. */ - // @ts-expect-error - computeValueAxisTicks(context, valueInfo, axisConfig) { + computeValueAxisTicks( + context: Context, + valueInfo: { minimum: number; maximum: number }, + axisConfig: AxisConfig + ) { const { args } = context; const formatter = args.valueAxisFormatter ?? echarts.format.addCommas; - // prettier not formatting nested ternaries properly, so turn it off - // prettier-ignore const minValue = axisConfig.min == null ? Math.min(0, valueInfo.minimum) : axisConfig.min === 'dataMin' - ? valueInfo.minimum - : axisConfig.min; - // prettier not formatting nested ternaries properly, so turn it off - // prettier-ignore + ? valueInfo.minimum + : axisConfig.min; const maxValue = axisConfig.max == null ? valueInfo.maximum : axisConfig.max === 'dataMax' - ? valueInfo.maximum - : axisConfig.max; + ? valueInfo.maximum + : axisConfig.max; const scale = echarts.helper.createScale([minValue, maxValue], axisConfig); - // @ts-expect-error return scale.getTicks(false).map((tick) => ({ label: tick.value != null ? formatter(tick.value) : '', position: scale.normalize(tick.value), @@ -857,8 +841,12 @@ export default class BarChartModifier extends AbstractChartModifier { /** * Generates the plot config for a single plot on this chart. */ - // @ts-expect-error - generatePlotConfig(series, layout, context, gridIndex) { + generatePlotConfig( + series: { data: Record[] }, + layout: Layout, + context: Context, + gridIndex: number + ) { const { args, styles, data } = context; const { noDataText, seriesConfig } = args; @@ -892,19 +880,17 @@ export default class BarChartModifier extends AbstractChartModifier { gridIndex, type: 'value', max: - // prettier not formatting nested ternaries properly, so turn it off - // prettier-ignore !isGroupedOrStacked && valueAxisScale === 'shared' ? valueAxisMax && valueAxisMax !== 'dataMax' ? valueAxisMax : data.maxValue : valueAxisMax !== 'dataMaxRoundedUp' - ? valueAxisMax - : undefined, + ? valueAxisMax + : undefined, axisLabel: { ...(valueAxisFormatter && { - // @ts-expect-error - formatter: (value, axisIndex) => valueAxisFormatter(value, 'axis', axisIndex), + formatter: (value: number, axisIndex: number) => + valueAxisFormatter(value, 'axis', axisIndex), }), // margin between the axis label and the axis line margin: isHorizontal ? valueAxisStyle.marginTop : valueAxisStyle.marginRight, @@ -924,8 +910,8 @@ export default class BarChartModifier extends AbstractChartModifier { }), axisLabel: { ...(categoryAxisFormatter && { - // @ts-expect-error - formatter: (value, axisIndex) => categoryAxisFormatter(value, 'axis', axisIndex), + formatter: (value: string, axisIndex: number) => + categoryAxisFormatter(value, 'axis', axisIndex), }), // Determine how many categories are shown on the axis interval: @@ -941,8 +927,7 @@ export default class BarChartModifier extends AbstractChartModifier { }; const categoryTicks = this.computeCategoryAxisTicks(context, categoryInfo, categoryAxisConfig); - // Configure the Y axis - const yAxisConfig = {}; + const yAxisConfig: Record = {}; const yAxisInfo = this.computeYAxisInfo( yAxisStyle, isHorizontal ? categoryTicks : valueTicks, @@ -951,8 +936,7 @@ export default class BarChartModifier extends AbstractChartModifier { layout = this.addAxisPointer(context, layout, yAxisConfig, yAxisInfo, 'y'); - // Configure the X axis - const xAxisConfig = {}; + const xAxisConfig: Record = {}; const xAxisInfo = this.computeXAxisInfo( args, layout, @@ -1055,11 +1039,9 @@ export default class BarChartModifier extends AbstractChartModifier { }), }, ] - : // @ts-expect-error - series.data.map((info) => ({ + : series.data.map((info) => ({ ...seriesBaseConfig, name: info.label, - // @ts-expect-error data: computeData(info.data, categoryInfo.categories, args).map((item) => ({ ...item, ...setItemColor(colorMap, item, info.label), @@ -1106,7 +1088,6 @@ export default class BarChartModifier extends AbstractChartModifier { lineWidth: plotStyle.borderTopWidth, }, silent: true, - // render above the axis lines of the chart z: 10, }, ]), @@ -1118,8 +1099,7 @@ export default class BarChartModifier extends AbstractChartModifier { /** * Generates the configuration for an axis label. */ - // @ts-expect-error - generateAxisLabelConfig(layout, style) { + generateAxisLabelConfig(layout: Layout, style: Style) { return { color: style.color, fontStyle: style.fontStyle, @@ -1129,7 +1109,6 @@ export default class BarChartModifier extends AbstractChartModifier { align: style.textAlign, verticalAlign: style.verticalAlign, backgroundColor: style.backgroundColor, - // Safari only parses contituent values, so use "top" as a proxy for all borderWidth: style.borderTopWidth, borderColor: style.borderTopColor, borderType: style.borderTopType, @@ -1142,8 +1121,13 @@ export default class BarChartModifier extends AbstractChartModifier { * Adds the configuration for the axis pointer for the `axis` to `config` and * returns an updated `layout`. */ - // @ts-expect-error - addAxisPointer(context, layout, config, axisInfo, axis) { + addAxisPointer( + context: Context, + layout: Layout, + config: Record, + axisInfo: { height: number; width: number }, + axis: 'x' | 'y' + ) { const { args, styles } = context; const name = `${axis}AxisPointer`; const type = args[name]; @@ -1194,8 +1178,7 @@ export default class BarChartModifier extends AbstractChartModifier { } : { ...(formatter && { - // @ts-expect-error - formatter: (params) => formatter(params.value), + formatter: (params: { value: number }) => formatter(params.value), }), color: labelStyle.color, fontStyle: labelStyle.fontStyle, @@ -1203,7 +1186,6 @@ export default class BarChartModifier extends AbstractChartModifier { fontFamily: labelStyle.fontFamily, fontSize: labelStyle.fontSize, backgroundColor: labelStyle.backgroundColor, - // Safari only parses contituent values, so use "top" as a proxy for all borderWidth: labelStyle.borderTopWidth, borderColor: labelStyle.borderTopColor, borderType: labelStyle.borderTopType, @@ -1262,8 +1244,12 @@ export default class BarChartModifier extends AbstractChartModifier { /** * Generates text to overlay on each cell of the chart, if any. */ - // @ts-expect-error - generateTextOverlayConfig(series, args, layout, style) { + generateTextOverlayConfig( + series: { data: Record[] }, + args: ChartArgs, + layout: Layout, + style: Style + ) { const { noDataText } = args; return (!series.data || series.data.length == 0) && noDataText @@ -1283,8 +1269,11 @@ export default class BarChartModifier extends AbstractChartModifier { /** * Computes style and metrics about the Y axis for charts that use an Y axis. */ - // @ts-expect-error - computeYAxisInfo(style, ticks, isHorizontal) { + computeYAxisInfo( + style: Style, + ticks: { label: string; position: number }[], + isHorizontal: boolean + ) { // HACK TODO: When ticks are too close to each other, the following tick // will be hidden. This can cause the Y axis to calculate the // width wrong if the tick that is hidden has a wider width than @@ -1308,9 +1297,7 @@ export default class BarChartModifier extends AbstractChartModifier { return ticks; }, []); - // @ts-expect-error const labelMetrics = computeMaxTextMetrics( - // @ts-expect-error renderedTicks.map((tick) => tick.label), style ); @@ -1341,8 +1328,14 @@ export default class BarChartModifier extends AbstractChartModifier { /** * Computes style and metrics about the X axis for charts that use an X axis. */ - // @ts-expect-error - computeXAxisInfo(args, layout, style, ticks, yAxisInfo, isHorizontal) { + computeXAxisInfo( + args: ChartArgs, + layout: Layout, + style: Style, + ticks: { label: string; position: number }[], + yAxisInfo: { width: number }, + isHorizontal: boolean + ) { const { categoryAxisMaxLabelCount, categoryAxisType } = args; const maxLabelCount = Math.min(categoryAxisMaxLabelCount ?? ticks.length, ticks.length); const width = @@ -1358,7 +1351,6 @@ export default class BarChartModifier extends AbstractChartModifier { // two different in the final value. Skipping this for now, since it // requires a bunch of refactoring to make this work. [twl 17.Mar.23] const labelMetrics = computeMaxTextMetrics( - // @ts-expect-error ticks.map((tick) => tick.label), style, maxLabelWidth diff --git a/ember-apache-echarts/src/modifiers/graph-chart.ts b/ember-apache-echarts/src/modifiers/graph-chart.ts index 6e8122b3..13268f18 100644 --- a/ember-apache-echarts/src/modifiers/graph-chart.ts +++ b/ember-apache-echarts/src/modifiers/graph-chart.ts @@ -91,7 +91,7 @@ type TitleConfig = { }; export default class GraphChartModifier extends AbstractChartModifier { - @tracked drillPath = []; + @tracked drillPath: number[] = []; /** * Configures the chart with the provided arguments. @@ -122,7 +122,6 @@ export default class GraphChartModifier extends AbstractChartModifier { const seriesIndex = fromActionPayload['seriesIndex']; const dataIndex = fromActionPayload['dataIndexInside']; const series = args.series[seriesIndex]; - // @ts-expect-error: TypeScript doesn't know about the structure of `series.data` const name = series.data[dataIndex] ? series.data[dataIndex].name : null; if (name) { @@ -205,9 +204,7 @@ export default class GraphChartModifier extends AbstractChartModifier { const { layout, args, styles } = context; const { drillUpButtonText = '<' } = args; - // @ts-expect-error: Need to figure out what these do const style = resolveStyle(styles.drillUpButton, layout); - // @ts-expect-error: Need to figure out what these do const titleStyle = resolveStyle(styles.chartTitle, layout); const xMargins = style.marginLeft + style.marginRight; const yMargins = style.marginTop + style.marginBottom; @@ -234,8 +231,7 @@ export default class GraphChartModifier extends AbstractChartModifier { /** * Generates the configuration for the drill up button. */ - // @ts-expect-error: Need to figure out what these do - generateDrillUpButtonConfig(text: string, layout, style) { + generateDrillUpButtonConfig(text: string, layout: { width: number; height: number; x: number; y: number }, style: { marginLeft: number; marginTop: number; paddingLeft: number; paddingRight: number; paddingTop: number; paddingBottom: number; borderTopLeftRadius?: number; borderTopRightRadius?: number; borderBottomRightRadius?: number; borderBottomLeftRadius?: number; borderColor?: string; backgroundColor?: string; color: string; fontStyle: string; fontWeight: string; fontSize: number; fontFamily: string }) { const textMetrics = computeTextMetrics(text, style); return { @@ -279,8 +275,7 @@ export default class GraphChartModifier extends AbstractChartModifier { }, }, ], - // @ts-expect-error: Need to figure out what these do - onclick: () => this.drillPath.popObject(), + onclick: () => this.drillPath.pop(), }, ], }; @@ -289,8 +284,7 @@ export default class GraphChartModifier extends AbstractChartModifier { /** * Generates the plot configuration for the graph chart. */ - // @ts-expect-error: Need to figure out what these do - generatePlotConfig(info, _cell, _context, _gridIndex) { + generatePlotConfig(info: { data: unknown[]; links: unknown[] }, _cell: unknown, _context: unknown, _gridIndex: unknown) { // Implement the logic to generate the plot configuration return { // Example configuration diff --git a/ember-apache-echarts/src/modifiers/pie-chart.ts b/ember-apache-echarts/src/modifiers/pie-chart.ts index 0a5047a2..b2c9da6c 100644 --- a/ember-apache-echarts/src/modifiers/pie-chart.ts +++ b/ember-apache-echarts/src/modifiers/pie-chart.ts @@ -5,8 +5,8 @@ import type { ECharts, SelectChangedPayload } from 'echarts'; // https://echarts.apache.org/handbook/en/basics/import/ [twl 6.Apr.22] type ChartArgs = { - series?: unknown[]; - data?: unknown[]; + series?: { data: { name: string }[] }[]; + data?: { name: string }[]; tooltipFormatter?: (params: unknown) => string; onSelect?: (name: string | null) => void; variant?: 'pie' | 'donut'; @@ -69,7 +69,6 @@ export default class PieChartModifier extends AbstractChartModifier { const seriesIndex = fromActionPayload['seriesIndex']; const dataIndex = fromActionPayload['dataIndexInside']; const series = allSeries[seriesIndex]; - // @ts-expect-error: until the abstract is typed this needs to wait const name = series.data[dataIndex] ? series.data[dataIndex].name : null; if (name) { @@ -86,8 +85,11 @@ export default class PieChartModifier extends AbstractChartModifier { /** * Generates the plot config for a single plot on this chart. */ - // @ts-expect-error: until the abstract is typed this needs to wait - generatePlotConfig(series, layout, context) { + generatePlotConfig( + series: { data: { name: string }[] }, + layout: { innerHeight: number; innerWidth: number; innerX: number; innerY: number }, + context: { args: ChartArgs } + ) { const { variant, noDataText } = context.args; return (!series.data || series.data.length == 0) && noDataText @@ -114,8 +116,12 @@ export default class PieChartModifier extends AbstractChartModifier { /** * Generates text to overlay on each cell of the chart, if any. */ - // @ts-expect-error: until the abstract is typed this needs to wait - generateTextOverlayConfig(series, args, layout, style) { + generateTextOverlayConfig( + series: { data: { name: string }[] }, + args: ChartArgs, + layout: { innerWidth: number; innerHeight: number; innerX: number; innerY: number }, + style: { [key: string]: unknown } + ) { const { noDataText } = args; return (!series.data || series.data.length == 0) && noDataText diff --git a/ember-apache-echarts/src/modifiers/tree-chart.ts b/ember-apache-echarts/src/modifiers/tree-chart.ts index 15a40b8d..eaff6917 100644 --- a/ember-apache-echarts/src/modifiers/tree-chart.ts +++ b/ember-apache-echarts/src/modifiers/tree-chart.ts @@ -59,7 +59,7 @@ type TitleConfig = { }; export default class GraphChartModifier extends AbstractChartModifier { - @tracked drillPath = []; + @tracked drillPath: number[] = []; /** * Configures the chart with the provided arguments. @@ -91,8 +91,7 @@ export default class GraphChartModifier extends AbstractChartModifier { const seriesIndex = fromActionPayload['seriesIndex']; const dataIndex = fromActionPayload['dataIndexInside']; const series = args.series?.[seriesIndex]; - // @ts-expect-error: TypeScript doesn't know about the structure of `series.data` - const name = series.data[dataIndex] ? series.data[dataIndex].name : null; + const name = series?.data[dataIndex]?.name ?? null; if (name) { chart.dispatchAction({ @@ -174,9 +173,7 @@ export default class GraphChartModifier extends AbstractChartModifier { const { layout, args, styles } = context; const { drillUpButtonText = '<' } = args; - // @ts-expect-error: Need to figure out what these do const style = resolveStyle(styles.drillUpButton, layout); - // @ts-expect-error: Need to figure out what these do const titleStyle = resolveStyle(styles.chartTitle, layout); const xMargins = style.marginLeft + style.marginRight; const yMargins = style.marginTop + style.marginBottom; @@ -186,9 +183,7 @@ export default class GraphChartModifier extends AbstractChartModifier { style.marginLeft += titleStyle.marginLeft; const buttonConfig = this.generateDrillUpButtonConfig(drillUpButtonText, layout, style); - // @ts-expect-error: Need to figure out what these do mergeAtPaths(config, [buttonConfig]); - // @ts-expect-error: Need to figure out what these do const buttonBox = buttonConfig['graphic.elements'][0].children[0].shape; return { @@ -203,61 +198,86 @@ export default class GraphChartModifier extends AbstractChartModifier { /** * Generates the configuration for the drill up button. */ - // @ts-expect-error: Need to figure out what these do - generateDrillUpButtonConfig(_text, _layout, _style) { - // const textMetrics = computeTextMetrics(text, style); - // return { - // 'graphic.elements': [ - // { - // type: 'group', - // left: style.marginLeft, - // top: style.marginTop, - // children: [ - // // NOTE: This element is referenced by path in `addDrillUpButton` - // { - // type: 'rect', - // shape: { - // width: textMetrics.width + style.paddingLeft + style.paddingRight, - // height: textMetrics.fontHeight + style.paddingTop + style.paddingBottom, - // r: [ - // style.borderTopLeftRadius ?? 0, - // style.borderTopRightRadius ?? 0, - // style.borderBottomRightRadius ?? 0, - // style.borderBottomLeftRadius ?? 0, - // ], - // }, - // style: { - // stroke: style.borderColor ?? '#fff', - // fill: style.backgroundColor ?? '#fff', - // }, - // }, - // { - // type: 'text', - // x: style.paddingLeft, - // y: style.paddingTop, - // style: { - // fill: style.color, - // text, - // font: `${style.fontStyle} ${style.fontWeight} ${style.fontSize}px ${style.fontFamily}`, - // }, - // textConfig: { - // distance: 0, - // inside: true, - // position: [10, 0], - // }, - // }, - // ], - // onclick: () => this.drillPath.popObject(), - // }, - // ], - // }; + generateDrillUpButtonConfig( + text: string, + layout: { width: number; height: number; x: number; y: number }, + style: { + marginLeft: number; + marginTop: number; + paddingLeft: number; + paddingRight: number; + paddingTop: number; + paddingBottom: number; + borderTopLeftRadius?: number; + borderTopRightRadius?: number; + borderBottomRightRadius?: number; + borderBottomLeftRadius?: number; + borderColor?: string; + backgroundColor?: string; + color: string; + fontStyle: string; + fontWeight: string; + fontSize: number; + fontFamily: string; + } + ) { + const textMetrics = computeTextMetrics(text, style); + + return { + 'graphic.elements': [ + { + type: 'group', + left: style.marginLeft, + top: style.marginTop, + children: [ + { + type: 'rect', + shape: { + width: textMetrics.width + style.paddingLeft + style.paddingRight, + height: textMetrics.fontHeight + style.paddingTop + style.paddingBottom, + r: [ + style.borderTopLeftRadius ?? 0, + style.borderTopRightRadius ?? 0, + style.borderBottomRightRadius ?? 0, + style.borderBottomLeftRadius ?? 0, + ], + }, + style: { + stroke: style.borderColor ?? '#fff', + fill: style.backgroundColor ?? '#fff', + }, + }, + { + type: 'text', + x: style.paddingLeft, + y: style.paddingTop, + style: { + fill: style.color, + text, + font: `${style.fontStyle} ${style.fontWeight} ${style.fontSize}px ${style.fontFamily}`, + }, + textConfig: { + distance: 0, + inside: true, + position: [10, 0], + }, + }, + ], + onclick: () => this.drillPath.pop(), + }, + ], + }; } /** * Generates the plot configuration for the graph chart. */ - // @ts-expect-error: Need to figure out what these do - generatePlotConfig(info, _cell, _context, _gridIndex) { + generatePlotConfig( + info: { data: unknown[]; links: unknown[] }, + _cell: unknown, + _context: unknown, + _gridIndex: unknown + ) { // Implement the logic to generate the plot configuration return { // Example configuration diff --git a/ember-apache-echarts/src/utils/chart/parse-axis-label.ts b/ember-apache-echarts/src/utils/chart/parse-axis-label.ts index 5b326b72..165c476f 100644 --- a/ember-apache-echarts/src/utils/chart/parse-axis-label.ts +++ b/ember-apache-echarts/src/utils/chart/parse-axis-label.ts @@ -9,7 +9,7 @@ * * @return {object} An object containing the `label` and an optional `type` */ -export default function parseAxisLabel(label: string) { +export default function parseAxisLabel(label: string): { label: string; type?: string } { if (label.startsWith('{') && label.endsWith('}') && label.includes('|')) { const [type, text] = label.substring(1, label.length - 2).split('|'); diff --git a/ember-apache-echarts/src/utils/create-lookup.ts b/ember-apache-echarts/src/utils/create-lookup.ts index 65757245..9ee2c533 100644 --- a/ember-apache-echarts/src/utils/create-lookup.ts +++ b/ember-apache-echarts/src/utils/create-lookup.ts @@ -14,23 +14,23 @@ * for all elements in the array. If `duplicateKeys` is set, * then the value property will be an an array of values. */ -export default function createLookup( - // @ts-expect-error: Return to these as I am not sure what the purpose of this is. - array, - keyProperty = 'id', - valueProperty?: string, +export default function createLookup( + array: T[], + keyProperty: K = 'id' as K, + valueProperty?: V, duplicateKeys = false -) { +): Record { return !array ? {} - : // @ts-expect-error: Return to these as I am not sure what the purpose of this is. - array.reduce((lookup, item) => { - const key = item[keyProperty]; + : array.reduce((lookup: Record, item: T) => { + const key = item[keyProperty] as unknown as string; const value = valueProperty ? item[valueProperty] : item; if (duplicateKeys) { - lookup[key] = lookup[key] ?? []; - lookup[key].push(value); + if (!lookup[key]) { + lookup[key] = []; + } + (lookup[key] as T[V][]).push(value); } else { lookup[key] = value; } diff --git a/ember-apache-echarts/src/utils/data/compute-statistic.ts b/ember-apache-echarts/src/utils/data/compute-statistic.ts index b21917cb..9c067977 100644 --- a/ember-apache-echarts/src/utils/data/compute-statistic.ts +++ b/ember-apache-echarts/src/utils/data/compute-statistic.ts @@ -1,4 +1,3 @@ -// @ts-expect-error: remove lodash import { compact } from 'lodash-es'; import getUniqueDatasetValues from './get-unique-dataset-values.ts'; @@ -18,6 +17,6 @@ type Stat = 'min' | 'max'; * * @return {number} The value of the statistic for the dataset */ -export default function computeStatistic(dataset: DataSeries[], stat: Stat, property = 'value') { +export default function computeStatistic(dataset: DataSeries[], stat: Stat, property = 'value'): number { return Math[stat](...compact(getUniqueDatasetValues(dataset, property))); } diff --git a/ember-apache-echarts/src/utils/data/get-series-data.ts b/ember-apache-echarts/src/utils/data/get-series-data.ts index 7eb6781c..56de326f 100644 --- a/ember-apache-echarts/src/utils/data/get-series-data.ts +++ b/ember-apache-echarts/src/utils/data/get-series-data.ts @@ -1,4 +1,3 @@ -// @ts-expect-error: remove lodash import { compact } from 'lodash-es'; import createLookup from '../create-lookup.ts'; @@ -23,7 +22,7 @@ export default function getSeriesData( categories: string[], categoryProperty: string, valueProperty: string -) { +): number[] { const lookup = createLookup(data, categoryProperty, valueProperty); return compact(categories.map((category) => lookup[category])); diff --git a/ember-apache-echarts/src/utils/data/get-series-totals.ts b/ember-apache-echarts/src/utils/data/get-series-totals.ts index 41f0e741..d49d3b3a 100644 --- a/ember-apache-echarts/src/utils/data/get-series-totals.ts +++ b/ember-apache-echarts/src/utils/data/get-series-totals.ts @@ -1,4 +1,3 @@ -// @ts-expect-error: remove lodash import { flatten, transform } from 'lodash-es'; type DataItem = { @@ -24,18 +23,17 @@ export default function getSeriesTotals( categories: string[], categoryProperty: string, valueProperty: string -) { +): number[] { const allData = flatten(data.map((info) => info.data)); return Object.values( transform( allData, - // @ts-expect-error: remove lodash (totals, item) => { totals[item?.[categoryProperty]] = (totals[item?.[categoryProperty]] ?? 0) + (item?.[valueProperty] ?? 0); }, - {} + {} as Record ) ); } diff --git a/ember-apache-echarts/src/utils/data/get-unique-dataset-values.ts b/ember-apache-echarts/src/utils/data/get-unique-dataset-values.ts index 250115ec..87c011ef 100644 --- a/ember-apache-echarts/src/utils/data/get-unique-dataset-values.ts +++ b/ember-apache-echarts/src/utils/data/get-unique-dataset-values.ts @@ -12,7 +12,7 @@ type DataSeries = { * * @return {any[]} An array of unique property values for `property` */ -export default function getUniqueDatasetValues(dataset: DataSeries[], property: string) { +export default function getUniqueDatasetValues(dataset: DataSeries[], property: string): any[] { const result = new Map(); for (const series of dataset) { diff --git a/ember-apache-echarts/src/utils/data/rotate-data-series.ts b/ember-apache-echarts/src/utils/data/rotate-data-series.ts index 33a4183c..bb5582c9 100644 --- a/ember-apache-echarts/src/utils/data/rotate-data-series.ts +++ b/ember-apache-echarts/src/utils/data/rotate-data-series.ts @@ -1,6 +1,14 @@ -// @ts-expect-error: remove lodash import { omit } from 'lodash-es'; -import getUniqueDatasetValues from './get-unique-dataset-values.ts'; +import getUniqueDatasetValues from './get-unique-dataset-values'; + +interface DataItem { + [key: string]: any; +} + +interface Series { + label: string; + data: DataItem[]; +} /** * Rotates the data series so the data elements in each series become series @@ -11,21 +19,18 @@ import getUniqueDatasetValues from './get-unique-dataset-values.ts'; * and the data within each series are the columns in those rows, this swaps the * rows for the columns. * - * @param {object[]} data An array of data objects + * @param {Series[]} data An array of data objects * @param {string} categoryProperty The name of the property in each data * object that represents the category * @param {string} valueProperty The name of the property in each data * object that represents the value * - * @return {object[]} data An array of data objects + * @return {Series[]} data An array of data objects */ -// @ts-expect-error: not sure -const rotateDataSeries = (data, categoryProperty: string, valueProperty: string) => +const rotateDataSeries = (data: Series[], categoryProperty: string, valueProperty: string): Series[] => getUniqueDatasetValues(data, categoryProperty).map((label) => ({ label, - // @ts-expect-error: not sure data: data.map((series) => { - // @ts-expect-error: not sure const item = series.data.find((item) => item[categoryProperty] === label); return !item @@ -35,7 +40,7 @@ const rotateDataSeries = (data, categoryProperty: string, valueProperty: string) [valueProperty]: item[valueProperty], ...omit(series, 'data', 'label'), }; - }), + }).filter(Boolean) as DataItem[], })); export default rotateDataSeries;