@aquera/nile-visualization 2.5.0 → 2.7.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.
|
@@ -17,6 +17,99 @@ export interface TreeNode {
|
|
|
17
17
|
expanded?: boolean;
|
|
18
18
|
children?: TreeNode[];
|
|
19
19
|
}
|
|
20
|
+
export interface QueryLanguageConfig {
|
|
21
|
+
/** Enables the query layer at all. Default: false. */
|
|
22
|
+
enabled: boolean;
|
|
23
|
+
/**
|
|
24
|
+
* - `'auto'` → submit emits the raw typed string.
|
|
25
|
+
* - `'strict'` → submit parses the input with filtrex and emits a
|
|
26
|
+
* pure-JSON payload (`{ source }`) plus a sibling
|
|
27
|
+
* `evaluate` function on the event detail. On
|
|
28
|
+
* parse failure, `nile-prompt-parse-error` fires and
|
|
29
|
+
* `nile-prompt-submit` does NOT fire.
|
|
30
|
+
* Default: `'auto'`.
|
|
31
|
+
*/
|
|
32
|
+
mode?: 'auto' | 'strict';
|
|
33
|
+
/** Parser engine. Currently only `'filtrex'` is supported. */
|
|
34
|
+
engine?: 'filtrex';
|
|
35
|
+
/** Optional hint text shown beneath the input describing the syntax. */
|
|
36
|
+
syntaxHint?: string;
|
|
37
|
+
/**
|
|
38
|
+
* Extra functions registered with filtrex on top of the built-in set.
|
|
39
|
+
* Each function receives the resolved argument values from the
|
|
40
|
+
* expression and returns a value usable in further comparisons.
|
|
41
|
+
*
|
|
42
|
+
* Example: `{ daysAgo: (d) => (Date.now() - +new Date(d)) / 86400000 }`
|
|
43
|
+
* Used in expressions like `daysAgo(lastSeen) < 7`.
|
|
44
|
+
*/
|
|
45
|
+
extraFunctions?: Record<string, (...args: any[]) => unknown>;
|
|
46
|
+
/**
|
|
47
|
+
* Optional value-getter for accessing row properties. Filtrex calls
|
|
48
|
+
* this for every identifier reference. Default: dotted property
|
|
49
|
+
* access (`row.foo.bar` works out of the box).
|
|
50
|
+
*
|
|
51
|
+
* Override when your data model uses a Map / class / proxy that
|
|
52
|
+
* standard property access can't read.
|
|
53
|
+
*/
|
|
54
|
+
customProp?: (name: string, get: (key: string) => unknown, obj: unknown) => unknown;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Recursive ESTree-shaped node returned by the AST parser. The shape
|
|
58
|
+
* matches what `jsep` produces — a small, well-known JS-expression AST.
|
|
59
|
+
* Pure JSON: every field is a string, number, boolean, null, or another
|
|
60
|
+
* `QueryNode`, so `JSON.stringify` round-trips cleanly.
|
|
61
|
+
*/
|
|
62
|
+
export type QueryNode = {
|
|
63
|
+
type: 'Identifier';
|
|
64
|
+
name: string;
|
|
65
|
+
} | {
|
|
66
|
+
type: 'Literal';
|
|
67
|
+
value: string | number | boolean | null;
|
|
68
|
+
raw?: string;
|
|
69
|
+
} | {
|
|
70
|
+
type: 'BinaryExpression';
|
|
71
|
+
operator: string;
|
|
72
|
+
left: QueryNode;
|
|
73
|
+
right: QueryNode;
|
|
74
|
+
} | {
|
|
75
|
+
type: 'LogicalExpression';
|
|
76
|
+
operator: string;
|
|
77
|
+
left: QueryNode;
|
|
78
|
+
right: QueryNode;
|
|
79
|
+
} | {
|
|
80
|
+
type: 'UnaryExpression';
|
|
81
|
+
operator: string;
|
|
82
|
+
argument: QueryNode;
|
|
83
|
+
prefix: boolean;
|
|
84
|
+
} | {
|
|
85
|
+
type: 'CallExpression';
|
|
86
|
+
callee: QueryNode;
|
|
87
|
+
arguments: QueryNode[];
|
|
88
|
+
} | {
|
|
89
|
+
type: 'MemberExpression';
|
|
90
|
+
computed: boolean;
|
|
91
|
+
object: QueryNode;
|
|
92
|
+
property: QueryNode;
|
|
93
|
+
} | {
|
|
94
|
+
type: 'ArrayExpression';
|
|
95
|
+
elements: QueryNode[];
|
|
96
|
+
} | {
|
|
97
|
+
type: 'Compound';
|
|
98
|
+
body: QueryNode[];
|
|
99
|
+
};
|
|
100
|
+
export type QueryJson = {
|
|
101
|
+
/** The exact string the user typed, retained for display / serialization. */
|
|
102
|
+
source: string;
|
|
103
|
+
ast: QueryNode;
|
|
104
|
+
};
|
|
105
|
+
export type QuerySubmitDetail = {
|
|
106
|
+
id: string;
|
|
107
|
+
/** JSON-serializable payload — string in auto mode, `QueryJson` in strict. */
|
|
108
|
+
value: string | QueryJson;
|
|
109
|
+
/** Compiled filtrex predicate. Only present in strict mode on success.
|
|
110
|
+
* Use as `data.filter(e.detail.evaluate)`. */
|
|
111
|
+
evaluate?: (row: unknown) => unknown;
|
|
112
|
+
};
|
|
20
113
|
export interface FilterControl {
|
|
21
114
|
id: string;
|
|
22
115
|
label: string;
|
|
@@ -50,28 +143,26 @@ export interface FilterControl {
|
|
|
50
143
|
* Prompt variant only. Inline callback invoked when the user presses
|
|
51
144
|
* Enter inside the prompt input. Called in addition to the
|
|
52
145
|
* `nile-prompt-submit` event.
|
|
53
|
-
*/
|
|
54
|
-
onSubmit?: (value: string, id: string) => void;
|
|
55
|
-
/**
|
|
56
|
-
* Prompt variant only. Colors used for the animated border gradient.
|
|
57
|
-
* Pass any number of CSS color strings (hex / rgb / hsl / named); they
|
|
58
|
-
* are evenly distributed across the gradient by default. To control
|
|
59
|
-
* stops explicitly, include positions in the strings themselves
|
|
60
|
-
* (e.g. `'#2563eb 0%'`, `'#06b6d4 50%'`).
|
|
61
146
|
*
|
|
62
|
-
*
|
|
63
|
-
*
|
|
147
|
+
* - auto mode (default) → `value` is the raw `string` the user typed.
|
|
148
|
+
* - strict mode (success) → `value` is a `QueryJson` (pure-JSON
|
|
149
|
+
* `{ source }`); the compiled predicate is
|
|
150
|
+
* passed as the third argument so callers
|
|
151
|
+
* that want to filter inline can. JSON
|
|
152
|
+
* consumers can ignore that argument.
|
|
153
|
+
* - strict mode (parse fail) → routes through `onParseError` instead;
|
|
154
|
+
* `onSubmit` is NOT called.
|
|
64
155
|
*/
|
|
65
|
-
|
|
156
|
+
onSubmit?: (value: string | QueryJson, id: string, evaluate?: (row: unknown) => unknown) => void;
|
|
66
157
|
/**
|
|
67
|
-
* Prompt variant only.
|
|
68
|
-
*
|
|
158
|
+
* Prompt variant only. Inline callback invoked when `queryLanguage.mode`
|
|
159
|
+
* is `'strict'` and the user-typed expression FAILS to parse on submit.
|
|
160
|
+
* Mirrors the `nile-prompt-parse-error` event.
|
|
69
161
|
*/
|
|
162
|
+
onParseError?: (input: string, error: Error, id: string) => void;
|
|
163
|
+
queryLanguage?: QueryLanguageConfig;
|
|
164
|
+
gradientColors?: string[];
|
|
70
165
|
gradientDirection?: string;
|
|
71
|
-
/**
|
|
72
|
-
* Prompt variant only. Animation duration in milliseconds for one full
|
|
73
|
-
* gradient sweep. Default: 4500.
|
|
74
|
-
*/
|
|
75
166
|
gradientSpeedMs?: number;
|
|
76
167
|
valueB?: string;
|
|
77
168
|
operator?: string;
|
|
@@ -124,6 +215,12 @@ export declare class NileFilterChart extends NileElement {
|
|
|
124
215
|
private _renderComparison;
|
|
125
216
|
private _renderThreshold;
|
|
126
217
|
private _renderPreset;
|
|
218
|
+
private static readonly _builtInFiltrexFns;
|
|
219
|
+
private static _filtrexLoading;
|
|
220
|
+
private static _getCompileExpression;
|
|
221
|
+
private static _jsepLoading;
|
|
222
|
+
private static _getJsep;
|
|
223
|
+
private _handleSubmit;
|
|
127
224
|
private _renderPrompt;
|
|
128
225
|
private _renderControl;
|
|
129
226
|
private _renderGroup;
|
|
@@ -1,9 +1,10 @@
|
|
|
1
|
+
var NileFilterChart_1;
|
|
1
2
|
import { __decorate } from "tslib";
|
|
2
3
|
import { html, nothing } from 'lit';
|
|
3
4
|
import { customElement, property, state } from 'lit/decorators.js';
|
|
4
5
|
import { styles } from './nile-filter-chart.css.js';
|
|
5
6
|
import NileElement from '../internal/nile-element.js';
|
|
6
|
-
let NileFilterChart = class NileFilterChart extends NileElement {
|
|
7
|
+
let NileFilterChart = NileFilterChart_1 = class NileFilterChart extends NileElement {
|
|
7
8
|
constructor() {
|
|
8
9
|
super(...arguments);
|
|
9
10
|
this.config = null;
|
|
@@ -425,21 +426,80 @@ let NileFilterChart = class NileFilterChart extends NileElement {
|
|
|
425
426
|
</button>`)}
|
|
426
427
|
</div>`;
|
|
427
428
|
}
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
429
|
+
static async _getCompileExpression() {
|
|
430
|
+
if (!NileFilterChart_1._filtrexLoading) {
|
|
431
|
+
NileFilterChart_1._filtrexLoading = import('filtrex').then(m => {
|
|
432
|
+
const fn = m.compileExpression
|
|
433
|
+
?? m.default?.compileExpression;
|
|
434
|
+
if (typeof fn !== 'function') {
|
|
435
|
+
throw new Error('filtrex: compileExpression export not found');
|
|
436
|
+
}
|
|
437
|
+
return fn;
|
|
438
|
+
});
|
|
439
|
+
}
|
|
440
|
+
return NileFilterChart_1._filtrexLoading;
|
|
441
|
+
}
|
|
442
|
+
static async _getJsep() {
|
|
443
|
+
if (!NileFilterChart_1._jsepLoading) {
|
|
444
|
+
NileFilterChart_1._jsepLoading = import('jsep').then(m => {
|
|
445
|
+
const candidate = m.default ?? m;
|
|
446
|
+
if (typeof candidate !== 'function') {
|
|
447
|
+
throw new Error('jsep: function export not found');
|
|
448
|
+
}
|
|
449
|
+
const fn = candidate;
|
|
450
|
+
try {
|
|
451
|
+
fn.addBinaryOp?.('and', 2);
|
|
452
|
+
fn.addBinaryOp?.('or', 1);
|
|
453
|
+
fn.addBinaryOp?.('in', 6);
|
|
454
|
+
fn.addUnaryOp?.('not');
|
|
455
|
+
}
|
|
456
|
+
catch {
|
|
457
|
+
}
|
|
458
|
+
return fn;
|
|
459
|
+
});
|
|
460
|
+
}
|
|
461
|
+
return NileFilterChart_1._jsepLoading;
|
|
462
|
+
}
|
|
463
|
+
async _handleSubmit(value, ctrl) {
|
|
464
|
+
const ql = ctrl.queryLanguage;
|
|
465
|
+
if (!ql?.enabled || (ql.mode ?? 'auto') === 'auto') {
|
|
466
|
+
this.emit('nile-prompt-submit', { id: ctrl.id, value });
|
|
467
|
+
if (typeof ctrl.onSubmit === 'function')
|
|
468
|
+
ctrl.onSubmit(value, ctrl.id);
|
|
469
|
+
return;
|
|
470
|
+
}
|
|
471
|
+
try {
|
|
472
|
+
const jsep = await NileFilterChart_1._getJsep();
|
|
473
|
+
const ast = jsep(value);
|
|
474
|
+
const compileExpression = await NileFilterChart_1._getCompileExpression();
|
|
475
|
+
const extraFunctions = {
|
|
476
|
+
...NileFilterChart_1._builtInFiltrexFns,
|
|
477
|
+
...(ql.extraFunctions ?? {}),
|
|
478
|
+
};
|
|
479
|
+
const evaluate = compileExpression(value, {
|
|
480
|
+
extraFunctions,
|
|
481
|
+
...(ql.customProp ? { customProp: ql.customProp } : {}),
|
|
482
|
+
});
|
|
483
|
+
const json = { source: value, ast };
|
|
484
|
+
this.emit('nile-prompt-submit', { id: ctrl.id, value: json, evaluate });
|
|
485
|
+
if (typeof ctrl.onSubmit === 'function')
|
|
486
|
+
ctrl.onSubmit(json, ctrl.id, evaluate);
|
|
487
|
+
}
|
|
488
|
+
catch (err) {
|
|
489
|
+
const error = err instanceof Error ? err : new Error(String(err));
|
|
490
|
+
this.emit('nile-prompt-parse-error', {
|
|
491
|
+
id: ctrl.id,
|
|
492
|
+
input: value,
|
|
493
|
+
error: { message: error.message },
|
|
494
|
+
});
|
|
495
|
+
if (typeof ctrl.onParseError === 'function') {
|
|
496
|
+
ctrl.onParseError(value, error, ctrl.id);
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
}
|
|
436
500
|
_renderPrompt(ctrl) {
|
|
437
501
|
const value = String(this.selectedValues.get(ctrl.id) ?? '');
|
|
438
502
|
const animated = this._promptPlaceholder.get(ctrl.id) ?? '';
|
|
439
|
-
// Build the inline style overrides as CSS custom properties — these
|
|
440
|
-
// are picked up by the .fc-prompt rule via var(--fc-prompt-gradient,
|
|
441
|
-
// <default>) / var(--fc-prompt-gradient-speed, 4.5s). Only emit a
|
|
442
|
-
// declaration when the consumer actually supplied an override.
|
|
443
503
|
const styleParts = [];
|
|
444
504
|
if (ctrl.gradientColors && ctrl.gradientColors.length > 0) {
|
|
445
505
|
const dir = ctrl.gradientDirection ?? '90deg';
|
|
@@ -484,10 +544,7 @@ let NileFilterChart = class NileFilterChart extends NileElement {
|
|
|
484
544
|
@keydown="${(e) => {
|
|
485
545
|
if (e.key === 'Enter') {
|
|
486
546
|
const t = e.target;
|
|
487
|
-
this.
|
|
488
|
-
if (typeof ctrl.onSubmit === 'function') {
|
|
489
|
-
ctrl.onSubmit(t.value, ctrl.id);
|
|
490
|
-
}
|
|
547
|
+
this._handleSubmit(t.value, ctrl);
|
|
491
548
|
}
|
|
492
549
|
}}"
|
|
493
550
|
/>
|
|
@@ -577,6 +634,25 @@ let NileFilterChart = class NileFilterChart extends NileElement {
|
|
|
577
634
|
</div>`;
|
|
578
635
|
}
|
|
579
636
|
};
|
|
637
|
+
NileFilterChart._builtInFiltrexFns = {
|
|
638
|
+
contains: (s, sub) => String(s ?? '').includes(String(sub ?? '')),
|
|
639
|
+
startsWith: (s, p) => String(s ?? '').startsWith(String(p ?? '')),
|
|
640
|
+
endsWith: (s, p) => String(s ?? '').endsWith(String(p ?? '')),
|
|
641
|
+
lower: (s) => String(s ?? '').toLowerCase(),
|
|
642
|
+
upper: (s) => String(s ?? '').toUpperCase(),
|
|
643
|
+
len: (s) => (s == null ? 0 : Array.isArray(s) ? s.length : String(s).length),
|
|
644
|
+
isEmpty: (s) => s == null || s === '' || (Array.isArray(s) && s.length === 0),
|
|
645
|
+
isNotEmpty: (s) => !(s == null || s === '' || (Array.isArray(s) && s.length === 0)),
|
|
646
|
+
between: (n, lo, hi) => Number(n) >= Number(lo) && Number(n) <= Number(hi),
|
|
647
|
+
year: (d) => new Date(d).getFullYear(),
|
|
648
|
+
month: (d) => new Date(d).getMonth() + 1,
|
|
649
|
+
day: (d) => new Date(d).getDate(),
|
|
650
|
+
daysAgo: (d) => (Date.now() - +new Date(d)) / 86400000,
|
|
651
|
+
matches: (s, re) => new RegExp(String(re ?? '')).test(String(s ?? '')),
|
|
652
|
+
coalesce: (...xs) => xs.find(x => x != null && x !== '') ?? null,
|
|
653
|
+
};
|
|
654
|
+
NileFilterChart._filtrexLoading = null;
|
|
655
|
+
NileFilterChart._jsepLoading = null;
|
|
580
656
|
__decorate([
|
|
581
657
|
property({ attribute: false })
|
|
582
658
|
], NileFilterChart.prototype, "config", void 0);
|
|
@@ -589,7 +665,7 @@ __decorate([
|
|
|
589
665
|
__decorate([
|
|
590
666
|
state()
|
|
591
667
|
], NileFilterChart.prototype, "_promptPlaceholder", void 0);
|
|
592
|
-
NileFilterChart = __decorate([
|
|
668
|
+
NileFilterChart = NileFilterChart_1 = __decorate([
|
|
593
669
|
customElement('nile-filter-chart')
|
|
594
670
|
], NileFilterChart);
|
|
595
671
|
export { NileFilterChart };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aquera/nile-visualization",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.7.0",
|
|
4
4
|
"description": "A visualization Library for the Nile Design System",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Aquera Inc",
|
|
@@ -57,21 +57,15 @@
|
|
|
57
57
|
"clean": "rimraf dist/*"
|
|
58
58
|
},
|
|
59
59
|
"dependencies": {
|
|
60
|
-
"lit": "^3.0.0"
|
|
60
|
+
"lit": "^3.0.0",
|
|
61
|
+
"filtrex": "^3.0.0",
|
|
62
|
+
"jsep": "^1.3.9"
|
|
61
63
|
},
|
|
62
64
|
"peerDependencies": {
|
|
63
65
|
"highcharts": ">=10.0.0",
|
|
64
66
|
"@aquera/nile-elements": ">=0.1.0",
|
|
65
67
|
"@aquera/nile-data-grid": ">=0.1.0"
|
|
66
68
|
},
|
|
67
|
-
"peerDependenciesMeta": {
|
|
68
|
-
"@aquera/nile-elements": {
|
|
69
|
-
"optional": true
|
|
70
|
-
},
|
|
71
|
-
"@aquera/nile-data-grid": {
|
|
72
|
-
"optional": true
|
|
73
|
-
}
|
|
74
|
-
},
|
|
75
69
|
"devDependencies": {
|
|
76
70
|
"@rollup/plugin-babel": "^6.0.3",
|
|
77
71
|
"@rollup/plugin-commonjs": "^25.0.3",
|