Skip to content

Commit

Permalink
feat: Show category counts/percentages on ValueCounts hover (#43)
Browse files Browse the repository at this point in the history
* swap column for arrow field (to carry along data-type)

* pass along field data to ValueCountsPlot

* Update text on hover to display count of hovered value

* remove lint-only changes
  • Loading branch information
jwilber authored Aug 16, 2024
1 parent 317db12 commit 90da659
Show file tree
Hide file tree
Showing 3 changed files with 38 additions and 5 deletions.
2 changes: 1 addition & 1 deletion lib/clients/DataTable.ts
Original file line number Diff line number Diff line change
Expand Up @@ -265,7 +265,7 @@ export class DataTable extends MosaicClient {
} else {
vis = new ValueCounts({
table: this.#meta.table,
column: field.name,
field: field,
filterBy: this.filterBy!,
});
}
Expand Down
10 changes: 6 additions & 4 deletions lib/clients/ValueCounts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@ import { assert } from "../utils/assert.ts";
interface UniqueValuesOptions {
/** The table to query. */
table: string;
/** The column to use for the histogram. */
column: string;
/** An arrow Field containing the column info to use for the histogram. */
field: arrow.Field;
/** A mosaic selection to filter the data. */
filterBy: Selection;
}
Expand All @@ -29,13 +29,15 @@ type CountTable = arrow.Table<{ key: arrow.Utf8; total: arrow.Int }>;
export class ValueCounts extends MosaicClient {
#table: string;
#column: string;
#field: arrow.Field;
#el: HTMLElement = document.createElement("div");
#plot: ReturnType<typeof ValueCountsPlot> | undefined;

constructor(options: UniqueValuesOptions) {
super(options.filterBy);
this.#table = options.table;
this.#column = options.column;
this.#column = options.field.name;
this.#field = options.field;

// FIXME: There is some issue with the mosaic client or the query we
// are using here. Updates to the Selection (`filterBy`) seem to be
Expand Down Expand Up @@ -83,7 +85,7 @@ export class ValueCounts extends MosaicClient {

queryResult(data: CountTable): this {
if (!this.#plot) {
let plot = this.#plot = ValueCountsPlot(data);
let plot = (this.#plot = ValueCountsPlot(data, this.#field));
this.#el.appendChild(plot);
effect(() => {
let clause = this.clause(plot.selected.value);
Expand Down
31 changes: 31 additions & 0 deletions lib/utils/ValueCountsPlot.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { effect, signal } from "@preact/signals-core";
import type * as arrow from "apache-arrow";
import * as d3 from "../deps/d3.ts";
import { assert } from "./assert.ts";
import { formatDataType } from "./formatting.ts";

type CountTableData = arrow.Table<{
key: arrow.Utf8;
Expand All @@ -22,6 +23,7 @@ interface ValueCountsPlot {

export function ValueCountsPlot(
data: CountTableData,
field: arrow.Field,
{
width = 125,
height = 30,
Expand All @@ -33,6 +35,8 @@ export function ValueCountsPlot(
backgroundBarColor = "rgb(226, 226, 226)",
}: ValueCountsPlot = {},
) {
const fieldType = formatDataType(field.type);

let root = document.createElement("div");
root.style.position = "relative";

Expand Down Expand Up @@ -64,6 +68,7 @@ export function ValueCountsPlot(
let hovering = signal<string | undefined>(undefined);
let selected = signal<string | undefined>(undefined);
let counts = signal<CountTableData>(data);
let countLabel = signal<string>(fieldType);

let hitArea = document.createElement("div");
Object.assign(hitArea.style, {
Expand All @@ -77,9 +82,28 @@ export function ValueCountsPlot(
});
hitArea.addEventListener("mousemove", (event) => {
hovering.value = bars.nearestX(event);

let update: Record<string, number> = Object.fromEntries(
Array.from(data.toArray(), (d) => [d.key, d.total]),
);

let total = Object.values(update).reduce((a, b) => a + b, 0);

const hoveredValue = hovering.value;
const hoveredValueCount = hoveredValue !== undefined
? update[hoveredValue]
: undefined;

countLabel.value =
hoveredValue !== undefined && hoveredValueCount !== undefined
? `${hoveredValueCount} row${hoveredValueCount === 1 ? "" : "s"} (${
d3.format(".1%")(hoveredValueCount / total)
})`
: fieldType;
});
hitArea.addEventListener("mouseout", () => {
hovering.value = undefined;
countLabel.value = fieldType;
});
hitArea.addEventListener("mousedown", (event) => {
let next = bars.nearestX(event);
Expand All @@ -89,6 +113,13 @@ export function ValueCountsPlot(
effect(() => {
text.textContent = bars.textFor(hovering.value ?? selected.value);
bars.render(counts.value, hovering.value, selected.value);

const labelElement = root.parentElement?.parentElement?.querySelector(
".gray",
);
if (labelElement) {
labelElement.textContent = countLabel.value;
}
});

root.appendChild(container);
Expand Down

0 comments on commit 90da659

Please sign in to comment.