@uwdata/mosaic-inputs 0.7.1 → 0.9.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/dist/mosaic-inputs.js +2073 -1533
- package/dist/mosaic-inputs.min.js +5 -5
- package/package.json +5 -5
- package/src/Menu.js +68 -19
- package/src/Search.js +33 -13
- package/src/Slider.js +93 -32
- package/src/Table.js +5 -1
- package/src/util/format.js +5 -2
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@uwdata/mosaic-inputs",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.0",
|
|
4
4
|
"description": "Mosaic input components.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"inputs",
|
|
@@ -20,14 +20,14 @@
|
|
|
20
20
|
"scripts": {
|
|
21
21
|
"prebuild": "rimraf dist && mkdir dist",
|
|
22
22
|
"build": "node ../../esbuild.js mosaic-inputs",
|
|
23
|
-
"lint": "eslint src test
|
|
23
|
+
"lint": "eslint src test",
|
|
24
24
|
"test": "mocha 'test/**/*-test.js'",
|
|
25
25
|
"prepublishOnly": "npm run test && npm run lint && npm run build"
|
|
26
26
|
},
|
|
27
27
|
"dependencies": {
|
|
28
|
-
"@uwdata/mosaic-core": "^0.
|
|
29
|
-
"@uwdata/mosaic-sql": "^0.
|
|
28
|
+
"@uwdata/mosaic-core": "^0.9.0",
|
|
29
|
+
"@uwdata/mosaic-sql": "^0.9.0",
|
|
30
30
|
"isoformat": "^0.2.1"
|
|
31
31
|
},
|
|
32
|
-
"gitHead": "
|
|
32
|
+
"gitHead": "89bb9b0dfa747aed691eaeba35379525a6764c61"
|
|
33
33
|
}
|
package/src/Menu.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { MosaicClient, isParam, isSelection } from '@uwdata/mosaic-core';
|
|
2
|
-
import { Query
|
|
1
|
+
import { MosaicClient, Param, isParam, isSelection, point } from '@uwdata/mosaic-core';
|
|
2
|
+
import { Query } from '@uwdata/mosaic-sql';
|
|
3
3
|
import { input } from './input.js';
|
|
4
4
|
|
|
5
5
|
const isObject = v => {
|
|
@@ -9,6 +9,33 @@ const isObject = v => {
|
|
|
9
9
|
export const menu = options => input(Menu, options);
|
|
10
10
|
|
|
11
11
|
export class Menu extends MosaicClient {
|
|
12
|
+
/**
|
|
13
|
+
* Create a new menu input.
|
|
14
|
+
* @param {object} [options] Options object
|
|
15
|
+
* @param {HTMLElement} [options.element] The parent DOM element in which to
|
|
16
|
+
* place the menu elements. If undefined, a new `div` element is created.
|
|
17
|
+
* @param {Selection} [options.filterBy] A selection to filter the database
|
|
18
|
+
* table indicated by the *from* option.
|
|
19
|
+
* @param {Param} [options.as] The output param or selection. A selection
|
|
20
|
+
* clause is added for the currently selected menu option.
|
|
21
|
+
* @param {string} [options.field] The database column name to use within
|
|
22
|
+
* generated selection clause predicates. Defaults to the *column* option.
|
|
23
|
+
* @param {(any | { value: any, label?: string })[]} [options.options] An
|
|
24
|
+
* array of menu options, as literal values or option objects. Option
|
|
25
|
+
* objects have a `value` property and an optional `label` property. If no
|
|
26
|
+
* label or *format* function is provided, the string-coerced value is used.
|
|
27
|
+
* @param {(value: any) => string} [options.format] A format function that
|
|
28
|
+
* takes an option value as input and generates a string label. The format
|
|
29
|
+
* function is not applied when an explicit label is provided in an option
|
|
30
|
+
* object.
|
|
31
|
+
* @param {*} [options.value] The initial selected menu value.
|
|
32
|
+
* @param {string} [options.from] The name of a database table to use as a data
|
|
33
|
+
* source for this widget. Used in conjunction with the *column* option.
|
|
34
|
+
* @param {string} [options.column] The name of a database column from which
|
|
35
|
+
* to pull menu options. The unique column values are used as menu options.
|
|
36
|
+
* Used in conjunction with the *from* option.
|
|
37
|
+
* @param {string} [options.label] A text label for this input.
|
|
38
|
+
*/
|
|
12
39
|
constructor({
|
|
13
40
|
element,
|
|
14
41
|
filterBy,
|
|
@@ -18,36 +45,52 @@ export class Menu extends MosaicClient {
|
|
|
18
45
|
format = x => x, // TODO
|
|
19
46
|
options,
|
|
20
47
|
value,
|
|
48
|
+
field = column,
|
|
21
49
|
as
|
|
22
50
|
} = {}) {
|
|
23
51
|
super(filterBy);
|
|
24
52
|
this.from = from;
|
|
25
53
|
this.column = column;
|
|
26
|
-
this.selection = as;
|
|
27
54
|
this.format = format;
|
|
55
|
+
this.field = field;
|
|
56
|
+
const selection = this.selection = as;
|
|
28
57
|
|
|
29
58
|
this.element = element ?? document.createElement('div');
|
|
30
59
|
this.element.setAttribute('class', 'input');
|
|
31
|
-
this.element
|
|
60
|
+
Object.defineProperty(this.element, 'value', { value: this });
|
|
32
61
|
|
|
33
62
|
const lab = document.createElement('label');
|
|
34
63
|
lab.innerText = label || column;
|
|
35
64
|
this.element.appendChild(lab);
|
|
36
65
|
|
|
37
66
|
this.select = document.createElement('select');
|
|
67
|
+
this.element.appendChild(this.select);
|
|
68
|
+
|
|
69
|
+
// if provided, populate menu options
|
|
38
70
|
if (options) {
|
|
39
71
|
this.data = options.map(value => isObject(value) ? value : { value });
|
|
72
|
+
this.selectedValue(value ?? '');
|
|
40
73
|
this.update();
|
|
41
74
|
}
|
|
42
|
-
value = value ?? this.selection?.value ?? this.data?.[0]?.value;
|
|
43
|
-
if (this.selection?.value === undefined) this.publish(value);
|
|
44
|
-
this.element.appendChild(this.select);
|
|
45
75
|
|
|
46
|
-
|
|
76
|
+
// initialize selection or param bindings
|
|
77
|
+
if (selection) {
|
|
78
|
+
const isParam = !isSelection(selection);
|
|
79
|
+
|
|
80
|
+
// publish any initial menu value to the selection/param
|
|
81
|
+
// later updates propagate this back to the menu element
|
|
82
|
+
// do not publish if using a param that already has a value
|
|
83
|
+
if (value != null && (!isParam || selection.value === undefined)) {
|
|
84
|
+
this.publish(value);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// publish selected value upon menu change
|
|
47
88
|
this.select.addEventListener('input', () => {
|
|
48
89
|
this.publish(this.selectedValue() ?? null);
|
|
49
90
|
});
|
|
50
|
-
|
|
91
|
+
|
|
92
|
+
// if bound to a scalar param, respond to value updates
|
|
93
|
+
if (isParam) {
|
|
51
94
|
this.selection.addEventListener('value', value => {
|
|
52
95
|
if (value !== this.select.value) {
|
|
53
96
|
this.selectedValue(value);
|
|
@@ -76,14 +119,11 @@ export class Menu extends MosaicClient {
|
|
|
76
119
|
}
|
|
77
120
|
|
|
78
121
|
publish(value) {
|
|
79
|
-
const { selection,
|
|
122
|
+
const { selection, field } = this;
|
|
80
123
|
if (isSelection(selection)) {
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
value,
|
|
85
|
-
predicate: (value !== '' && value !== undefined) ? eq(column, literal(value)) : null
|
|
86
|
-
});
|
|
124
|
+
if (value === '') value = undefined; // 'All' option
|
|
125
|
+
const clause = point(field, value, { source: this });
|
|
126
|
+
selection.update(clause);
|
|
87
127
|
} else if (isParam(selection)) {
|
|
88
128
|
selection.update(value);
|
|
89
129
|
}
|
|
@@ -101,12 +141,15 @@ export class Menu extends MosaicClient {
|
|
|
101
141
|
}
|
|
102
142
|
|
|
103
143
|
queryResult(data) {
|
|
144
|
+
// column option values, with an inserted 'All' value
|
|
104
145
|
this.data = [{ value: '', label: 'All' }, ...data];
|
|
105
146
|
return this;
|
|
106
147
|
}
|
|
107
148
|
|
|
108
149
|
update() {
|
|
109
|
-
const { data, format, select } = this;
|
|
150
|
+
const { data, format, select, selection } = this;
|
|
151
|
+
|
|
152
|
+
// generate menu item options
|
|
110
153
|
select.replaceChildren();
|
|
111
154
|
for (const { value, label } of data) {
|
|
112
155
|
const opt = document.createElement('option');
|
|
@@ -114,9 +157,15 @@ export class Menu extends MosaicClient {
|
|
|
114
157
|
opt.innerText = label ?? format(value);
|
|
115
158
|
this.select.appendChild(opt);
|
|
116
159
|
}
|
|
117
|
-
|
|
118
|
-
|
|
160
|
+
|
|
161
|
+
// update menu value based on param/selection
|
|
162
|
+
if (selection) {
|
|
163
|
+
const value = isSelection(selection)
|
|
164
|
+
? selection.valueFor(this)
|
|
165
|
+
: selection.value;
|
|
166
|
+
this.selectedValue(value ?? '');
|
|
119
167
|
}
|
|
168
|
+
|
|
120
169
|
return this;
|
|
121
170
|
}
|
|
122
171
|
}
|
package/src/Search.js
CHANGED
|
@@ -1,15 +1,37 @@
|
|
|
1
|
-
import { MosaicClient, isParam, isSelection } from '@uwdata/mosaic-core';
|
|
2
|
-
import {
|
|
3
|
-
Query, regexp_matches, contains, prefix, suffix, literal
|
|
4
|
-
} from '@uwdata/mosaic-sql';
|
|
1
|
+
import { MosaicClient, Param, isParam, isSelection, match } from '@uwdata/mosaic-core';
|
|
2
|
+
import { Query } from '@uwdata/mosaic-sql';
|
|
5
3
|
import { input } from './input.js';
|
|
6
4
|
|
|
7
|
-
const FUNCTIONS = { contains, prefix, suffix, regexp: regexp_matches };
|
|
8
5
|
let _id = 0;
|
|
9
6
|
|
|
10
7
|
export const search = options => input(Search, options);
|
|
11
8
|
|
|
12
9
|
export class Search extends MosaicClient {
|
|
10
|
+
/**
|
|
11
|
+
* Create a new text search input.
|
|
12
|
+
* @param {object} [options] Options object
|
|
13
|
+
* @param {HTMLElement} [options.element] The parent DOM element in which to
|
|
14
|
+
* place the search elements. If undefined, a new `div` element is created.
|
|
15
|
+
* @param {Selection} [options.filterBy] A selection to filter the database
|
|
16
|
+
* table indicated by the *from* option.
|
|
17
|
+
* @param {Param} [options.as] The output param or selection. A selection
|
|
18
|
+
* clause is added based on the current text search query.
|
|
19
|
+
* @param {string} [options.field] The database column name to use within
|
|
20
|
+
* generated selection clause predicates. Defaults to the *column* option.
|
|
21
|
+
* @param {'contains' | 'prefix' | 'suffix' | 'regexp'} [options.type] The
|
|
22
|
+
* type of text search query to perform. One of:
|
|
23
|
+
* - `"contains"` (default): the query string may appear anywhere in the text
|
|
24
|
+
* - `"prefix"`: the query string must appear at the start of the text
|
|
25
|
+
* - `"suffix"`: the query string must appear at the end of the text
|
|
26
|
+
* - `"regexp"`: the query string is a regular expression the text must match
|
|
27
|
+
* @param {string} [options.from] The name of a database table to use as an
|
|
28
|
+
* autocomplete data source for this widget. Used in conjunction with the
|
|
29
|
+
* *column* option.
|
|
30
|
+
* @param {string} [options.column] The name of a database column from which
|
|
31
|
+
* to pull valid search results. The unique column values are used as search
|
|
32
|
+
* autocomplete values. Used in conjunction with the *from* option.
|
|
33
|
+
* @param {string} [options.label] A text label for this input.
|
|
34
|
+
*/
|
|
13
35
|
constructor({
|
|
14
36
|
element,
|
|
15
37
|
filterBy,
|
|
@@ -17,6 +39,7 @@ export class Search extends MosaicClient {
|
|
|
17
39
|
column,
|
|
18
40
|
label,
|
|
19
41
|
type = 'contains',
|
|
42
|
+
field = column,
|
|
20
43
|
as
|
|
21
44
|
} = {}) {
|
|
22
45
|
super(filterBy);
|
|
@@ -25,10 +48,11 @@ export class Search extends MosaicClient {
|
|
|
25
48
|
this.from = from;
|
|
26
49
|
this.column = column;
|
|
27
50
|
this.selection = as;
|
|
51
|
+
this.field = field;
|
|
28
52
|
|
|
29
53
|
this.element = element ?? document.createElement('div');
|
|
30
54
|
this.element.setAttribute('class', 'input');
|
|
31
|
-
this.element
|
|
55
|
+
Object.defineProperty(this.element, 'value', { value: this });
|
|
32
56
|
|
|
33
57
|
if (label) {
|
|
34
58
|
const lab = document.createElement('label');
|
|
@@ -62,14 +86,10 @@ export class Search extends MosaicClient {
|
|
|
62
86
|
}
|
|
63
87
|
|
|
64
88
|
publish(value) {
|
|
65
|
-
const { selection,
|
|
89
|
+
const { selection, field, type } = this;
|
|
66
90
|
if (isSelection(selection)) {
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
schema: { type },
|
|
70
|
-
value,
|
|
71
|
-
predicate: value ? FUNCTIONS[type](column, literal(value)) : null
|
|
72
|
-
});
|
|
91
|
+
const clause = match(field, value, { source: this, method: type });
|
|
92
|
+
selection.update(clause);
|
|
73
93
|
} else if (isParam(selection)) {
|
|
74
94
|
selection.update(value);
|
|
75
95
|
}
|
package/src/Slider.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { MosaicClient, isParam, isSelection } from '@uwdata/mosaic-core';
|
|
2
|
-
import { Query,
|
|
1
|
+
import { MosaicClient, Param, interval, isParam, isSelection, point } from '@uwdata/mosaic-core';
|
|
2
|
+
import { Query, max, min } from '@uwdata/mosaic-sql';
|
|
3
3
|
import { input } from './input.js';
|
|
4
4
|
|
|
5
5
|
let _id = 0;
|
|
@@ -7,6 +7,36 @@ let _id = 0;
|
|
|
7
7
|
export const slider = options => input(Slider, options);
|
|
8
8
|
|
|
9
9
|
export class Slider extends MosaicClient {
|
|
10
|
+
/**
|
|
11
|
+
* Create a new slider input.
|
|
12
|
+
* @param {object} [options] Options object
|
|
13
|
+
* @param {HTMLElement} [options.element] The parent DOM element in which to
|
|
14
|
+
* place the slider elements. If undefined, a new `div` element is created.
|
|
15
|
+
* @param {Selection} [options.filterBy] A selection to filter the database
|
|
16
|
+
* table indicated by the *from* option.
|
|
17
|
+
* @param {Param} [options.as] The output param or selection. A selection
|
|
18
|
+
* clause is added based on the currently selected slider option.
|
|
19
|
+
* @param {string} [options.field] The database column name to use within
|
|
20
|
+
* generated selection clause predicates. Defaults to the *column* option.
|
|
21
|
+
* @param {'point' | 'interval'} [options.select] The type of selection clause
|
|
22
|
+
* predicate to generate if the **as** option is a Selection. If `'point'`
|
|
23
|
+
* (the default), the selection predicate is an equality check for the slider
|
|
24
|
+
* value. If `'interval'`, the predicate checks an interval from the minimum
|
|
25
|
+
* to the current slider value.
|
|
26
|
+
* @param {number} [options.min] The minimum slider value.
|
|
27
|
+
* @param {number} [options.max] The maximum slider value.
|
|
28
|
+
* @param {number} [options.step] The slider step, the amount to increment
|
|
29
|
+
* between consecutive values.
|
|
30
|
+
* @param {number} [options.value] The initial slider value.
|
|
31
|
+
* @param {string} [options.from] The name of a database table to use as a data
|
|
32
|
+
* source for this widget. Used in conjunction with the *column* option.
|
|
33
|
+
* The minimum and maximum values of the column determine the slider range.
|
|
34
|
+
* @param {string} [options.column] The name of a database column whose values
|
|
35
|
+
* determine the slider range. Used in conjunction with the *from* option.
|
|
36
|
+
* The minimum and maximum values of the column determine the slider range.
|
|
37
|
+
* @param {string} [options.label] A text label for this input.
|
|
38
|
+
* @param {number} [options.width] The width of the slider in screen pixels.
|
|
39
|
+
*/
|
|
10
40
|
constructor({
|
|
11
41
|
element,
|
|
12
42
|
filterBy,
|
|
@@ -18,6 +48,8 @@ export class Slider extends MosaicClient {
|
|
|
18
48
|
column,
|
|
19
49
|
label = column,
|
|
20
50
|
value = as?.value,
|
|
51
|
+
select = 'point',
|
|
52
|
+
field = column,
|
|
21
53
|
width
|
|
22
54
|
} = {}) {
|
|
23
55
|
super(filterBy);
|
|
@@ -25,45 +57,59 @@ export class Slider extends MosaicClient {
|
|
|
25
57
|
this.from = from;
|
|
26
58
|
this.column = column || 'value';
|
|
27
59
|
this.selection = as;
|
|
60
|
+
this.selectionType = select;
|
|
61
|
+
this.field = field;
|
|
28
62
|
this.min = min;
|
|
29
63
|
this.max = max;
|
|
30
64
|
this.step = step;
|
|
31
65
|
|
|
32
66
|
this.element = element || document.createElement('div');
|
|
33
67
|
this.element.setAttribute('class', 'input');
|
|
34
|
-
this.element
|
|
68
|
+
Object.defineProperty(this.element, 'value', { value: this });
|
|
35
69
|
|
|
36
70
|
if (label) {
|
|
37
|
-
const
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
this.element.appendChild(
|
|
71
|
+
const desc = document.createElement('label');
|
|
72
|
+
desc.setAttribute('for', this.id);
|
|
73
|
+
desc.innerText = label;
|
|
74
|
+
this.element.appendChild(desc);
|
|
41
75
|
}
|
|
42
76
|
|
|
43
77
|
this.slider = document.createElement('input');
|
|
44
78
|
this.slider.setAttribute('id', this.id);
|
|
45
79
|
this.slider.setAttribute('type', 'range');
|
|
46
80
|
if (width != null) this.slider.style.width = `${+width}px`;
|
|
47
|
-
if (min != null) this.slider.setAttribute('min', min);
|
|
48
|
-
if (max != null) this.slider.setAttribute('max', max);
|
|
49
|
-
if (step != null) this.slider.setAttribute('step', step);
|
|
81
|
+
if (min != null) this.slider.setAttribute('min', `${min}`);
|
|
82
|
+
if (max != null) this.slider.setAttribute('max', `${max}`);
|
|
83
|
+
if (step != null) this.slider.setAttribute('step', `${step}`);
|
|
84
|
+
this.element.appendChild(this.slider);
|
|
85
|
+
|
|
86
|
+
this.curval = document.createElement('label');
|
|
87
|
+
this.curval.setAttribute('for', this.id);
|
|
88
|
+
this.curval.setAttribute('class', 'value');
|
|
89
|
+
this.element.appendChild(this.curval);
|
|
90
|
+
|
|
91
|
+
// handle initial value
|
|
50
92
|
if (value != null) {
|
|
51
|
-
this.slider.setAttribute('value', value);
|
|
93
|
+
this.slider.setAttribute('value', `${value}`);
|
|
52
94
|
if (this.selection?.value === undefined) this.publish(value);
|
|
53
95
|
}
|
|
54
|
-
this.
|
|
96
|
+
this.curval.innerText = this.slider.value;
|
|
97
|
+
|
|
98
|
+
// respond to slider input
|
|
99
|
+
this.slider.addEventListener('input', () => {
|
|
100
|
+
const { value } = this.slider;
|
|
101
|
+
this.curval.innerText = value;
|
|
102
|
+
if (this.selection) this.publish(+value);
|
|
103
|
+
});
|
|
55
104
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
105
|
+
// track param updates
|
|
106
|
+
if (this.selection && !isSelection(this.selection)) {
|
|
107
|
+
this.selection.addEventListener('value', value => {
|
|
108
|
+
if (value !== +this.slider.value) {
|
|
109
|
+
this.slider.value = value;
|
|
110
|
+
this.curval.innerText = value;
|
|
111
|
+
}
|
|
59
112
|
});
|
|
60
|
-
if (!isSelection(this.selection)) {
|
|
61
|
-
this.selection.addEventListener('value', value => {
|
|
62
|
-
if (value !== +this.slider.value) {
|
|
63
|
-
this.slider.value = value;
|
|
64
|
-
}
|
|
65
|
-
});
|
|
66
|
-
}
|
|
67
113
|
}
|
|
68
114
|
}
|
|
69
115
|
|
|
@@ -78,21 +124,36 @@ export class Slider extends MosaicClient {
|
|
|
78
124
|
|
|
79
125
|
queryResult(data) {
|
|
80
126
|
const { min, max } = Array.from(data)[0];
|
|
81
|
-
if (this.min == null)
|
|
82
|
-
|
|
83
|
-
|
|
127
|
+
if (this.min == null) {
|
|
128
|
+
this.min = min;
|
|
129
|
+
this.slider.setAttribute('min', `${min}`);
|
|
130
|
+
}
|
|
131
|
+
if (this.max == null) {
|
|
132
|
+
this.max = max;
|
|
133
|
+
this.slider.setAttribute('max', `${max}`);
|
|
134
|
+
}
|
|
135
|
+
if (this.step == null) {
|
|
136
|
+
this.step = (max - min) / 500;
|
|
137
|
+
this.slider.setAttribute('step', `${this.step}`);
|
|
138
|
+
}
|
|
84
139
|
return this;
|
|
85
140
|
}
|
|
86
141
|
|
|
87
142
|
publish(value) {
|
|
88
|
-
const {
|
|
143
|
+
const { field, selectionType, selection } = this;
|
|
89
144
|
if (isSelection(selection)) {
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
145
|
+
if (selectionType === 'interval') {
|
|
146
|
+
/** @type {[number, number]} */
|
|
147
|
+
const domain = [this.min ?? 0, value];
|
|
148
|
+
selection.update(interval(field, domain, {
|
|
149
|
+
source: this,
|
|
150
|
+
bin: 'ceil',
|
|
151
|
+
scale: { type: 'identity', domain },
|
|
152
|
+
pixelSize: this.step
|
|
153
|
+
}));
|
|
154
|
+
} else {
|
|
155
|
+
selection.update(point(field, value, { source: this }));
|
|
156
|
+
}
|
|
96
157
|
} else if (isParam(this.selection)) {
|
|
97
158
|
selection.update(value);
|
|
98
159
|
}
|
package/src/Table.js
CHANGED
|
@@ -8,6 +8,10 @@ let _id = -1;
|
|
|
8
8
|
export const table = options => input(Table, options);
|
|
9
9
|
|
|
10
10
|
export class Table extends MosaicClient {
|
|
11
|
+
/**
|
|
12
|
+
* Create a new Table instance.
|
|
13
|
+
* @param {object} options Options object
|
|
14
|
+
*/
|
|
11
15
|
constructor({
|
|
12
16
|
element,
|
|
13
17
|
filterBy,
|
|
@@ -38,7 +42,7 @@ export class Table extends MosaicClient {
|
|
|
38
42
|
|
|
39
43
|
this.element = element || document.createElement('div');
|
|
40
44
|
this.element.setAttribute('id', this.id);
|
|
41
|
-
this.element
|
|
45
|
+
Object.defineProperty(this.element, 'value', { value: this });
|
|
42
46
|
if (typeof width === 'number') this.element.style.width = `${width}px`;
|
|
43
47
|
if (maxWidth) this.element.style.maxWidth = `${maxWidth}px`;
|
|
44
48
|
this.element.style.maxHeight = `${height}px`;
|
package/src/util/format.js
CHANGED
|
@@ -42,6 +42,9 @@ export function formatDate(date) {
|
|
|
42
42
|
|
|
43
43
|
// Memoize the last-returned locale.
|
|
44
44
|
export function localize(f) {
|
|
45
|
-
let key =
|
|
46
|
-
|
|
45
|
+
let key = null;
|
|
46
|
+
let value;
|
|
47
|
+
return (locale = 'en') => locale === key
|
|
48
|
+
? value
|
|
49
|
+
: (value = f(key = locale));
|
|
47
50
|
}
|