@uwdata/mosaic-inputs
Advanced tools
Comparing version 0.8.0 to 0.9.0
{ | ||
"name": "@uwdata/mosaic-inputs", | ||
"version": "0.8.0", | ||
"version": "0.9.0", | ||
"description": "Mosaic input components.", | ||
@@ -28,7 +28,7 @@ "keywords": [ | ||
"dependencies": { | ||
"@uwdata/mosaic-core": "^0.8.0", | ||
"@uwdata/mosaic-sql": "^0.8.0", | ||
"@uwdata/mosaic-core": "^0.9.0", | ||
"@uwdata/mosaic-sql": "^0.9.0", | ||
"isoformat": "^0.2.1" | ||
}, | ||
"gitHead": "a24b4c9f7dfa1c38c6af96ec17e075326c1af9b0" | ||
"gitHead": "89bb9b0dfa747aed691eaeba35379525a6764c61" | ||
} |
@@ -1,3 +0,3 @@ | ||
import { MosaicClient, isParam, isSelection } from '@uwdata/mosaic-core'; | ||
import { Query, eq, literal } from '@uwdata/mosaic-sql'; | ||
import { MosaicClient, Param, isParam, isSelection, point } from '@uwdata/mosaic-core'; | ||
import { Query } from '@uwdata/mosaic-sql'; | ||
import { input } from './input.js'; | ||
@@ -13,4 +13,27 @@ | ||
/** | ||
* Create a new Menu instance. | ||
* @param {object} options Options object | ||
* Create a new menu input. | ||
* @param {object} [options] Options object | ||
* @param {HTMLElement} [options.element] The parent DOM element in which to | ||
* place the menu elements. If undefined, a new `div` element is created. | ||
* @param {Selection} [options.filterBy] A selection to filter the database | ||
* table indicated by the *from* option. | ||
* @param {Param} [options.as] The output param or selection. A selection | ||
* clause is added for the currently selected menu option. | ||
* @param {string} [options.field] The database column name to use within | ||
* generated selection clause predicates. Defaults to the *column* option. | ||
* @param {(any | { value: any, label?: string })[]} [options.options] An | ||
* array of menu options, as literal values or option objects. Option | ||
* objects have a `value` property and an optional `label` property. If no | ||
* label or *format* function is provided, the string-coerced value is used. | ||
* @param {(value: any) => string} [options.format] A format function that | ||
* takes an option value as input and generates a string label. The format | ||
* function is not applied when an explicit label is provided in an option | ||
* object. | ||
* @param {*} [options.value] The initial selected menu value. | ||
* @param {string} [options.from] The name of a database table to use as a data | ||
* source for this widget. Used in conjunction with the *column* option. | ||
* @param {string} [options.column] The name of a database column from which | ||
* to pull menu options. The unique column values are used as menu options. | ||
* Used in conjunction with the *from* option. | ||
* @param {string} [options.label] A text label for this input. | ||
*/ | ||
@@ -26,2 +49,3 @@ constructor({ | ||
value, | ||
field = column, | ||
as | ||
@@ -32,8 +56,9 @@ } = {}) { | ||
this.column = column; | ||
this.selection = as; | ||
this.format = format; | ||
this.field = field; | ||
const selection = this.selection = as; | ||
this.element = element ?? document.createElement('div'); | ||
this.element.setAttribute('class', 'input'); | ||
this.element.value = this; | ||
Object.defineProperty(this.element, 'value', { value: this }); | ||
@@ -45,15 +70,29 @@ const lab = document.createElement('label'); | ||
this.select = document.createElement('select'); | ||
this.element.appendChild(this.select); | ||
// if provided, populate menu options | ||
if (options) { | ||
this.data = options.map(value => isObject(value) ? value : { value }); | ||
this.selectedValue(value ?? ''); | ||
this.update(); | ||
} | ||
value = value ?? this.selection?.value ?? this.data?.[0]?.value; | ||
if (this.selection?.value === undefined) this.publish(value); | ||
this.element.appendChild(this.select); | ||
if (this.selection) { | ||
// initialize selection or param bindings | ||
if (selection) { | ||
const isParam = !isSelection(selection); | ||
// publish any initial menu value to the selection/param | ||
// later updates propagate this back to the menu element | ||
// do not publish if using a param that already has a value | ||
if (value != null && (!isParam || selection.value === undefined)) { | ||
this.publish(value); | ||
} | ||
// publish selected value upon menu change | ||
this.select.addEventListener('input', () => { | ||
this.publish(this.selectedValue() ?? null); | ||
}); | ||
if (!isSelection(this.selection)) { | ||
// if bound to a scalar param, respond to value updates | ||
if (isParam) { | ||
this.selection.addEventListener('value', value => { | ||
@@ -87,10 +126,7 @@ if (value !== this.select.value) { | ||
publish(value) { | ||
const { selection, column } = this; | ||
const { selection, field } = this; | ||
if (isSelection(selection)) { | ||
selection.update({ | ||
source: this, | ||
schema: { type: 'point' }, | ||
value, | ||
predicate: (value !== '' && value !== undefined) ? eq(column, literal(value)) : null | ||
}); | ||
if (value === '') value = undefined; // 'All' option | ||
const clause = point(field, value, { source: this }); | ||
selection.update(clause); | ||
} else if (isParam(selection)) { | ||
@@ -113,2 +149,3 @@ selection.update(value); | ||
queryResult(data) { | ||
// column option values, with an inserted 'All' value | ||
this.data = [{ value: '', label: 'All' }, ...data]; | ||
@@ -119,3 +156,5 @@ return this; | ||
update() { | ||
const { data, format, select } = this; | ||
const { data, format, select, selection } = this; | ||
// generate menu item options | ||
select.replaceChildren(); | ||
@@ -128,7 +167,13 @@ for (const { value, label } of data) { | ||
} | ||
if (this.selection) { | ||
this.selectedValue(this.selection?.value ?? ''); | ||
// update menu value based on param/selection | ||
if (selection) { | ||
const value = isSelection(selection) | ||
? selection.valueFor(this) | ||
: selection.value; | ||
this.selectedValue(value ?? ''); | ||
} | ||
return this; | ||
} | ||
} |
@@ -1,8 +0,5 @@ | ||
import { MosaicClient, isParam, isSelection } from '@uwdata/mosaic-core'; | ||
import { | ||
Query, regexp_matches, contains, prefix, suffix, literal | ||
} from '@uwdata/mosaic-sql'; | ||
import { MosaicClient, Param, isParam, isSelection, match } from '@uwdata/mosaic-core'; | ||
import { Query } from '@uwdata/mosaic-sql'; | ||
import { input } from './input.js'; | ||
const FUNCTIONS = { contains, prefix, suffix, regexp: regexp_matches }; | ||
let _id = 0; | ||
@@ -14,4 +11,25 @@ | ||
/** | ||
* Create a new Search instance. | ||
* @param {object} options Options object | ||
* Create a new text search input. | ||
* @param {object} [options] Options object | ||
* @param {HTMLElement} [options.element] The parent DOM element in which to | ||
* place the search elements. If undefined, a new `div` element is created. | ||
* @param {Selection} [options.filterBy] A selection to filter the database | ||
* table indicated by the *from* option. | ||
* @param {Param} [options.as] The output param or selection. A selection | ||
* clause is added based on the current text search query. | ||
* @param {string} [options.field] The database column name to use within | ||
* generated selection clause predicates. Defaults to the *column* option. | ||
* @param {'contains' | 'prefix' | 'suffix' | 'regexp'} [options.type] The | ||
* type of text search query to perform. One of: | ||
* - `"contains"` (default): the query string may appear anywhere in the text | ||
* - `"prefix"`: the query string must appear at the start of the text | ||
* - `"suffix"`: the query string must appear at the end of the text | ||
* - `"regexp"`: the query string is a regular expression the text must match | ||
* @param {string} [options.from] The name of a database table to use as an | ||
* autocomplete data source for this widget. Used in conjunction with the | ||
* *column* option. | ||
* @param {string} [options.column] The name of a database column from which | ||
* to pull valid search results. The unique column values are used as search | ||
* autocomplete values. Used in conjunction with the *from* option. | ||
* @param {string} [options.label] A text label for this input. | ||
*/ | ||
@@ -25,2 +43,3 @@ constructor({ | ||
type = 'contains', | ||
field = column, | ||
as | ||
@@ -34,6 +53,7 @@ } = {}) { | ||
this.selection = as; | ||
this.field = field; | ||
this.element = element ?? document.createElement('div'); | ||
this.element.setAttribute('class', 'input'); | ||
this.element.value = this; | ||
Object.defineProperty(this.element, 'value', { value: this }); | ||
@@ -72,10 +92,6 @@ if (label) { | ||
publish(value) { | ||
const { selection, column, type } = this; | ||
const { selection, field, type } = this; | ||
if (isSelection(selection)) { | ||
selection.update({ | ||
source: this, | ||
schema: { type }, | ||
value, | ||
predicate: value ? FUNCTIONS[type](column, literal(value)) : null | ||
}); | ||
const clause = match(field, value, { source: this, method: type }); | ||
selection.update(clause); | ||
} else if (isParam(selection)) { | ||
@@ -82,0 +98,0 @@ selection.update(value); |
@@ -1,3 +0,3 @@ | ||
import { MosaicClient, isParam, isSelection } from '@uwdata/mosaic-core'; | ||
import { Query, eq, literal, max, min } from '@uwdata/mosaic-sql'; | ||
import { MosaicClient, Param, interval, isParam, isSelection, point } from '@uwdata/mosaic-core'; | ||
import { Query, max, min } from '@uwdata/mosaic-sql'; | ||
import { input } from './input.js'; | ||
@@ -11,4 +11,30 @@ | ||
/** | ||
* Create a new Slider instance. | ||
* @param {object} options Options object | ||
* Create a new slider input. | ||
* @param {object} [options] Options object | ||
* @param {HTMLElement} [options.element] The parent DOM element in which to | ||
* place the slider elements. If undefined, a new `div` element is created. | ||
* @param {Selection} [options.filterBy] A selection to filter the database | ||
* table indicated by the *from* option. | ||
* @param {Param} [options.as] The output param or selection. A selection | ||
* clause is added based on the currently selected slider option. | ||
* @param {string} [options.field] The database column name to use within | ||
* generated selection clause predicates. Defaults to the *column* option. | ||
* @param {'point' | 'interval'} [options.select] The type of selection clause | ||
* predicate to generate if the **as** option is a Selection. If `'point'` | ||
* (the default), the selection predicate is an equality check for the slider | ||
* value. If `'interval'`, the predicate checks an interval from the minimum | ||
* to the current slider value. | ||
* @param {number} [options.min] The minimum slider value. | ||
* @param {number} [options.max] The maximum slider value. | ||
* @param {number} [options.step] The slider step, the amount to increment | ||
* between consecutive values. | ||
* @param {number} [options.value] The initial slider value. | ||
* @param {string} [options.from] The name of a database table to use as a data | ||
* source for this widget. Used in conjunction with the *column* option. | ||
* The minimum and maximum values of the column determine the slider range. | ||
* @param {string} [options.column] The name of a database column whose values | ||
* determine the slider range. Used in conjunction with the *from* option. | ||
* The minimum and maximum values of the column determine the slider range. | ||
* @param {string} [options.label] A text label for this input. | ||
* @param {number} [options.width] The width of the slider in screen pixels. | ||
*/ | ||
@@ -26,2 +52,4 @@ constructor({ | ||
value = as?.value, | ||
select = 'point', | ||
field = column, | ||
width | ||
@@ -34,2 +62,4 @@ } = {}) { | ||
this.selection = as; | ||
this.selectionType = select; | ||
this.field = field; | ||
this.min = min; | ||
@@ -41,9 +71,9 @@ this.max = max; | ||
this.element.setAttribute('class', 'input'); | ||
this.element.value = this; | ||
Object.defineProperty(this.element, 'value', { value: this }); | ||
if (label) { | ||
const lab = document.createElement('label'); | ||
lab.setAttribute('for', this.id); | ||
lab.innerText = label; | ||
this.element.appendChild(lab); | ||
const desc = document.createElement('label'); | ||
desc.setAttribute('for', this.id); | ||
desc.innerText = label; | ||
this.element.appendChild(desc); | ||
} | ||
@@ -55,22 +85,34 @@ | ||
if (width != null) this.slider.style.width = `${+width}px`; | ||
if (min != null) this.slider.setAttribute('min', min); | ||
if (max != null) this.slider.setAttribute('max', max); | ||
if (step != null) this.slider.setAttribute('step', step); | ||
if (min != null) this.slider.setAttribute('min', `${min}`); | ||
if (max != null) this.slider.setAttribute('max', `${max}`); | ||
if (step != null) this.slider.setAttribute('step', `${step}`); | ||
this.element.appendChild(this.slider); | ||
this.curval = document.createElement('label'); | ||
this.curval.setAttribute('for', this.id); | ||
this.curval.setAttribute('class', 'value'); | ||
this.element.appendChild(this.curval); | ||
// handle initial value | ||
if (value != null) { | ||
this.slider.setAttribute('value', value); | ||
this.slider.setAttribute('value', `${value}`); | ||
if (this.selection?.value === undefined) this.publish(value); | ||
} | ||
this.element.appendChild(this.slider); | ||
this.curval.innerText = this.slider.value; | ||
if (this.selection) { | ||
this.slider.addEventListener('input', () => { | ||
this.publish(+this.slider.value); | ||
// respond to slider input | ||
this.slider.addEventListener('input', () => { | ||
const { value } = this.slider; | ||
this.curval.innerText = value; | ||
if (this.selection) this.publish(+value); | ||
}); | ||
// track param updates | ||
if (this.selection && !isSelection(this.selection)) { | ||
this.selection.addEventListener('value', value => { | ||
if (value !== +this.slider.value) { | ||
this.slider.value = value; | ||
this.curval.innerText = value; | ||
} | ||
}); | ||
if (!isSelection(this.selection)) { | ||
this.selection.addEventListener('value', value => { | ||
if (value !== +this.slider.value) { | ||
this.slider.value = value; | ||
} | ||
}); | ||
} | ||
} | ||
@@ -90,5 +132,14 @@ } | ||
const { min, max } = Array.from(data)[0]; | ||
if (this.min == null) this.slider.setAttribute('min', min); | ||
if (this.max == null) this.slider.setAttribute('max', max); | ||
if (this.step == null) this.slider.setAttribute('step', String((max - min) / 500)); | ||
if (this.min == null) { | ||
this.min = min; | ||
this.slider.setAttribute('min', `${min}`); | ||
} | ||
if (this.max == null) { | ||
this.max = max; | ||
this.slider.setAttribute('max', `${max}`); | ||
} | ||
if (this.step == null) { | ||
this.step = (max - min) / 500; | ||
this.slider.setAttribute('step', `${this.step}`); | ||
} | ||
return this; | ||
@@ -98,10 +149,16 @@ } | ||
publish(value) { | ||
const { selection, column } = this; | ||
const { field, selectionType, selection } = this; | ||
if (isSelection(selection)) { | ||
selection.update({ | ||
source: this, | ||
schema: { type: 'point' }, | ||
value, | ||
predicate: eq(column, literal(value)) | ||
}); | ||
if (selectionType === 'interval') { | ||
/** @type {[number, number]} */ | ||
const domain = [this.min ?? 0, value]; | ||
selection.update(interval(field, domain, { | ||
source: this, | ||
bin: 'ceil', | ||
scale: { type: 'identity', domain }, | ||
pixelSize: this.step | ||
})); | ||
} else { | ||
selection.update(point(field, value, { source: this })); | ||
} | ||
} else if (isParam(this.selection)) { | ||
@@ -108,0 +165,0 @@ selection.update(value); |
@@ -45,3 +45,3 @@ import { MosaicClient, coordinator } from '@uwdata/mosaic-core'; | ||
this.element.setAttribute('id', this.id); | ||
this.element.value = this; | ||
Object.defineProperty(this.element, 'value', { value: this }); | ||
if (typeof width === 'number') this.element.style.width = `${width}px`; | ||
@@ -48,0 +48,0 @@ if (maxWidth) this.element.style.maxWidth = `${maxWidth}px`; |
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
780466
16824
+ Added@uwdata/mosaic-core@0.9.0(transitive)
+ Added@uwdata/mosaic-sql@0.9.0(transitive)
- Removed@uwdata/mosaic-core@0.8.0(transitive)
- Removed@uwdata/mosaic-sql@0.8.0(transitive)
Updated@uwdata/mosaic-core@^0.9.0
Updated@uwdata/mosaic-sql@^0.9.0