@real1ty-obsidian-plugins/utils 2.12.0 → 2.14.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/components/index.d.ts +2 -0
- package/dist/components/index.d.ts.map +1 -0
- package/dist/components/index.js +2 -0
- package/dist/components/index.js.map +1 -0
- package/dist/components/input-managers/base.d.ts +30 -0
- package/dist/components/input-managers/base.d.ts.map +1 -0
- package/dist/components/input-managers/base.js +115 -0
- package/dist/components/input-managers/base.js.map +1 -0
- package/dist/components/input-managers/expression.d.ts +12 -0
- package/dist/components/input-managers/expression.d.ts.map +1 -0
- package/dist/components/input-managers/expression.js +56 -0
- package/dist/components/input-managers/expression.js.map +1 -0
- package/dist/components/input-managers/index.d.ts +4 -0
- package/dist/components/input-managers/index.d.ts.map +1 -0
- package/dist/components/input-managers/index.js +4 -0
- package/dist/components/input-managers/index.js.map +1 -0
- package/dist/components/input-managers/search.d.ts +6 -0
- package/dist/components/input-managers/search.d.ts.map +1 -0
- package/dist/components/input-managers/search.js +16 -0
- package/dist/components/input-managers/search.js.map +1 -0
- package/dist/core/evaluator/base.d.ts.map +1 -1
- package/dist/core/evaluator/base.js +12 -3
- package/dist/core/evaluator/base.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/components/index.ts +1 -0
- package/src/components/input-managers/base.ts +150 -0
- package/src/components/input-managers/expression.ts +92 -0
- package/src/components/input-managers/index.ts +3 -0
- package/src/components/input-managers/search.ts +25 -0
- package/src/core/evaluator/base.ts +15 -3
- package/src/index.ts +2 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/components/index.ts"],"names":[],"mappings":"AAAA,cAAc,kBAAkB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/components/index.ts"],"names":[],"mappings":"AAAA,cAAc,kBAAkB,CAAC","sourcesContent":["export * from \"./input-managers\";\n"]}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
export type InputManagerFilterChangeCallback = () => void;
|
|
2
|
+
export declare abstract class InputManager {
|
|
3
|
+
protected parentEl: HTMLElement;
|
|
4
|
+
protected placeholder: string;
|
|
5
|
+
protected cssPrefix: string;
|
|
6
|
+
protected onFilterChange: InputManagerFilterChangeCallback;
|
|
7
|
+
protected containerEl: HTMLElement;
|
|
8
|
+
protected inputEl: HTMLInputElement | null;
|
|
9
|
+
protected debounceTimer: number | null;
|
|
10
|
+
protected currentValue: string;
|
|
11
|
+
protected persistentlyVisible: boolean;
|
|
12
|
+
protected onHide?: () => void;
|
|
13
|
+
protected hiddenClass: string;
|
|
14
|
+
protected debounceMs: number;
|
|
15
|
+
protected cssClass: string;
|
|
16
|
+
constructor(parentEl: HTMLElement, placeholder: string, cssPrefix: string, onFilterChange: InputManagerFilterChangeCallback, initiallyVisible: boolean, onHide?: () => void, debounceMs?: number);
|
|
17
|
+
private render;
|
|
18
|
+
private handleInputChange;
|
|
19
|
+
protected applyFilterImmediately(): void;
|
|
20
|
+
protected updateFilterValue(value: string): void;
|
|
21
|
+
getCurrentValue(): string;
|
|
22
|
+
show(): void;
|
|
23
|
+
hide(): void;
|
|
24
|
+
focus(): void;
|
|
25
|
+
isVisible(): boolean;
|
|
26
|
+
setPersistentlyVisible(value: boolean): void;
|
|
27
|
+
destroy(): void;
|
|
28
|
+
abstract shouldInclude(data: unknown): boolean;
|
|
29
|
+
}
|
|
30
|
+
//# sourceMappingURL=base.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"base.d.ts","sourceRoot":"","sources":["../../../src/components/input-managers/base.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,gCAAgC,GAAG,MAAM,IAAI,CAAC;AAI1D,8BAAsB,YAAY;IAYhC,SAAS,CAAC,QAAQ,EAAE,WAAW;IAC/B,SAAS,CAAC,WAAW,EAAE,MAAM;IAC7B,SAAS,CAAC,SAAS,EAAE,MAAM;IAC3B,SAAS,CAAC,cAAc,EAAE,gCAAgC;IAd3D,SAAS,CAAC,WAAW,EAAE,WAAW,CAAC;IACnC,SAAS,CAAC,OAAO,EAAE,gBAAgB,GAAG,IAAI,CAAQ;IAClD,SAAS,CAAC,aAAa,EAAE,MAAM,GAAG,IAAI,CAAQ;IAC9C,SAAS,CAAC,YAAY,SAAM;IAC5B,SAAS,CAAC,mBAAmB,UAAS;IACtC,SAAS,CAAC,MAAM,CAAC,EAAE,MAAM,IAAI,CAAC;IAC9B,SAAS,CAAC,WAAW,EAAE,MAAM,CAAC;IAC9B,SAAS,CAAC,UAAU,EAAE,MAAM,CAAC;IAC7B,SAAS,CAAC,QAAQ,EAAE,MAAM,CAAC;gBAGhB,QAAQ,EAAE,WAAW,EACrB,WAAW,EAAE,MAAM,EACnB,SAAS,EAAE,MAAM,EACjB,cAAc,EAAE,gCAAgC,EAC1D,gBAAgB,EAAE,OAAO,EACzB,MAAM,CAAC,EAAE,MAAM,IAAI,EACnB,UAAU,GAAE,MAA4B;IAmBzC,OAAO,CAAC,MAAM;IA0Bd,OAAO,CAAC,iBAAiB;IAUzB,SAAS,CAAC,sBAAsB,IAAI,IAAI;IAQxC,SAAS,CAAC,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAMhD,eAAe,IAAI,MAAM;IAIzB,IAAI,IAAI,IAAI;IAMZ,IAAI,IAAI,IAAI;IAiBZ,KAAK,IAAI,IAAI;IAIb,SAAS,IAAI,OAAO;IAIpB,sBAAsB,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI;IAU5C,OAAO,IAAI,IAAI;IAYf,QAAQ,CAAC,aAAa,CAAC,IAAI,EAAE,OAAO,GAAG,OAAO;CAC9C"}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
const DEFAULT_DEBOUNCE_MS = 150;
|
|
2
|
+
export class InputManager {
|
|
3
|
+
constructor(parentEl, placeholder, cssPrefix, onFilterChange, initiallyVisible, onHide, debounceMs = DEFAULT_DEBOUNCE_MS) {
|
|
4
|
+
this.parentEl = parentEl;
|
|
5
|
+
this.placeholder = placeholder;
|
|
6
|
+
this.cssPrefix = cssPrefix;
|
|
7
|
+
this.onFilterChange = onFilterChange;
|
|
8
|
+
this.inputEl = null;
|
|
9
|
+
this.debounceTimer = null;
|
|
10
|
+
this.currentValue = "";
|
|
11
|
+
this.persistentlyVisible = false;
|
|
12
|
+
this.hiddenClass = `${cssPrefix}-hidden`;
|
|
13
|
+
this.debounceMs = debounceMs;
|
|
14
|
+
this.cssClass = `${cssPrefix}-input`;
|
|
15
|
+
const classes = initiallyVisible
|
|
16
|
+
? `${cssPrefix}-container`
|
|
17
|
+
: `${cssPrefix}-container ${this.hiddenClass}`;
|
|
18
|
+
this.containerEl = this.parentEl.createEl("div", {
|
|
19
|
+
cls: classes,
|
|
20
|
+
});
|
|
21
|
+
this.onHide = onHide;
|
|
22
|
+
this.render();
|
|
23
|
+
}
|
|
24
|
+
render() {
|
|
25
|
+
this.inputEl = this.containerEl.createEl("input", {
|
|
26
|
+
type: "text",
|
|
27
|
+
cls: this.cssClass,
|
|
28
|
+
placeholder: this.placeholder,
|
|
29
|
+
});
|
|
30
|
+
this.inputEl.addEventListener("input", () => {
|
|
31
|
+
this.handleInputChange();
|
|
32
|
+
});
|
|
33
|
+
this.inputEl.addEventListener("keydown", (evt) => {
|
|
34
|
+
var _a;
|
|
35
|
+
if (evt.key === "Escape") {
|
|
36
|
+
// Only allow hiding if not persistently visible
|
|
37
|
+
if (!this.persistentlyVisible) {
|
|
38
|
+
this.hide();
|
|
39
|
+
}
|
|
40
|
+
else {
|
|
41
|
+
// Just blur the input if persistently visible
|
|
42
|
+
(_a = this.inputEl) === null || _a === void 0 ? void 0 : _a.blur();
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
else if (evt.key === "Enter") {
|
|
46
|
+
this.applyFilterImmediately();
|
|
47
|
+
}
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
handleInputChange() {
|
|
51
|
+
if (this.debounceTimer !== null) {
|
|
52
|
+
window.clearTimeout(this.debounceTimer);
|
|
53
|
+
}
|
|
54
|
+
this.debounceTimer = window.setTimeout(() => {
|
|
55
|
+
this.applyFilterImmediately();
|
|
56
|
+
}, this.debounceMs);
|
|
57
|
+
}
|
|
58
|
+
applyFilterImmediately() {
|
|
59
|
+
var _a, _b;
|
|
60
|
+
const newValue = (_b = (_a = this.inputEl) === null || _a === void 0 ? void 0 : _a.value.trim()) !== null && _b !== void 0 ? _b : "";
|
|
61
|
+
if (newValue !== this.currentValue) {
|
|
62
|
+
this.updateFilterValue(newValue);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
updateFilterValue(value) {
|
|
66
|
+
this.currentValue = value;
|
|
67
|
+
this.onFilterChange();
|
|
68
|
+
}
|
|
69
|
+
getCurrentValue() {
|
|
70
|
+
return this.currentValue;
|
|
71
|
+
}
|
|
72
|
+
show() {
|
|
73
|
+
var _a;
|
|
74
|
+
this.containerEl.removeClass(this.hiddenClass);
|
|
75
|
+
(_a = this.inputEl) === null || _a === void 0 ? void 0 : _a.focus();
|
|
76
|
+
}
|
|
77
|
+
hide() {
|
|
78
|
+
var _a;
|
|
79
|
+
// Don't allow hiding if persistently visible
|
|
80
|
+
if (this.persistentlyVisible) {
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
this.containerEl.addClass(this.hiddenClass);
|
|
84
|
+
if (this.inputEl) {
|
|
85
|
+
this.inputEl.value = "";
|
|
86
|
+
}
|
|
87
|
+
this.updateFilterValue("");
|
|
88
|
+
(_a = this.onHide) === null || _a === void 0 ? void 0 : _a.call(this);
|
|
89
|
+
}
|
|
90
|
+
focus() {
|
|
91
|
+
var _a;
|
|
92
|
+
(_a = this.inputEl) === null || _a === void 0 ? void 0 : _a.focus();
|
|
93
|
+
}
|
|
94
|
+
isVisible() {
|
|
95
|
+
return !this.containerEl.hasClass(this.hiddenClass);
|
|
96
|
+
}
|
|
97
|
+
setPersistentlyVisible(value) {
|
|
98
|
+
this.persistentlyVisible = value;
|
|
99
|
+
if (value) {
|
|
100
|
+
this.show();
|
|
101
|
+
}
|
|
102
|
+
else {
|
|
103
|
+
this.hide();
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
destroy() {
|
|
107
|
+
if (this.debounceTimer !== null) {
|
|
108
|
+
window.clearTimeout(this.debounceTimer);
|
|
109
|
+
this.debounceTimer = null;
|
|
110
|
+
}
|
|
111
|
+
this.containerEl.remove();
|
|
112
|
+
this.inputEl = null;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
//# sourceMappingURL=base.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"base.js","sourceRoot":"","sources":["../../../src/components/input-managers/base.ts"],"names":[],"mappings":"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","sourcesContent":["export type InputManagerFilterChangeCallback = () => void;\n\nconst DEFAULT_DEBOUNCE_MS = 150;\n\nexport abstract class InputManager {\n\tprotected containerEl: HTMLElement;\n\tprotected inputEl: HTMLInputElement | null = null;\n\tprotected debounceTimer: number | null = null;\n\tprotected currentValue = \"\";\n\tprotected persistentlyVisible = false;\n\tprotected onHide?: () => void;\n\tprotected hiddenClass: string;\n\tprotected debounceMs: number;\n\tprotected cssClass: string;\n\n\tconstructor(\n\t\tprotected parentEl: HTMLElement,\n\t\tprotected placeholder: string,\n\t\tprotected cssPrefix: string,\n\t\tprotected onFilterChange: InputManagerFilterChangeCallback,\n\t\tinitiallyVisible: boolean,\n\t\tonHide?: () => void,\n\t\tdebounceMs: number = DEFAULT_DEBOUNCE_MS\n\t) {\n\t\tthis.hiddenClass = `${cssPrefix}-hidden`;\n\t\tthis.debounceMs = debounceMs;\n\t\tthis.cssClass = `${cssPrefix}-input`;\n\n\t\tconst classes = initiallyVisible\n\t\t\t? `${cssPrefix}-container`\n\t\t\t: `${cssPrefix}-container ${this.hiddenClass}`;\n\n\t\tthis.containerEl = this.parentEl.createEl(\"div\", {\n\t\t\tcls: classes,\n\t\t});\n\n\t\tthis.onHide = onHide;\n\n\t\tthis.render();\n\t}\n\n\tprivate render(): void {\n\t\tthis.inputEl = this.containerEl.createEl(\"input\", {\n\t\t\ttype: \"text\",\n\t\t\tcls: this.cssClass,\n\t\t\tplaceholder: this.placeholder,\n\t\t});\n\n\t\tthis.inputEl.addEventListener(\"input\", () => {\n\t\t\tthis.handleInputChange();\n\t\t});\n\n\t\tthis.inputEl.addEventListener(\"keydown\", (evt) => {\n\t\t\tif (evt.key === \"Escape\") {\n\t\t\t\t// Only allow hiding if not persistently visible\n\t\t\t\tif (!this.persistentlyVisible) {\n\t\t\t\t\tthis.hide();\n\t\t\t\t} else {\n\t\t\t\t\t// Just blur the input if persistently visible\n\t\t\t\t\tthis.inputEl?.blur();\n\t\t\t\t}\n\t\t\t} else if (evt.key === \"Enter\") {\n\t\t\t\tthis.applyFilterImmediately();\n\t\t\t}\n\t\t});\n\t}\n\n\tprivate handleInputChange(): void {\n\t\tif (this.debounceTimer !== null) {\n\t\t\twindow.clearTimeout(this.debounceTimer);\n\t\t}\n\n\t\tthis.debounceTimer = window.setTimeout(() => {\n\t\t\tthis.applyFilterImmediately();\n\t\t}, this.debounceMs);\n\t}\n\n\tprotected applyFilterImmediately(): void {\n\t\tconst newValue = this.inputEl?.value.trim() ?? \"\";\n\n\t\tif (newValue !== this.currentValue) {\n\t\t\tthis.updateFilterValue(newValue);\n\t\t}\n\t}\n\n\tprotected updateFilterValue(value: string): void {\n\t\tthis.currentValue = value;\n\n\t\tthis.onFilterChange();\n\t}\n\n\tgetCurrentValue(): string {\n\t\treturn this.currentValue;\n\t}\n\n\tshow(): void {\n\t\tthis.containerEl.removeClass(this.hiddenClass);\n\n\t\tthis.inputEl?.focus();\n\t}\n\n\thide(): void {\n\t\t// Don't allow hiding if persistently visible\n\t\tif (this.persistentlyVisible) {\n\t\t\treturn;\n\t\t}\n\n\t\tthis.containerEl.addClass(this.hiddenClass);\n\n\t\tif (this.inputEl) {\n\t\t\tthis.inputEl.value = \"\";\n\t\t}\n\n\t\tthis.updateFilterValue(\"\");\n\n\t\tthis.onHide?.();\n\t}\n\n\tfocus(): void {\n\t\tthis.inputEl?.focus();\n\t}\n\n\tisVisible(): boolean {\n\t\treturn !this.containerEl.hasClass(this.hiddenClass);\n\t}\n\n\tsetPersistentlyVisible(value: boolean): void {\n\t\tthis.persistentlyVisible = value;\n\n\t\tif (value) {\n\t\t\tthis.show();\n\t\t} else {\n\t\t\tthis.hide();\n\t\t}\n\t}\n\n\tdestroy(): void {\n\t\tif (this.debounceTimer !== null) {\n\t\t\twindow.clearTimeout(this.debounceTimer);\n\n\t\t\tthis.debounceTimer = null;\n\t\t}\n\n\t\tthis.containerEl.remove();\n\n\t\tthis.inputEl = null;\n\t}\n\n\tabstract shouldInclude(data: unknown): boolean;\n}\n"]}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { InputManager } from "./base";
|
|
2
|
+
export declare class ExpressionFilterInputManager extends InputManager {
|
|
3
|
+
private compiledFunc;
|
|
4
|
+
private propertyMapping;
|
|
5
|
+
private lastWarnedExpression;
|
|
6
|
+
constructor(parentEl: HTMLElement, cssPrefix: string, onFilterChange: () => void, initiallyVisible?: boolean, placeholder?: string, onHide?: () => void, debounceMs?: number);
|
|
7
|
+
protected updateFilterValue(filterValue: string): void;
|
|
8
|
+
shouldInclude(event: {
|
|
9
|
+
meta?: Record<string, unknown>;
|
|
10
|
+
}): boolean;
|
|
11
|
+
}
|
|
12
|
+
//# sourceMappingURL=expression.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"expression.d.ts","sourceRoot":"","sources":["../../../src/components/input-managers/expression.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAC;AAEtC,qBAAa,4BAA6B,SAAQ,YAAY;IAC7D,OAAO,CAAC,YAAY,CAAkD;IAEtE,OAAO,CAAC,eAAe,CAA6B;IAEpD,OAAO,CAAC,oBAAoB,CAAuB;gBAGlD,QAAQ,EAAE,WAAW,EACrB,SAAS,EAAE,MAAM,EACjB,cAAc,EAAE,MAAM,IAAI,EAC1B,gBAAgB,GAAE,OAAe,EACjC,WAAW,GAAE,MAA4B,EACzC,MAAM,CAAC,EAAE,MAAM,IAAI,EACnB,UAAU,CAAC,EAAE,MAAM;IASpB,SAAS,CAAC,iBAAiB,CAAC,WAAW,EAAE,MAAM,GAAG,IAAI;IAUtD,aAAa,CAAC,KAAK,EAAE;QAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;KAAE,GAAG,OAAO;CAsDjE"}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { buildPropertyMapping, sanitizeExpression } from "../../core/expression-utils";
|
|
2
|
+
import { InputManager } from "./base";
|
|
3
|
+
export class ExpressionFilterInputManager extends InputManager {
|
|
4
|
+
constructor(parentEl, cssPrefix, onFilterChange, initiallyVisible = false, placeholder = "Status === 'Done'", onHide, debounceMs) {
|
|
5
|
+
super(parentEl, placeholder, cssPrefix, onFilterChange, initiallyVisible, onHide, debounceMs);
|
|
6
|
+
this.compiledFunc = null;
|
|
7
|
+
this.propertyMapping = new Map();
|
|
8
|
+
this.lastWarnedExpression = null;
|
|
9
|
+
this.cssClass = `${cssPrefix}-expression-input`;
|
|
10
|
+
if (this.inputEl) {
|
|
11
|
+
this.inputEl.className = this.cssClass;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
updateFilterValue(filterValue) {
|
|
15
|
+
super.updateFilterValue(filterValue);
|
|
16
|
+
this.compiledFunc = null;
|
|
17
|
+
this.propertyMapping.clear();
|
|
18
|
+
this.lastWarnedExpression = null;
|
|
19
|
+
}
|
|
20
|
+
shouldInclude(event) {
|
|
21
|
+
if (!this.currentValue)
|
|
22
|
+
return true;
|
|
23
|
+
const frontmatter = event.meta || {};
|
|
24
|
+
try {
|
|
25
|
+
const currentKeys = new Set(Object.keys(frontmatter));
|
|
26
|
+
const existingKeys = new Set(this.propertyMapping.keys());
|
|
27
|
+
const newKeys = [...currentKeys].filter((key) => !existingKeys.has(key));
|
|
28
|
+
if (newKeys.length > 0) {
|
|
29
|
+
const allKeys = new Set([...existingKeys, ...currentKeys]);
|
|
30
|
+
this.propertyMapping = buildPropertyMapping(Array.from(allKeys));
|
|
31
|
+
this.compiledFunc = null;
|
|
32
|
+
}
|
|
33
|
+
if (!this.compiledFunc) {
|
|
34
|
+
const sanitized = sanitizeExpression(this.currentValue, this.propertyMapping);
|
|
35
|
+
const params = Array.from(this.propertyMapping.values());
|
|
36
|
+
// eslint-disable-next-line @typescript-eslint/no-implied-eval -- Dynamic function creation for expression evaluation with sanitized input
|
|
37
|
+
this.compiledFunc = new Function(...params, `"use strict"; return ${sanitized};`);
|
|
38
|
+
}
|
|
39
|
+
const values = Array.from(this.propertyMapping.keys()).map((key) => { var _a; return (_a = frontmatter[key]) !== null && _a !== void 0 ? _a : undefined; });
|
|
40
|
+
const result = this.compiledFunc(...values);
|
|
41
|
+
return result;
|
|
42
|
+
}
|
|
43
|
+
catch (error) {
|
|
44
|
+
if (error instanceof ReferenceError) {
|
|
45
|
+
const hasInequality = this.currentValue.includes("!==") || this.currentValue.includes("!=");
|
|
46
|
+
return hasInequality;
|
|
47
|
+
}
|
|
48
|
+
if (this.lastWarnedExpression !== this.currentValue) {
|
|
49
|
+
console.warn("Invalid filter expression:", this.currentValue, error);
|
|
50
|
+
this.lastWarnedExpression = this.currentValue;
|
|
51
|
+
}
|
|
52
|
+
return false;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
//# sourceMappingURL=expression.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"expression.js","sourceRoot":"","sources":["../../../src/components/input-managers/expression.ts"],"names":[],"mappings":"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","sourcesContent":["import { buildPropertyMapping, sanitizeExpression } from \"../../core/expression-utils\";\n\nimport { InputManager } from \"./base\";\n\nexport class ExpressionFilterInputManager extends InputManager {\n\tprivate compiledFunc: ((...args: unknown[]) => boolean) | null = null;\n\n\tprivate propertyMapping = new Map<string, string>();\n\n\tprivate lastWarnedExpression: string | null = null;\n\n\tconstructor(\n\t\tparentEl: HTMLElement,\n\t\tcssPrefix: string,\n\t\tonFilterChange: () => void,\n\t\tinitiallyVisible: boolean = false,\n\t\tplaceholder: string = \"Status === 'Done'\",\n\t\tonHide?: () => void,\n\t\tdebounceMs?: number\n\t) {\n\t\tsuper(parentEl, placeholder, cssPrefix, onFilterChange, initiallyVisible, onHide, debounceMs);\n\t\tthis.cssClass = `${cssPrefix}-expression-input`;\n\t\tif (this.inputEl) {\n\t\t\tthis.inputEl.className = this.cssClass;\n\t\t}\n\t}\n\n\tprotected updateFilterValue(filterValue: string): void {\n\t\tsuper.updateFilterValue(filterValue);\n\n\t\tthis.compiledFunc = null;\n\n\t\tthis.propertyMapping.clear();\n\n\t\tthis.lastWarnedExpression = null;\n\t}\n\n\tshouldInclude(event: { meta?: Record<string, unknown> }): boolean {\n\t\tif (!this.currentValue) return true;\n\n\t\tconst frontmatter = event.meta || {};\n\n\t\ttry {\n\t\t\tconst currentKeys = new Set(Object.keys(frontmatter));\n\n\t\t\tconst existingKeys = new Set(this.propertyMapping.keys());\n\n\t\t\tconst newKeys = [...currentKeys].filter((key) => !existingKeys.has(key));\n\n\t\t\tif (newKeys.length > 0) {\n\t\t\t\tconst allKeys = new Set([...existingKeys, ...currentKeys]);\n\n\t\t\t\tthis.propertyMapping = buildPropertyMapping(Array.from(allKeys));\n\n\t\t\t\tthis.compiledFunc = null;\n\t\t\t}\n\n\t\t\tif (!this.compiledFunc) {\n\t\t\t\tconst sanitized = sanitizeExpression(this.currentValue, this.propertyMapping);\n\n\t\t\t\tconst params = Array.from(this.propertyMapping.values());\n\n\t\t\t\t// eslint-disable-next-line @typescript-eslint/no-implied-eval -- Dynamic function creation for expression evaluation with sanitized input\n\t\t\t\tthis.compiledFunc = new Function(...params, `\"use strict\"; return ${sanitized};`) as (\n\t\t\t\t\t...args: unknown[]\n\t\t\t\t) => boolean;\n\t\t\t}\n\n\t\t\tconst values = Array.from(this.propertyMapping.keys()).map(\n\t\t\t\t(key) => frontmatter[key] ?? undefined\n\t\t\t);\n\n\t\t\tconst result = this.compiledFunc(...values);\n\n\t\t\treturn result;\n\t\t} catch (error) {\n\t\t\tif (error instanceof ReferenceError) {\n\t\t\t\tconst hasInequality = this.currentValue.includes(\"!==\") || this.currentValue.includes(\"!=\");\n\n\t\t\t\treturn hasInequality;\n\t\t\t}\n\n\t\t\tif (this.lastWarnedExpression !== this.currentValue) {\n\t\t\t\tconsole.warn(\"Invalid filter expression:\", this.currentValue, error);\n\n\t\t\t\tthis.lastWarnedExpression = this.currentValue;\n\t\t\t}\n\n\t\t\treturn false;\n\t\t}\n\t}\n}\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/input-managers/index.ts"],"names":[],"mappings":"AAAA,cAAc,QAAQ,CAAC;AACvB,cAAc,cAAc,CAAC;AAC7B,cAAc,UAAU,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/components/input-managers/index.ts"],"names":[],"mappings":"AAAA,cAAc,QAAQ,CAAC;AACvB,cAAc,cAAc,CAAC;AAC7B,cAAc,UAAU,CAAC","sourcesContent":["export * from \"./base\";\nexport * from \"./expression\";\nexport * from \"./search\";\n"]}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { InputManager } from "./base";
|
|
2
|
+
export declare class SearchFilterInputManager extends InputManager {
|
|
3
|
+
constructor(parentEl: HTMLElement, cssPrefix: string, onFilterChange: () => void, initiallyVisible?: boolean, placeholder?: string, onHide?: () => void, debounceMs?: number);
|
|
4
|
+
shouldInclude(value: string): boolean;
|
|
5
|
+
}
|
|
6
|
+
//# sourceMappingURL=search.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"search.d.ts","sourceRoot":"","sources":["../../../src/components/input-managers/search.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAC;AAEtC,qBAAa,wBAAyB,SAAQ,YAAY;gBAExD,QAAQ,EAAE,WAAW,EACrB,SAAS,EAAE,MAAM,EACjB,cAAc,EAAE,MAAM,IAAI,EAC1B,gBAAgB,GAAE,OAAe,EACjC,WAAW,GAAE,MAAqB,EAClC,MAAM,CAAC,EAAE,MAAM,IAAI,EACnB,UAAU,CAAC,EAAE,MAAM;IASpB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO;CAKrC"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { InputManager } from "./base";
|
|
2
|
+
export class SearchFilterInputManager extends InputManager {
|
|
3
|
+
constructor(parentEl, cssPrefix, onFilterChange, initiallyVisible = false, placeholder = "Search ...", onHide, debounceMs) {
|
|
4
|
+
super(parentEl, placeholder, cssPrefix, onFilterChange, initiallyVisible, onHide, debounceMs);
|
|
5
|
+
this.cssClass = `${cssPrefix}-search-input`;
|
|
6
|
+
if (this.inputEl) {
|
|
7
|
+
this.inputEl.className = this.cssClass;
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
shouldInclude(value) {
|
|
11
|
+
if (!this.currentValue)
|
|
12
|
+
return true;
|
|
13
|
+
return value.toLowerCase().includes(this.currentValue.toLowerCase());
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
//# sourceMappingURL=search.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"search.js","sourceRoot":"","sources":["../../../src/components/input-managers/search.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAC;AAEtC,MAAM,OAAO,wBAAyB,SAAQ,YAAY;IACzD,YACC,QAAqB,EACrB,SAAiB,EACjB,cAA0B,EAC1B,mBAA4B,KAAK,EACjC,cAAsB,YAAY,EAClC,MAAmB,EACnB,UAAmB;QAEnB,KAAK,CAAC,QAAQ,EAAE,WAAW,EAAE,SAAS,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,EAAE,UAAU,CAAC,CAAC;QAC9F,IAAI,CAAC,QAAQ,GAAG,GAAG,SAAS,eAAe,CAAC;QAC5C,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;YAClB,IAAI,CAAC,OAAO,CAAC,SAAS,GAAG,IAAI,CAAC,QAAQ,CAAC;QACxC,CAAC;IACF,CAAC;IAED,aAAa,CAAC,KAAa;QAC1B,IAAI,CAAC,IAAI,CAAC,YAAY;YAAE,OAAO,IAAI,CAAC;QAEpC,OAAO,KAAK,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,YAAY,CAAC,WAAW,EAAE,CAAC,CAAC;IACtE,CAAC;CACD","sourcesContent":["import { InputManager } from \"./base\";\n\nexport class SearchFilterInputManager extends InputManager {\n\tconstructor(\n\t\tparentEl: HTMLElement,\n\t\tcssPrefix: string,\n\t\tonFilterChange: () => void,\n\t\tinitiallyVisible: boolean = false,\n\t\tplaceholder: string = \"Search ...\",\n\t\tonHide?: () => void,\n\t\tdebounceMs?: number\n\t) {\n\t\tsuper(parentEl, placeholder, cssPrefix, onFilterChange, initiallyVisible, onHide, debounceMs);\n\t\tthis.cssClass = `${cssPrefix}-search-input`;\n\t\tif (this.inputEl) {\n\t\t\tthis.inputEl.className = this.cssClass;\n\t\t}\n\t}\n\n\tshouldInclude(value: string): boolean {\n\t\tif (!this.currentValue) return true;\n\n\t\treturn value.toLowerCase().includes(this.currentValue.toLowerCase());\n\t}\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"base.d.ts","sourceRoot":"","sources":["../../../src/core/evaluator/base.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAgB,MAAM,MAAM,CAAC;AAI1D,MAAM,WAAW,QAAQ;IACxB,EAAE,EAAE,MAAM,CAAC;IACX,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,OAAO,CAAC;CACjB;AAED;;;GAGG;AACH,8BAAsB,aAAa,CAAC,KAAK,SAAS,QAAQ,EAAE,SAAS;IACpE,SAAS,CAAC,KAAK,EAAE,KAAK,EAAE,CAAM;IAC9B,OAAO,CAAC,iBAAiB,CAA2D;IACpF,OAAO,CAAC,eAAe,CAA6B;IACpD,OAAO,CAAC,YAAY,CAA6B;gBAErC,aAAa,EAAE,eAAe,CAAC,SAAS,CAAC;IAQrD,SAAS,CAAC,QAAQ,CAAC,YAAY,CAAC,QAAQ,EAAE,SAAS,GAAG,KAAK,EAAE;IAE7D,OAAO,IAAI,IAAI;IAMf,SAAS,CAAC,YAAY,CAAC,IAAI,EAAE,KAAK,EAAE,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO;
|
|
1
|
+
{"version":3,"file":"base.d.ts","sourceRoot":"","sources":["../../../src/core/evaluator/base.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAgB,MAAM,MAAM,CAAC;AAI1D,MAAM,WAAW,QAAQ;IACxB,EAAE,EAAE,MAAM,CAAC;IACX,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,OAAO,CAAC;CACjB;AAED;;;GAGG;AACH,8BAAsB,aAAa,CAAC,KAAK,SAAS,QAAQ,EAAE,SAAS;IACpE,SAAS,CAAC,KAAK,EAAE,KAAK,EAAE,CAAM;IAC9B,OAAO,CAAC,iBAAiB,CAA2D;IACpF,OAAO,CAAC,eAAe,CAA6B;IACpD,OAAO,CAAC,YAAY,CAA6B;gBAErC,aAAa,EAAE,eAAe,CAAC,SAAS,CAAC;IAQrD,SAAS,CAAC,QAAQ,CAAC,YAAY,CAAC,QAAQ,EAAE,SAAS,GAAG,KAAK,EAAE;IAE7D,OAAO,IAAI,IAAI;IAMf,SAAS,CAAC,YAAY,CAAC,IAAI,EAAE,KAAK,EAAE,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO;IA2ClF,SAAS,CAAC,QAAQ,CAAC,KAAK,EAAE,GAAG,GAAG,OAAO;CAGvC"}
|
|
@@ -26,8 +26,16 @@ export class BaseEvaluator {
|
|
|
26
26
|
return false;
|
|
27
27
|
}
|
|
28
28
|
try {
|
|
29
|
-
|
|
30
|
-
|
|
29
|
+
// Progressively build property mapping as we encounter new properties
|
|
30
|
+
const currentKeys = new Set(Object.keys(frontmatter));
|
|
31
|
+
const existingKeys = new Set(this.propertyMapping.keys());
|
|
32
|
+
const newKeys = [...currentKeys].filter((key) => !existingKeys.has(key));
|
|
33
|
+
// If new properties are found, rebuild the mapping and invalidate compiled functions
|
|
34
|
+
if (newKeys.length > 0) {
|
|
35
|
+
const allKeys = new Set([...existingKeys, ...currentKeys]);
|
|
36
|
+
this.propertyMapping = buildPropertyMapping(Array.from(allKeys));
|
|
37
|
+
// Clear compiled functions since property mapping changed
|
|
38
|
+
this.compiledFunctions.clear();
|
|
31
39
|
}
|
|
32
40
|
let compiledFunc = this.compiledFunctions.get(rule.id);
|
|
33
41
|
if (!compiledFunc) {
|
|
@@ -36,7 +44,8 @@ export class BaseEvaluator {
|
|
|
36
44
|
compiledFunc = new Function(...params, `"use strict"; return ${sanitized};`);
|
|
37
45
|
this.compiledFunctions.set(rule.id, compiledFunc);
|
|
38
46
|
}
|
|
39
|
-
|
|
47
|
+
// Use undefined for missing properties instead of letting them be undefined implicitly
|
|
48
|
+
const values = Array.from(this.propertyMapping.keys()).map((key) => { var _a; return (_a = frontmatter[key]) !== null && _a !== void 0 ? _a : undefined; });
|
|
40
49
|
const result = compiledFunc(...values);
|
|
41
50
|
return result === true;
|
|
42
51
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"base.js","sourceRoot":"","sources":["../../../src/core/evaluator/base.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,oBAAoB,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AAQ/E;;;GAGG;AACH,MAAM,OAAgB,aAAa;IAMlC,YAAY,aAAyC;QAL3C,UAAK,GAAY,EAAE,CAAC;QACtB,sBAAiB,GAAG,IAAI,GAAG,EAAgD,CAAC;QAC5E,oBAAe,GAAG,IAAI,GAAG,EAAkB,CAAC;QAC5C,iBAAY,GAAwB,IAAI,CAAC;QAGhD,IAAI,CAAC,YAAY,GAAG,aAAa,CAAC,SAAS,CAAC,CAAC,QAAQ,EAAE,EAAE;YACxD,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,YAAY,CAAC,QAAQ,CAAC,CAAC;YACzC,IAAI,CAAC,iBAAiB,CAAC,KAAK,EAAE,CAAC;YAC/B,IAAI,CAAC,eAAe,CAAC,KAAK,EAAE,CAAC;QAC9B,CAAC,CAAC,CAAC;IACJ,CAAC;IAID,OAAO;;QACN,MAAA,IAAI,CAAC,YAAY,0CAAE,WAAW,EAAE,CAAC;QACjC,IAAI,CAAC,iBAAiB,CAAC,KAAK,EAAE,CAAC;QAC/B,IAAI,CAAC,eAAe,CAAC,KAAK,EAAE,CAAC;IAC9B,CAAC;IAES,YAAY,CAAC,IAAW,EAAE,WAAoC;QACvE,IAAI,CAAC,IAAI,CAAC,OAAO,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,CAAC;YACvC,OAAO,KAAK,CAAC;QACd,CAAC;QAED,IAAI,CAAC;YACJ,IAAI,IAAI,CAAC,eAAe,CAAC,IAAI,
|
|
1
|
+
{"version":3,"file":"base.js","sourceRoot":"","sources":["../../../src/core/evaluator/base.ts"],"names":[],"mappings":"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","sourcesContent":["import type { BehaviorSubject, Subscription } from \"rxjs\";\n\nimport { buildPropertyMapping, sanitizeExpression } from \"../expression-utils\";\n\nexport interface BaseRule {\n\tid: string;\n\texpression: string;\n\tenabled: boolean;\n}\n\n/**\n * Generic base class for evaluating JavaScript expressions against frontmatter objects.\n * Provides reactive compilation of rules via RxJS subscription and safe evaluation.\n */\nexport abstract class BaseEvaluator<TRule extends BaseRule, TSettings> {\n\tprotected rules: TRule[] = [];\n\tprivate compiledFunctions = new Map<string, ((...args: any[]) => boolean) | null>();\n\tprivate propertyMapping = new Map<string, string>();\n\tprivate subscription: Subscription | null = null;\n\n\tconstructor(settingsStore: BehaviorSubject<TSettings>) {\n\t\tthis.subscription = settingsStore.subscribe((settings) => {\n\t\t\tthis.rules = this.extractRules(settings);\n\t\t\tthis.compiledFunctions.clear();\n\t\t\tthis.propertyMapping.clear();\n\t\t});\n\t}\n\n\tprotected abstract extractRules(settings: TSettings): TRule[];\n\n\tdestroy(): void {\n\t\tthis.subscription?.unsubscribe();\n\t\tthis.compiledFunctions.clear();\n\t\tthis.propertyMapping.clear();\n\t}\n\n\tprotected evaluateRule(rule: TRule, frontmatter: Record<string, unknown>): boolean {\n\t\tif (!rule.enabled || !rule.expression) {\n\t\t\treturn false;\n\t\t}\n\n\t\ttry {\n\t\t\t// Progressively build property mapping as we encounter new properties\n\t\t\tconst currentKeys = new Set(Object.keys(frontmatter));\n\t\t\tconst existingKeys = new Set(this.propertyMapping.keys());\n\t\t\tconst newKeys = [...currentKeys].filter((key) => !existingKeys.has(key));\n\n\t\t\t// If new properties are found, rebuild the mapping and invalidate compiled functions\n\t\t\tif (newKeys.length > 0) {\n\t\t\t\tconst allKeys = new Set([...existingKeys, ...currentKeys]);\n\t\t\t\tthis.propertyMapping = buildPropertyMapping(Array.from(allKeys));\n\t\t\t\t// Clear compiled functions since property mapping changed\n\t\t\t\tthis.compiledFunctions.clear();\n\t\t\t}\n\n\t\t\tlet compiledFunc = this.compiledFunctions.get(rule.id);\n\n\t\t\tif (!compiledFunc) {\n\t\t\t\tconst sanitized = sanitizeExpression(rule.expression, this.propertyMapping);\n\t\t\t\tconst params = Array.from(this.propertyMapping.values());\n\t\t\t\tcompiledFunc = new Function(...params, `\"use strict\"; return ${sanitized};`) as (\n\t\t\t\t\t...args: any[]\n\t\t\t\t) => boolean;\n\t\t\t\tthis.compiledFunctions.set(rule.id, compiledFunc);\n\t\t\t}\n\n\t\t\t// Use undefined for missing properties instead of letting them be undefined implicitly\n\t\t\tconst values = Array.from(this.propertyMapping.keys()).map(\n\t\t\t\t(key) => frontmatter[key] ?? undefined\n\t\t\t);\n\t\t\tconst result = compiledFunc(...values);\n\n\t\t\treturn result === true;\n\t\t} catch (error) {\n\t\t\tconsole.warn(`Invalid expression (${rule.id}):`, rule.expression, error);\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tprotected isTruthy(value: any): boolean {\n\t\treturn value === true;\n\t}\n}\n"]}
|
package/dist/index.d.ts
CHANGED
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAGA,cAAc,SAAS,CAAC;AAExB,cAAc,QAAQ,CAAC;AAGvB,cAAc,QAAQ,CAAC;AAEvB,cAAc,QAAQ,CAAC;AAEvB,cAAc,UAAU,CAAC;AACzB,cAAc,YAAY,CAAC;AAE3B,cAAc,UAAU,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAGA,cAAc,SAAS,CAAC;AAExB,cAAc,cAAc,CAAC;AAE7B,cAAc,QAAQ,CAAC;AAGvB,cAAc,QAAQ,CAAC;AAEvB,cAAc,QAAQ,CAAC;AAEvB,cAAc,UAAU,CAAC;AACzB,cAAc,YAAY,CAAC;AAE3B,cAAc,UAAU,CAAC"}
|
package/dist/index.js
CHANGED
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,WAAW;AAEX,kBAAkB;AAClB,cAAc,SAAS,CAAC;AACxB,iBAAiB;AACjB,cAAc,QAAQ,CAAC;AAEvB,kBAAkB;AAClB,cAAc,QAAQ,CAAC;AACvB,kBAAkB;AAClB,cAAc,QAAQ,CAAC;AACvB,kBAAkB;AAClB,cAAc,UAAU,CAAC;AACzB,cAAc,YAAY,CAAC;AAC3B,mBAAmB;AACnB,cAAc,UAAU,CAAC","sourcesContent":["// Settings\n\n// Async utilities\nexport * from \"./async\";\n// Core utilities\nexport * from \"./core\";\n\n// Date operations\nexport * from \"./date\";\n// File operations\nexport * from \"./file\";\n// Input utilities\nexport * from \"./inputs\";\nexport * from \"./settings\";\n// String utilities\nexport * from \"./string\";\n"]}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,WAAW;AAEX,kBAAkB;AAClB,cAAc,SAAS,CAAC;AACxB,aAAa;AACb,cAAc,cAAc,CAAC;AAC7B,iBAAiB;AACjB,cAAc,QAAQ,CAAC;AAEvB,kBAAkB;AAClB,cAAc,QAAQ,CAAC;AACvB,kBAAkB;AAClB,cAAc,QAAQ,CAAC;AACvB,kBAAkB;AAClB,cAAc,UAAU,CAAC;AACzB,cAAc,YAAY,CAAC;AAC3B,mBAAmB;AACnB,cAAc,UAAU,CAAC","sourcesContent":["// Settings\n\n// Async utilities\nexport * from \"./async\";\n// Components\nexport * from \"./components\";\n// Core utilities\nexport * from \"./core\";\n\n// Date operations\nexport * from \"./date\";\n// File operations\nexport * from \"./file\";\n// Input utilities\nexport * from \"./inputs\";\nexport * from \"./settings\";\n// String utilities\nexport * from \"./string\";\n"]}
|
package/package.json
CHANGED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./input-managers";
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
export type InputManagerFilterChangeCallback = () => void;
|
|
2
|
+
|
|
3
|
+
const DEFAULT_DEBOUNCE_MS = 150;
|
|
4
|
+
|
|
5
|
+
export abstract class InputManager {
|
|
6
|
+
protected containerEl: HTMLElement;
|
|
7
|
+
protected inputEl: HTMLInputElement | null = null;
|
|
8
|
+
protected debounceTimer: number | null = null;
|
|
9
|
+
protected currentValue = "";
|
|
10
|
+
protected persistentlyVisible = false;
|
|
11
|
+
protected onHide?: () => void;
|
|
12
|
+
protected hiddenClass: string;
|
|
13
|
+
protected debounceMs: number;
|
|
14
|
+
protected cssClass: string;
|
|
15
|
+
|
|
16
|
+
constructor(
|
|
17
|
+
protected parentEl: HTMLElement,
|
|
18
|
+
protected placeholder: string,
|
|
19
|
+
protected cssPrefix: string,
|
|
20
|
+
protected onFilterChange: InputManagerFilterChangeCallback,
|
|
21
|
+
initiallyVisible: boolean,
|
|
22
|
+
onHide?: () => void,
|
|
23
|
+
debounceMs: number = DEFAULT_DEBOUNCE_MS
|
|
24
|
+
) {
|
|
25
|
+
this.hiddenClass = `${cssPrefix}-hidden`;
|
|
26
|
+
this.debounceMs = debounceMs;
|
|
27
|
+
this.cssClass = `${cssPrefix}-input`;
|
|
28
|
+
|
|
29
|
+
const classes = initiallyVisible
|
|
30
|
+
? `${cssPrefix}-container`
|
|
31
|
+
: `${cssPrefix}-container ${this.hiddenClass}`;
|
|
32
|
+
|
|
33
|
+
this.containerEl = this.parentEl.createEl("div", {
|
|
34
|
+
cls: classes,
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
this.onHide = onHide;
|
|
38
|
+
|
|
39
|
+
this.render();
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
private render(): void {
|
|
43
|
+
this.inputEl = this.containerEl.createEl("input", {
|
|
44
|
+
type: "text",
|
|
45
|
+
cls: this.cssClass,
|
|
46
|
+
placeholder: this.placeholder,
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
this.inputEl.addEventListener("input", () => {
|
|
50
|
+
this.handleInputChange();
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
this.inputEl.addEventListener("keydown", (evt) => {
|
|
54
|
+
if (evt.key === "Escape") {
|
|
55
|
+
// Only allow hiding if not persistently visible
|
|
56
|
+
if (!this.persistentlyVisible) {
|
|
57
|
+
this.hide();
|
|
58
|
+
} else {
|
|
59
|
+
// Just blur the input if persistently visible
|
|
60
|
+
this.inputEl?.blur();
|
|
61
|
+
}
|
|
62
|
+
} else if (evt.key === "Enter") {
|
|
63
|
+
this.applyFilterImmediately();
|
|
64
|
+
}
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
private handleInputChange(): void {
|
|
69
|
+
if (this.debounceTimer !== null) {
|
|
70
|
+
window.clearTimeout(this.debounceTimer);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
this.debounceTimer = window.setTimeout(() => {
|
|
74
|
+
this.applyFilterImmediately();
|
|
75
|
+
}, this.debounceMs);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
protected applyFilterImmediately(): void {
|
|
79
|
+
const newValue = this.inputEl?.value.trim() ?? "";
|
|
80
|
+
|
|
81
|
+
if (newValue !== this.currentValue) {
|
|
82
|
+
this.updateFilterValue(newValue);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
protected updateFilterValue(value: string): void {
|
|
87
|
+
this.currentValue = value;
|
|
88
|
+
|
|
89
|
+
this.onFilterChange();
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
getCurrentValue(): string {
|
|
93
|
+
return this.currentValue;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
show(): void {
|
|
97
|
+
this.containerEl.removeClass(this.hiddenClass);
|
|
98
|
+
|
|
99
|
+
this.inputEl?.focus();
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
hide(): void {
|
|
103
|
+
// Don't allow hiding if persistently visible
|
|
104
|
+
if (this.persistentlyVisible) {
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
this.containerEl.addClass(this.hiddenClass);
|
|
109
|
+
|
|
110
|
+
if (this.inputEl) {
|
|
111
|
+
this.inputEl.value = "";
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
this.updateFilterValue("");
|
|
115
|
+
|
|
116
|
+
this.onHide?.();
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
focus(): void {
|
|
120
|
+
this.inputEl?.focus();
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
isVisible(): boolean {
|
|
124
|
+
return !this.containerEl.hasClass(this.hiddenClass);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
setPersistentlyVisible(value: boolean): void {
|
|
128
|
+
this.persistentlyVisible = value;
|
|
129
|
+
|
|
130
|
+
if (value) {
|
|
131
|
+
this.show();
|
|
132
|
+
} else {
|
|
133
|
+
this.hide();
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
destroy(): void {
|
|
138
|
+
if (this.debounceTimer !== null) {
|
|
139
|
+
window.clearTimeout(this.debounceTimer);
|
|
140
|
+
|
|
141
|
+
this.debounceTimer = null;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
this.containerEl.remove();
|
|
145
|
+
|
|
146
|
+
this.inputEl = null;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
abstract shouldInclude(data: unknown): boolean;
|
|
150
|
+
}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { buildPropertyMapping, sanitizeExpression } from "../../core/expression-utils";
|
|
2
|
+
|
|
3
|
+
import { InputManager } from "./base";
|
|
4
|
+
|
|
5
|
+
export class ExpressionFilterInputManager extends InputManager {
|
|
6
|
+
private compiledFunc: ((...args: unknown[]) => boolean) | null = null;
|
|
7
|
+
|
|
8
|
+
private propertyMapping = new Map<string, string>();
|
|
9
|
+
|
|
10
|
+
private lastWarnedExpression: string | null = null;
|
|
11
|
+
|
|
12
|
+
constructor(
|
|
13
|
+
parentEl: HTMLElement,
|
|
14
|
+
cssPrefix: string,
|
|
15
|
+
onFilterChange: () => void,
|
|
16
|
+
initiallyVisible: boolean = false,
|
|
17
|
+
placeholder: string = "Status === 'Done'",
|
|
18
|
+
onHide?: () => void,
|
|
19
|
+
debounceMs?: number
|
|
20
|
+
) {
|
|
21
|
+
super(parentEl, placeholder, cssPrefix, onFilterChange, initiallyVisible, onHide, debounceMs);
|
|
22
|
+
this.cssClass = `${cssPrefix}-expression-input`;
|
|
23
|
+
if (this.inputEl) {
|
|
24
|
+
this.inputEl.className = this.cssClass;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
protected updateFilterValue(filterValue: string): void {
|
|
29
|
+
super.updateFilterValue(filterValue);
|
|
30
|
+
|
|
31
|
+
this.compiledFunc = null;
|
|
32
|
+
|
|
33
|
+
this.propertyMapping.clear();
|
|
34
|
+
|
|
35
|
+
this.lastWarnedExpression = null;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
shouldInclude(event: { meta?: Record<string, unknown> }): boolean {
|
|
39
|
+
if (!this.currentValue) return true;
|
|
40
|
+
|
|
41
|
+
const frontmatter = event.meta || {};
|
|
42
|
+
|
|
43
|
+
try {
|
|
44
|
+
const currentKeys = new Set(Object.keys(frontmatter));
|
|
45
|
+
|
|
46
|
+
const existingKeys = new Set(this.propertyMapping.keys());
|
|
47
|
+
|
|
48
|
+
const newKeys = [...currentKeys].filter((key) => !existingKeys.has(key));
|
|
49
|
+
|
|
50
|
+
if (newKeys.length > 0) {
|
|
51
|
+
const allKeys = new Set([...existingKeys, ...currentKeys]);
|
|
52
|
+
|
|
53
|
+
this.propertyMapping = buildPropertyMapping(Array.from(allKeys));
|
|
54
|
+
|
|
55
|
+
this.compiledFunc = null;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
if (!this.compiledFunc) {
|
|
59
|
+
const sanitized = sanitizeExpression(this.currentValue, this.propertyMapping);
|
|
60
|
+
|
|
61
|
+
const params = Array.from(this.propertyMapping.values());
|
|
62
|
+
|
|
63
|
+
// eslint-disable-next-line @typescript-eslint/no-implied-eval -- Dynamic function creation for expression evaluation with sanitized input
|
|
64
|
+
this.compiledFunc = new Function(...params, `"use strict"; return ${sanitized};`) as (
|
|
65
|
+
...args: unknown[]
|
|
66
|
+
) => boolean;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const values = Array.from(this.propertyMapping.keys()).map(
|
|
70
|
+
(key) => frontmatter[key] ?? undefined
|
|
71
|
+
);
|
|
72
|
+
|
|
73
|
+
const result = this.compiledFunc(...values);
|
|
74
|
+
|
|
75
|
+
return result;
|
|
76
|
+
} catch (error) {
|
|
77
|
+
if (error instanceof ReferenceError) {
|
|
78
|
+
const hasInequality = this.currentValue.includes("!==") || this.currentValue.includes("!=");
|
|
79
|
+
|
|
80
|
+
return hasInequality;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
if (this.lastWarnedExpression !== this.currentValue) {
|
|
84
|
+
console.warn("Invalid filter expression:", this.currentValue, error);
|
|
85
|
+
|
|
86
|
+
this.lastWarnedExpression = this.currentValue;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
return false;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { InputManager } from "./base";
|
|
2
|
+
|
|
3
|
+
export class SearchFilterInputManager extends InputManager {
|
|
4
|
+
constructor(
|
|
5
|
+
parentEl: HTMLElement,
|
|
6
|
+
cssPrefix: string,
|
|
7
|
+
onFilterChange: () => void,
|
|
8
|
+
initiallyVisible: boolean = false,
|
|
9
|
+
placeholder: string = "Search ...",
|
|
10
|
+
onHide?: () => void,
|
|
11
|
+
debounceMs?: number
|
|
12
|
+
) {
|
|
13
|
+
super(parentEl, placeholder, cssPrefix, onFilterChange, initiallyVisible, onHide, debounceMs);
|
|
14
|
+
this.cssClass = `${cssPrefix}-search-input`;
|
|
15
|
+
if (this.inputEl) {
|
|
16
|
+
this.inputEl.className = this.cssClass;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
shouldInclude(value: string): boolean {
|
|
21
|
+
if (!this.currentValue) return true;
|
|
22
|
+
|
|
23
|
+
return value.toLowerCase().includes(this.currentValue.toLowerCase());
|
|
24
|
+
}
|
|
25
|
+
}
|
|
@@ -40,8 +40,17 @@ export abstract class BaseEvaluator<TRule extends BaseRule, TSettings> {
|
|
|
40
40
|
}
|
|
41
41
|
|
|
42
42
|
try {
|
|
43
|
-
|
|
44
|
-
|
|
43
|
+
// Progressively build property mapping as we encounter new properties
|
|
44
|
+
const currentKeys = new Set(Object.keys(frontmatter));
|
|
45
|
+
const existingKeys = new Set(this.propertyMapping.keys());
|
|
46
|
+
const newKeys = [...currentKeys].filter((key) => !existingKeys.has(key));
|
|
47
|
+
|
|
48
|
+
// If new properties are found, rebuild the mapping and invalidate compiled functions
|
|
49
|
+
if (newKeys.length > 0) {
|
|
50
|
+
const allKeys = new Set([...existingKeys, ...currentKeys]);
|
|
51
|
+
this.propertyMapping = buildPropertyMapping(Array.from(allKeys));
|
|
52
|
+
// Clear compiled functions since property mapping changed
|
|
53
|
+
this.compiledFunctions.clear();
|
|
45
54
|
}
|
|
46
55
|
|
|
47
56
|
let compiledFunc = this.compiledFunctions.get(rule.id);
|
|
@@ -55,7 +64,10 @@ export abstract class BaseEvaluator<TRule extends BaseRule, TSettings> {
|
|
|
55
64
|
this.compiledFunctions.set(rule.id, compiledFunc);
|
|
56
65
|
}
|
|
57
66
|
|
|
58
|
-
|
|
67
|
+
// Use undefined for missing properties instead of letting them be undefined implicitly
|
|
68
|
+
const values = Array.from(this.propertyMapping.keys()).map(
|
|
69
|
+
(key) => frontmatter[key] ?? undefined
|
|
70
|
+
);
|
|
59
71
|
const result = compiledFunc(...values);
|
|
60
72
|
|
|
61
73
|
return result === true;
|