Skip to content

Commit f50faa5

Browse files
authored
Upgrade to mosaic v0.18 (#81)
1 parent 90d5150 commit f50faa5

18 files changed

+462
-764
lines changed

deno.json

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -22,13 +22,13 @@
2222
"include": ["lib", "README.md"]
2323
},
2424
"imports": {
25-
"@js-temporal/polyfill": "npm:@js-temporal/polyfill@~0.4.4",
25+
"@js-temporal/polyfill": "npm:@js-temporal/polyfill@^0.5.1",
2626
"@lukeed/uuid": "npm:@lukeed/uuid@^2.0.1",
27-
"@preact/signals-core": "npm:@preact/signals-core@^1.8.0",
28-
"@uwdata/flechette": "npm:@uwdata/flechette@^1.1.0",
29-
"@uwdata/mosaic-core": "npm:@uwdata/mosaic-core@~0.11.0",
30-
"@uwdata/mosaic-plot": "npm:@uwdata/mosaic-plot@~0.11.0",
31-
"@uwdata/mosaic-sql": "npm:@uwdata/mosaic-sql@~0.11.0",
27+
"@preact/signals-core": "npm:@preact/signals-core@^1.11.0",
28+
"@uwdata/flechette": "npm:@uwdata/flechette@^2.1.0",
29+
"@uwdata/mosaic-core": "npm:@uwdata/mosaic-core@~0.18.0",
30+
"@uwdata/mosaic-plot": "npm:@uwdata/mosaic-plot@~0.18.0",
31+
"@uwdata/mosaic-sql": "npm:@uwdata/mosaic-sql@~0.18.0",
3232
"d3": "npm:d3@^7.9.0",
3333
"htl": "npm:htl@~0.3.1"
3434
},

deno.lock

Lines changed: 297 additions & 178 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

lib/clients/DataTable.ts

Lines changed: 26 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,17 @@
11
import * as flech from "@uwdata/flechette";
2-
// @ts-types="../deps/mosaic-core.d.ts"
32
import {
43
type Coordinator,
5-
type FieldInfo,
6-
type FieldRequest,
74
MosaicClient,
5+
queryFieldInfo,
86
Selection,
97
} from "@uwdata/mosaic-core";
10-
// @ts-types="../deps/mosaic-sql.d.ts"
11-
import { desc, Query, type SQLExpression } from "@uwdata/mosaic-sql";
8+
import {
9+
asc,
10+
desc,
11+
type FilterExpr,
12+
Query,
13+
type SelectQuery,
14+
} from "@uwdata/mosaic-sql";
1215
import * as signals from "@preact/signals-core";
1316
import { html } from "htl";
1417

@@ -21,6 +24,7 @@ import { signal } from "@preact/signals-core";
2124

2225
import stylesString from "./styles.css.ts";
2326
import { StatusBar } from "./StatusBar.ts";
27+
import { isFlechetteTable } from "../utils/guards.ts";
2428

