Skip to content

Commit

Permalink
Merge pull request #134 from sgratzl/release/v4.4.0
Browse files Browse the repository at this point in the history
Release v4.4.0
  • Loading branch information
sgratzl authored Jun 9, 2024
2 parents 9c51b11 + 75decf4 commit c43fcd4
Show file tree
Hide file tree
Showing 12 changed files with 137 additions and 30 deletions.
8 changes: 8 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,15 @@ updates:
directory: '/'
schedule:
interval: 'monthly'
target-branch: 'dev'
labels:
- 'dependencies'
- 'chore'
- package-ecosystem: 'npm'
directory: '/'
schedule:
interval: 'monthly'
target-branch: 'dev'
labels:
- 'dependencies'
- 'chore'
2 changes: 1 addition & 1 deletion .github/workflows/create_release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ jobs:
run: |
echo "next_tag=$(npm version --no-git-tag-version ${{ github.event.inputs.versionName }} --preid ${{ github.event.inputs.preid }})" >> $GITHUB_OUTPUT
- name: Create pull request into main
uses: peter-evans/create-pull-request@v4
uses: peter-evans/create-pull-request@v6
with:
branch: release/${{ steps.version.outputs.next_tag }}
commit-message: 'chore: release ${{ steps.version.outputs.next_tag }}'
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/deploy_website.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ jobs:
${{ runner.os }}-yarn2-v5
- run: yarn install
- run: yarn docs:build
- uses: actions/configure-pages@v4
- uses: actions/configure-pages@v5
- uses: actions/upload-pages-artifact@v3
with:
path: docs/.vitepress/dist
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/release_helper.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ jobs:
echo "releasing ${{ steps.extract_version.outputs.version }} with tag ${{ steps.extract_version.outputs.npm_tag }}"
- name: Create Release
id: create_release
uses: release-drafter/release-drafter@v5
uses: release-drafter/release-drafter@v6
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
Expand Down Expand Up @@ -70,7 +70,7 @@ jobs:
- run: yarn build
- run: yarn pack
- name: Upload Release Asset
uses: AButler/upload-release-assets@v2.0.2
uses: AButler/upload-release-assets@v3.0
with:
files: 'package.tgz'
repo-token: ${{ secrets.GITHUB_TOKEN }}
Expand Down
3 changes: 2 additions & 1 deletion docs/examples/items.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,9 @@ export const config: ChartConfiguration<'boxplot'> = {
data,
options: {
elements: {
boxplot: {
boxandwhiskers: {
itemRadius: 2,
itemHitRadius: 4,
},
},
},
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "@sgratzl/chartjs-chart-boxplot",
"description": "Chart.js module for charting boxplots and violin charts",
"version": "4.3.3",
"version": "4.4.0",
"publishConfig": {
"access": "public"
},
Expand Down
5 changes: 5 additions & 0 deletions src/controllers/BoxPlotController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
AnimationOptions,
ScriptableContext,
CartesianScaleTypeRegistry,
BarControllerDatasetOptions,
} from 'chart.js';
import { merge } from 'chart.js/helpers';
import { asBoxPlotStats, IBoxPlot, IBoxplotOptions } from '../data';
Expand Down Expand Up @@ -70,6 +71,10 @@ export class BoxPlotController extends StatsBase<IBoxPlot, Required<IBoxplotOpti

export interface BoxPlotControllerDatasetOptions
extends ControllerDatasetOptions,
Pick<
BarControllerDatasetOptions,
'barPercentage' | 'barThickness' | 'categoryPercentage' | 'maxBarThickness' | 'minBarLength'
>,
IBoxplotOptions,
ScriptableAndArrayOptions<IBoxAndWhiskersOptions, ScriptableContext<'boxplot'>>,
ScriptableAndArrayOptions<CommonHoverOptions, ScriptableContext<'boxplot'>>,
Expand Down
10 changes: 9 additions & 1 deletion src/controllers/StatsBase.ts
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,10 @@ export abstract class StatsBase<S extends IBaseStats, C extends Required<IBaseOp
/**
* @hidden
*/
getLabelAndValue(index: number): { label: string; value: string & { raw: S; hoveredOutlierIndex: number } & S } {
getLabelAndValue(index: number): {
label: string;
value: string & { raw: S; hoveredOutlierIndex: number; hoveredItemIndex: number } & S;
} {
const r = super.getLabelAndValue(index) as any;
const { vScale } = this._cachedMeta;
const parsed = this.getParsed(index) as unknown as S;
Expand All @@ -157,6 +160,7 @@ export abstract class StatsBase<S extends IBaseStats, C extends Required<IBaseOp
r.value = {
raw: parsed,
hoveredOutlierIndex: -1,
hoveredItemIndex: -1,
};
this._transformStats(r.value, parsed, (v) => vScale.getLabelForValue(v));
const s = this._toStringStats(r.value.raw);
Expand All @@ -166,6 +170,10 @@ export abstract class StatsBase<S extends IBaseStats, C extends Required<IBaseOp
// TODO formatter
return `(outlier: ${this.outliers[this.hoveredOutlierIndex]})`;
}
if (this.hoveredItemIndex >= 0) {
// TODO formatter
return `(item: ${this.items[this.hoveredItemIndex]})`;
}
return s;
};
return r;
Expand Down
5 changes: 5 additions & 0 deletions src/controllers/ViolinController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
AnimationOptions,
ScriptableContext,
CartesianScaleTypeRegistry,
BarControllerDatasetOptions,
} from 'chart.js';
import { merge } from 'chart.js/helpers';
import { asViolinStats, IViolin, IViolinOptions } from '../data';
Expand Down Expand Up @@ -79,6 +80,10 @@ export type ViolinDataPoint = number[] | (Partial<IViolin> & Pick<IViolin, 'medi

export interface ViolinControllerDatasetOptions
extends ControllerDatasetOptions,
Pick<
BarControllerDatasetOptions,
'barPercentage' | 'barThickness' | 'categoryPercentage' | 'maxBarThickness' | 'minBarLength'
>,
IViolinOptions,
ScriptableAndArrayOptions<IViolinElementOptions, ScriptableContext<'violin'>>,
ScriptableAndArrayOptions<CommonHoverOptions, ScriptableContext<'violin'>>,
Expand Down
2 changes: 1 addition & 1 deletion src/elements/BoxAndWiskers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,6 @@ export class BoxAndWiskers extends StatsBase<IBoxAndWhiskerProps, IBoxAndWhisker

declare module 'chart.js' {
export interface ElementOptionsByType<TType extends ChartType> {
boxplot: ScriptableAndArrayOptions<IBoxAndWhiskersOptions & CommonHoverOptions, ScriptableContext<TType>>;
boxandwhiskers: ScriptableAndArrayOptions<IBoxAndWhiskersOptions & CommonHoverOptions, ScriptableContext<TType>>;
}
}
117 changes: 95 additions & 22 deletions src/elements/base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,13 @@ export interface IStatsBaseOptions {
* @indexable
*/
itemBorderWidth: number;
/**
* hit radius for hit test of items
* @default 0
* @scriptable
* @indexable
*/
itemHitRadius: number;

/**
* padding that is added around the bounding box when computing a mouse hit
Expand Down Expand Up @@ -195,6 +202,7 @@ export const baseDefaults = {
itemStyle: 'circle',
itemRadius: 0,
itemBorderWidth: 0,
itemHitRadius: 0,

meanStyle: 'circle',
meanRadius: 3,
Expand Down Expand Up @@ -399,7 +407,8 @@ export class StatsBase<T extends IStatsBaseProps & { mean?: number }, O extends
}
return (
this._boxInRange(mouseX, mouseY, useFinalPosition) ||
this._outlierIndexInRange(mouseX, mouseY, useFinalPosition) >= 0
this._outlierIndexInRange(mouseX, mouseY, useFinalPosition) != null ||
this._itemIndexInRange(mouseX, mouseY, useFinalPosition) != null
);
}

Expand All @@ -422,23 +431,75 @@ export class StatsBase<T extends IStatsBaseProps & { mean?: number }, O extends
/**
* @hidden
*/
protected _outlierIndexInRange(mouseX: number, mouseY: number, useFinalPosition?: boolean): number {
protected _outlierIndexInRange(
mouseX: number,
mouseY: number,
useFinalPosition?: boolean
): { index: number; x: number; y: number } | null {
const props = this.getProps(['x', 'y'], useFinalPosition);
const hitRadius = this.options.outlierHitRadius;
const outliers = this._getOutliers(useFinalPosition);
const vertical = this.isVertical();

// check if along the outlier line
if ((vertical && Math.abs(mouseX - props.x) > hitRadius) || (!vertical && Math.abs(mouseY - props.y) > hitRadius)) {
return -1;
return null;
}
const toCompare = vertical ? mouseY : mouseX;
for (let i = 0; i < outliers.length; i += 1) {
if (Math.abs(outliers[i] - toCompare) <= hitRadius) {
return i;
return vertical ? { index: i, x: props.x, y: outliers[i] } : { index: i, x: outliers[i], y: props.y };
}
}
return null;
}

/**
* @hidden
*/
protected _itemIndexInRange(
mouseX: number,
mouseY: number,
useFinalPosition?: boolean
): { index: number; x: number; y: number } | null {
const hitRadius = this.options.itemHitRadius;
if (hitRadius <= 0) {
return null;
}
const props = this.getProps(['x', 'y', 'items', 'width', 'height', 'outliers'], useFinalPosition);
const vert = this.isVertical();
const { options } = this;

if (options.itemRadius <= 0 || !props.items || props.items.length <= 0) {
return null;
}
// jitter based on random data
// use the dataset index and index to initialize the random number generator
const random = rnd(this._datasetIndex * 1000 + this._index);
const outliers = new Set(props.outliers || []);

if (vert) {
for (let i = 0; i < props.items.length; i++) {
const y = props.items[i];
if (!outliers.has(y)) {
const x = props.x - props.width / 2 + random() * props.width;
if (Math.abs(x - mouseX) <= hitRadius && Math.abs(y - mouseY) <= hitRadius) {
return { index: i, x, y };
}
}
}
} else {
for (let i = 0; i < props.items.length; i++) {
const x = props.items[i];
if (!outliers.has(x)) {
const y = props.y - props.height / 2 + random() * props.height;
if (Math.abs(x - mouseX) <= hitRadius && Math.abs(y - mouseY) <= hitRadius) {
return { index: i, x, y };
}
}
}
}
return -1;
return null;
}

/**
Expand Down Expand Up @@ -482,28 +543,40 @@ export class StatsBase<T extends IStatsBaseProps & { mean?: number }, O extends
if (tooltip) {
// eslint-disable-next-line no-param-reassign
delete tooltip._tooltipOutlier;
// eslint-disable-next-line no-param-reassign
delete tooltip._tooltipItem;
}

const props = this.getProps(['x', 'y']);
const index = this._outlierIndexInRange(eventPosition.x, eventPosition.y);
if (index < 0 || !tooltip) {
return this.getCenterPoint();
//outlier
const info = this._outlierIndexInRange(eventPosition.x, eventPosition.y);
if (info != null && tooltip) {
// hack in the data of the hovered outlier
// eslint-disable-next-line no-param-reassign
tooltip._tooltipOutlier = {
index: info.index,
datasetIndex: this._datasetIndex,
};
return {
x: info.x,
y: info.y,
};
}
// hack in the data of the hovered outlier
// eslint-disable-next-line no-param-reassign
tooltip._tooltipOutlier = {
index,
datasetIndex: this._datasetIndex,
};
if (this.isVertical()) {
// items
const itemInfo = this._itemIndexInRange(eventPosition.x, eventPosition.y);
if (itemInfo != null && tooltip) {
// hack in the data of the hovered outlier
// eslint-disable-next-line no-param-reassign
tooltip._tooltipItem = {
index: itemInfo.index,
datasetIndex: this._datasetIndex,
};
return {
x: props.x as number,
y: this._getOutliers()[index],
x: itemInfo.x,
y: itemInfo.y,
};
}
return {
x: this._getOutliers()[index],
y: props.y as number,
};

// fallback
return this.getCenterPoint();
}
}
7 changes: 7 additions & 0 deletions src/tooltip.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@ export interface ExtendedTooltip extends TooltipModel<'boxplot' | 'violin'> {
index: number;
datasetIndex: number;
};
_tooltipItem?: {
index: number;
datasetIndex: number;
};
}

/**
Expand All @@ -19,6 +23,9 @@ export function patchInHoveredOutlier(
if (value && that._tooltipOutlier != null && item.datasetIndex === that._tooltipOutlier.datasetIndex) {
value.hoveredOutlierIndex = that._tooltipOutlier.index;
}
if (value && that._tooltipItem != null && item.datasetIndex === that._tooltipItem.datasetIndex) {
value.hoveredItemIndex = that._tooltipItem.index;
}
}

/**
Expand Down

0 comments on commit c43fcd4

Please sign in to comment.