@uwdata/mosaic-inputs 0.9.0 → 0.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,13 +1,13 @@
1
1
  {
2
2
  "name": "@uwdata/mosaic-inputs",
3
- "version": "0.9.0",
3
+ "version": "0.11.0",
4
4
  "description": "Mosaic input components.",
5
5
  "keywords": [
6
6
  "inputs",
7
7
  "mosaic"
8
8
  ],
9
9
  "license": "BSD-3-Clause",
10
- "author": "Jeffrey Heer (http://idl.cs.washington.edu)",
10
+ "author": "Jeffrey Heer (https://idl.uw.edu)",
11
11
  "type": "module",
12
12
  "main": "src/index.js",
13
13
  "module": "src/index.js",
@@ -21,13 +21,13 @@
21
21
  "prebuild": "rimraf dist && mkdir dist",
22
22
  "build": "node ../../esbuild.js mosaic-inputs",
23
23
  "lint": "eslint src test",
24
- "test": "mocha 'test/**/*-test.js'",
24
+ "test": "vitest run",
25
25
  "prepublishOnly": "npm run test && npm run lint && npm run build"
26
26
  },
27
27
  "dependencies": {
28
- "@uwdata/mosaic-core": "^0.9.0",
29
- "@uwdata/mosaic-sql": "^0.9.0",
28
+ "@uwdata/mosaic-core": "^0.11.0",
29
+ "@uwdata/mosaic-sql": "^0.11.0",
30
30
  "isoformat": "^0.2.1"
31
31
  },
32
- "gitHead": "89bb9b0dfa747aed691eaeba35379525a6764c61"
32
+ "gitHead": "861d616f39926a1d2aee83b59dbdd70b0b3caf12"
33
33
  }
package/src/Menu.js CHANGED
@@ -1,4 +1,4 @@
1
- import { MosaicClient, Param, isParam, isSelection, point } from '@uwdata/mosaic-core';
1
+ import { MosaicClient, Param, isParam, isSelection, clausePoint } from '@uwdata/mosaic-core';
2
2
  import { Query } from '@uwdata/mosaic-sql';
3
3
  import { input } from './input.js';
4
4
 