2529
interface DataTableOptions {
2630
table: string;
@@ -55,7 +59,9 @@ export async function datatable(
5559
.from(table)
5660
.select(options.columns ?? ["*"])
5761
.limit(0),
62+
{ type: "arrow" },
5863
);
64+
assert(isFlechetteTable(empty), "Expected a flechette table");
5965
let client = new DataTable({
6066
table,
6167
schema: empty.schema,
@@ -156,17 +162,6 @@ export class DataTable extends MosaicClient {
156162
return this.#sql;
157163
}
158164

159-
/**
160-
* Mosaic function. Client defines the fields to be requested from the coordinator.
161-
*/
162-
override fields(): Array<FieldRequest> {
163-
return this.#columns.map((column) => ({
164-
table: this.#meta.table,
165-
column,
166-
stats: [],
167-
}));
168-
}
169-
170165
node(): HTMLElement {
171166
return this.#root;
172167
}
@@ -187,10 +182,10 @@ export class DataTable extends MosaicClient {
187182
* @param filter - The filter predicates to apply to the query
188183
* @returns The query to be executed
189184
*/
190-
override query(filter: Array<unknown> = []): Query {
185+
override query(filter?: FilterExpr | null | undefined): SelectQuery {
191186
let query = Query.from(this.#meta.table)
192187
.select(this.#columns)
193-
.where(filter)
188+
.where(filter ?? [])
194189
.orderby(
195190
this.#orderby
196191
.filter((o) => o.order !== "unset")
@@ -227,7 +222,7 @@ export class DataTable extends MosaicClient {
227222
/**
228223
* Mosaic lifecycle function called after `queryResult`
229224
*/
230-
update(): this {
225+
override update(): this {
231226
if (!this.#pendingInternalRequest) {
232227
// on the first update, populate the table with initial data
233228
this.#appendRows(this.#rows * 2);
@@ -244,21 +239,26 @@ export class DataTable extends MosaicClient {
244239
this.requestQuery(query);
245240

246241
// prefetch subsequent data batch
247-
this.coordinator.prefetch(query.clone().offset(offset + this.#limit));
242+
this.coordinator!.prefetch(query.clone().offset(offset + this.#limit));
248243
}
249244

250-
/**
251-
* Mosaic lifecycle function called when the client is connected to a coordinator.
252-
*/
253-
override fieldInfo(infos: Array<FieldInfo>): this {
245+
override async prepare(): Promise<void> {
246+
const infos = await queryFieldInfo(
247+
this.coordinator!,
248+
this.#columns.map((column) => ({
249+
table: this.#meta.table,
250+
column,
251+
stats: [],
252+
})),
253+
);
254254
let classes = classof(this.#meta.schema);
255255

256256
{
257257
let statusBar = new StatusBar({
258258
table: this.#meta.table,
259259
filterBy: this.filterBy,
260260
});
261-
this.coordinator.connect(statusBar);
261+
this.coordinator!.connect(statusBar);
262262
this.#shadowRoot.querySelector(".quak")?.appendChild(
263263
statusBar.node(),
264264
);
@@ -290,7 +290,7 @@ export class DataTable extends MosaicClient {
290290
});
291291
}
292292
let th = thcol(field, this.#columnWidth, vis);
293-
this.coordinator.connect(vis);
293+
this.coordinator!.connect(vis);
294294
return th;
295295
});
296296

@@ -334,8 +334,6 @@ export class DataTable extends MosaicClient {
334334
}
335335
});
336336
}
337-
338-
return this;
339337
}
340338

341339
/** Number of rows to append */
@@ -557,23 +555,6 @@ function shouldGrayoutValue(value: string) {
557555
);
558556
}
559557

560-
/**
561-
* A mosaic SQL expression for ascending order
562-
*
563-
* The normal behavior in SQL is to sort nulls first when sorting in ascending order.
564-
* This function returns an expression that sorts nulls last (i.e., `NULLS LAST`),
565-
* like the `desc` function.
566-
*
567-
* @param field
568-
*/
569-
function asc(field: string): SQLExpression {
570-
// doesn't sort nulls for asc
571-
let expr = desc(field);
572-
// @ts-expect-error - private field
573-
expr._expr[0] = expr._expr[0].replace("DESC", "ASC");
574-
return expr;
575-
}
576-
577558
/**
578559
* Adds custom wheel behavior to an HTML element, allowing either horizontal or vertical scrolling based on the scroll input.
579560
* Prevents default scrolling to stop event propagation to parent elements.

lib/clients/Histogram.ts

Lines changed: 18 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,10 @@
1-
// @ts-types="../deps/mosaic-core.d.ts";
21
import {
3-
type ColumnField,
42
type FieldInfo,
5-
type FieldRequest,
63
MosaicClient,
4+
queryFieldInfo,
75
type Selection,
86
} from "@uwdata/mosaic-core";
9-
// @ts-types="../deps/mosaic-sql.d.ts";
10-
import { count, Query, type SQLExpression } from "@uwdata/mosaic-sql";
7+
import { count, type ExprNode, Query } from "@uwdata/mosaic-sql";
118
import * as mplot from "@uwdata/mosaic-plot";
129
import type * as flech from "@uwdata/flechette";
1310

@@ -42,9 +39,9 @@ export class Histogram extends MosaicClient implements Mark {
4239
};
4340
#el: HTMLElement = document.createElement("div");
4441
#select: {
45-
x1: ColumnField;
46-
x2: ColumnField;
47-
y: SQLExpression;
42+
x1: ExprNode;
43+
x2: ExprNode;
44+
y: ExprNode;
4845
};
4946
#interval: mplot.Interval1D | undefined = undefined;
5047
#initialized: boolean = false;
@@ -71,26 +68,25 @@ export class Histogram extends MosaicClient implements Mark {
7168
});
7269
}
7370

74-
override fields(): Array<FieldRequest> {
75-
return [
76-
{
77-
table: this.#source.table,
78-
column: this.#source.column,
79-
stats: ["min", "max"],
80-
},
81-
];
82-
}
83-
84-
override fieldInfo(info: Array<FieldInfo>) {
71+
override async prepare(): Promise<void> {
72+
const info = await queryFieldInfo(
73+
this.coordinator!,
74+
[
75+
{
76+
table: this.#source.table,
77+
column: this.#source.column,
78+
stats: ["min", "max"],
79+
},
80+
],
81+
);
8582
this.#fieldInfo = info[0];
86-
return this;
8783
}
8884
/**
8985
* Return a query specifying the data needed by this Mark client.
9086
* @param filter The filtering criteria to apply in the query.
9187
* @returns The client query
9288
*/
93-
override query(filter: Array<SQLExpression> = []): Query {
89+
override query(filter: Array<ExprNode> = []): Query {
9490
return Query
9591
.from({ source: this.#source.table })
9692
.select(this.#select)
@@ -128,7 +124,7 @@ export class Histogram extends MosaicClient implements Mark {
128124
/* Required by the Mark interface */
129125
type = "rectY";
130126
/** Required by `mplot.bin` to get the field info. */
131-
channelField(channel: string): FieldInfo {
127+
channelField(channel: string) {
132128
assert(channel === "x");
133129
assert(this.#fieldInfo, "No field info yet");
134130
return this.#fieldInfo;

lib/clients/StatusBar.ts

Lines changed: 7 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,5 @@
1-
import * as flech from "@uwdata/flechette";
2-
// @ts-types="../deps/mosaic-core.d.ts"
3-
import {
4-
type Interactor,
5-
MosaicClient,
6-
type Selection,
7-
} from "@uwdata/mosaic-core";
8-
// @ts-types="../deps/mosaic-sql.d.ts"
1+
import type * as flech from "@uwdata/flechette";
2+
import { MosaicClient, type Selection } from "@uwdata/mosaic-core";
93
import { count, Query } from "@uwdata/mosaic-sql";
104
import { assert } from "../utils/assert.ts";
115

@@ -35,18 +29,7 @@ export class StatusBar extends MosaicClient {
3529
this.#el.classList.add("status-bar");
3630

3731
this.#button.addEventListener("mousedown", () => {
38-
if (!this.filterBy) return;
39-
// TODO: A better way to do this?
40-
// We want to clear all the existing selections
41-
// @see https://github.com/uwdata/mosaic/blob/8e63149753e7d6ca30274c032a04744e14df2fd6/packages/core/src/Selection.js#L265-L272
42-
for (let { source } of this.filterBy.clauses) {
43-
if (!isInteractor(source)) {
44-
console.warn("Skipping non-interactor source", source);
45-
continue;
46-
}
47-
source.reset();
48-
this.filterBy.update(source.clause());
49-
}
32+
this.filterBy?.reset();
5033
});
5134

5235
this.#button.style.visibility = "hidden";
@@ -61,21 +44,17 @@ export class StatusBar extends MosaicClient {
6144
}
6245

6346
override query(filter = []) {
64-
let query = Query.from(this.#table)
47+
return Query.from(this.#table)
6548
.select({ count: count() })
6649
.where(filter);
67-
return query;
6850
}
6951

7052
override queryResult(table: flech.Table) {
53+
let count: unknown = table.get(0)?.count ?? 0;
7154
assert(
72-
isNumericArrowField(
73-
table.schema.fields.find((f) => f.name === "count"),
74-
),
75-
"Expected count field to be an integer or float",
55+
typeof count === "number" && !Number.isNaN(count),
56+
"Got NaN for count.",
7657
);
77-
78-
let count = Number(table.get(0)?.count ?? 0);
7958
if (!this.#totalRows) {
8059
// we need to know the total number of rows to display
8160
this.#totalRows = count;
@@ -94,18 +73,3 @@ export class StatusBar extends MosaicClient {
9473
return this.#el;
9574
}
9675
}
97-
98-
function isObject(x: unknown): x is Record<string, unknown> {
99-
return typeof x === "object" && x !== null && !Array.isArray(x);
100-
}
101-
102-
function isInteractor(x: unknown): x is Interactor {
103-
return isObject(x) && "clause" in x && "reset" in x;
104-
}
105-
106-
function isNumericArrowField(field?: flech.Field) {
107-
return (
108-
field?.type.typeId === flech.Type.Int ||
109-
field?.type.typeId === flech.Type.Float
110-
);
111-
}

0 commit comments

Comments
 (0)