@@ -122,7 +122,7 @@ export class Menu extends MosaicClient {
122
122
  const { selection, field } = this;
123
123
  if (isSelection(selection)) {
124
124
  if (value === '') value = undefined; // 'All' option
125
- const clause = point(field, value, { source: this });
125
+ const clause = clausePoint(field, value, { source: this });
126
126
  selection.update(clause);
127
127
  } else if (isParam(selection)) {
128
128
  selection.update(value);
package/src/Search.js CHANGED
@@ -1,4 +1,4 @@
1
- import { MosaicClient, Param, isParam, isSelection, match } from '@uwdata/mosaic-core';
1
+ import { MosaicClient, Param, isParam, isSelection, clauseMatch } from '@uwdata/mosaic-core';
2
2
  import { Query } from '@uwdata/mosaic-sql';
3
3
  import { input } from './input.js';
4
4
 
@@ -88,7 +88,7 @@ export class Search extends MosaicClient {
88
88
  publish(value) {
89
89
  const { selection, field, type } = this;
90
90
  if (isSelection(selection)) {
91
- const clause = match(field, value, { source: this, method: type });
91
+ const clause = clauseMatch(field, value, { source: this, method: type });
92
92
  selection.update(clause);
93
93
  } else if (isParam(selection)) {
94
94
  selection.update(value);
package/src/Slider.js CHANGED
@@ -1,4 +1,4 @@
1
- import { MosaicClient, Param, interval, isParam, isSelection, point } from '@uwdata/mosaic-core';
1
+ import { MosaicClient, Param, clauseInterval, clausePoint, isParam, isSelection } from '@uwdata/mosaic-core';
2
2
  import { Query, max, min } from '@uwdata/mosaic-sql';
3
3
  import { input } from './input.js';
4
4
 
@@ -145,14 +145,14 @@ export class Slider extends MosaicClient {
145
145
  if (selectionType === 'interval') {
146
146
  /** @type {[number, number]} */
147
147
  const domain = [this.min ?? 0, value];
148
- selection.update(interval(field, domain, {
148
+ selection.update(clauseInterval(field, domain, {
149
149
  source: this,
150
150
  bin: 'ceil',
151
151
  scale: { type: 'identity', domain },
152
152
  pixelSize: this.step
153
153
  }));
154
154
  } else {
155
- selection.update(point(field, value, { source: this }));
155
+ selection.update(clausePoint(field, value, { source: this }));
156
156
  }
157
157
  } else if (isParam(this.selection)) {
158
158
  selection.update(value);
package/src/Table.js CHANGED
@@ -1,4 +1,4 @@
1
- import { MosaicClient, coordinator } from '@uwdata/mosaic-core';
1
+ import { MosaicClient, clausePoints, coordinator, toDataColumns } from '@uwdata/mosaic-core';
2
2
  import { Query, column, desc } from '@uwdata/mosaic-sql';
3
3
  import { formatDate, formatLocaleAuto, formatLocaleNumber } from './util/format.js';
4
4
  import { input } from './input.js';
@@ -23,6 +23,7 @@ export class Table extends MosaicClient {
23
23
  maxWidth,
24
24
  height = 500,
25
25
  rowBatch = 100,
26
+ as
26
27
  } = {}) {
27
28
  super(filterBy);
28
29
  this.id = `table-${++_id}`;
@@ -36,6 +37,9 @@ export class Table extends MosaicClient {
36
37
  this.limit = +rowBatch;
37
38
  this.pending = false;
38
39
 
40
+ this.selection = as;
41
+ this.currentRow = -1;
42
+
39
43
  this.sortHeader = null;
40
44
  this.sortColumn = null;
41
45
  this.sortDesc = false;
@@ -72,10 +76,34 @@ export class Table extends MosaicClient {
72
76
  this.body = document.createElement('tbody');
73
77
  this.tbl.appendChild(this.body);
74
78
 
79
+ if (this.selection) {
80
+ this.body.addEventListener('pointerover', evt => {
81
+ const row = resolveRow(evt.target);
82
+ if (row > -1 && row !== this.currentRow) {
83
+ this.currentRow = row;
84
+ this.selection.update(this.clause([row]));
85
+ }
86
+ });
87
+ this.body.addEventListener('pointerleave', () => {
88
+ this.currentRow = -1;
89
+ this.selection.update(this.clause());
90
+ });
91
+ }
92
+
75
93
  this.style = document.createElement('style');
76
94
  this.element.appendChild(this.style);
77
95
  }
78
96
 
97
+ clause(rows = []) {
98
+ const { data, limit, schema } = this;
99
+ const fields = schema.map(s => s.column);
100
+ const values = rows.map(row => {
101
+ const { columns } = data[~~(row / limit)];
102
+ return fields.map(f => columns[f][row % limit]);
103
+ });
104
+ return clausePoints(fields, values, { source: this });
105
+ }
106
+
79
107
  requestData(offset = 0) {
80
108
  this.offset = offset;
81
109
 
@@ -133,30 +161,35 @@ export class Table extends MosaicClient {
133
161
  if (!this.pending) {
134
162
  // data is not from an internal request, so reset table
135
163
  this.loaded = false;
164
+ this.data = [];
136
165
  this.body.replaceChildren();
166
+ this.offset = 0;
137
167
  }
138
- this.data = data;
168
+ this.data.push(toDataColumns(data));
139
169
  return this;
140
170
  }
141
171
 
142
172
  update() {
143
173
  const { body, formats, data, schema, limit } = this;
144
174
  const nf = schema.length;
175
+ const n = data.length - 1;
176
+ const rowCount = limit * n;
145
177
 
146
- let count = 0;
147
- for (const row of data) {
148
- ++count;
178
+ const { numRows, columns } = data[n];
179
+ const cols = schema.map(s => columns[s.column]);
180
+ for (let i = 0; i < numRows; ++i) {
149
181
  const tr = document.createElement('tr');
150
- for (let i = 0; i < nf; ++i) {
151
- const value = row[schema[i].column];
182
+ Object.assign(tr, { __row__: rowCount + i });
183
+ for (let j = 0; j < nf; ++j) {
184
+ const value = cols[j][i];
152
185
  const td = document.createElement('td');
153
- td.innerText = value == null ? '' : formats[i](value);
186
+ td.innerText = value == null ? '' : formats[j](value);
154
187
  tr.appendChild(td);
155
188
  }
156
189
  body.appendChild(tr);
157
190
  }
158
191
 
159
- if (count < limit) {
192
+ if (numRows < limit) {
160
193
  // data table has been fully loaded
161
194
  this.loaded = true;
162
195
  }
@@ -190,6 +223,16 @@ export class Table extends MosaicClient {
190
223
  }
191
224
  }
192
225
 
226
+ /**
227
+ * Resolve a table row number from a table cell element.
228
+ * @param {any} element An HTML element.
229
+ * @returns {number} The resolved row, or -1 if not a row.
230
+ */
231
+ function resolveRow(element) {
232
+ const p = element.parentElement;
233
+ return Object.hasOwn(p, '__row__') ? +p.__row__ : -1;
234
+ }
235
+
193
236
  function formatof(base = {}, schema, locale) {
194
237
  return schema.map(({ column, type }) => {
195
238
  if (column in base) {