@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.
Files changed (35) hide show
  1. package/dist/components/index.d.ts +2 -0
  2. package/dist/components/index.d.ts.map +1 -0
  3. package/dist/components/index.js +2 -0
  4. package/dist/components/index.js.map +1 -0
  5. package/dist/components/input-managers/base.d.ts +30 -0
  6. package/dist/components/input-managers/base.d.ts.map +1 -0
  7. package/dist/components/input-managers/base.js +115 -0
  8. package/dist/components/input-managers/base.js.map +1 -0
  9. package/dist/components/input-managers/expression.d.ts +12 -0
  10. package/dist/components/input-managers/expression.d.ts.map +1 -0
  11. package/dist/components/input-managers/expression.js +56 -0
  12. package/dist/components/input-managers/expression.js.map +1 -0
  13. package/dist/components/input-managers/index.d.ts +4 -0
  14. package/dist/components/input-managers/index.d.ts.map +1 -0
  15. package/dist/components/input-managers/index.js +4 -0
  16. package/dist/components/input-managers/index.js.map +1 -0
  17. package/dist/components/input-managers/search.d.ts +6 -0
  18. package/dist/components/input-managers/search.d.ts.map +1 -0
  19. package/dist/components/input-managers/search.js +16 -0
  20. package/dist/components/input-managers/search.js.map +1 -0
  21. package/dist/core/evaluator/base.d.ts.map +1 -1
  22. package/dist/core/evaluator/base.js +12 -3
  23. package/dist/core/evaluator/base.js.map +1 -1
  24. package/dist/index.d.ts +1 -0
  25. package/dist/index.d.ts.map +1 -1
  26. package/dist/index.js +2 -0
  27. package/dist/index.js.map +1 -1
  28. package/package.json +1 -1
  29. package/src/components/index.ts +1 -0
  30. package/src/components/input-managers/base.ts +150 -0
  31. package/src/components/input-managers/expression.ts +92 -0
  32. package/src/components/input-managers/index.ts +3 -0
  33. package/src/components/input-managers/search.ts +25 -0
  34. package/src/core/evaluator/base.ts +15 -3
  35. package/src/index.ts +2 -0
@@ -0,0 +1,2 @@
1
+ export * from "./input-managers";
2
+ //# sourceMappingURL=index.d.ts.map
@@ -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,2 @@
1
+ export * from "./input-managers";
2
+ //# sourceMappingURL=index.js.map
@@ -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,4 @@
1
+ export * from "./base";
2
+ export * from "./expression";
3
+ export * from "./search";
4
+ //# sourceMappingURL=index.d.ts.map
@@ -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,4 @@
1
+ export * from "./base";
2
+ export * from "./expression";
3
+ export * from "./search";
4
+ //# sourceMappingURL=index.js.map
@@ -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;IA+BlF,SAAS,CAAC,QAAQ,CAAC,KAAK,EAAE,GAAG,GAAG,OAAO;CAGvC"}
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
- if (this.propertyMapping.size === 0) {
30
- this.propertyMapping = buildPropertyMapping(Object.keys(frontmatter));
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
- const values = Array.from(this.propertyMapping.keys()).map((key) => frontmatter[key]);
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,KAAK,CAAC,EAAE,CAAC;gBACrC,IAAI,CAAC,eAAe,GAAG,oBAAoB,CAAC,MAAM,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC,CAAC;YACvE,CAAC;YAED,IAAI,YAAY,GAAG,IAAI,CAAC,iBAAiB,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;YAEvD,IAAI,CAAC,YAAY,EAAE,CAAC;gBACnB,MAAM,SAAS,GAAG,kBAAkB,CAAC,IAAI,CAAC,UAAU,EAAE,IAAI,CAAC,eAAe,CAAC,CAAC;gBAC5E,MAAM,MAAM,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,eAAe,CAAC,MAAM,EAAE,CAAC,CAAC;gBACzD,YAAY,GAAG,IAAI,QAAQ,CAAC,GAAG,MAAM,EAAE,wBAAwB,SAAS,GAAG,CAE/D,CAAC;gBACb,IAAI,CAAC,iBAAiB,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,EAAE,YAAY,CAAC,CAAC;YACnD,CAAC;YAED,MAAM,MAAM,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,eAAe,CAAC,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC;YACtF,MAAM,MAAM,GAAG,YAAY,CAAC,GAAG,MAAM,CAAC,CAAC;YAEvC,OAAO,MAAM,KAAK,IAAI,CAAC;QACxB,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YAChB,OAAO,CAAC,IAAI,CAAC,uBAAuB,IAAI,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,UAAU,EAAE,KAAK,CAAC,CAAC;YACzE,OAAO,KAAK,CAAC;QACd,CAAC;IACF,CAAC;IAES,QAAQ,CAAC,KAAU;QAC5B,OAAO,KAAK,KAAK,IAAI,CAAC;IACvB,CAAC;CACD","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\tif (this.propertyMapping.size === 0) {\n\t\t\t\tthis.propertyMapping = buildPropertyMapping(Object.keys(frontmatter));\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\tconst values = Array.from(this.propertyMapping.keys()).map((key) => frontmatter[key]);\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"]}
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
@@ -1,4 +1,5 @@
1
1
  export * from "./async";
2
+ export * from "./components";
2
3
  export * from "./core";
3
4
  export * from "./date";
4
5
  export * from "./file";
@@ -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
@@ -1,6 +1,8 @@
1
1
  // Settings
2
2
  // Async utilities
3
3
  export * from "./async";
4
+ // Components
5
+ export * from "./components";
4
6
  // Core utilities
5
7
  export * from "./core";
6
8
  // Date operations
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@real1ty-obsidian-plugins/utils",
3
- "version": "2.12.0",
3
+ "version": "2.14.0",
4
4
  "description": "Shared utilities for Obsidian plugins",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -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,3 @@
1
+ export * from "./base";
2
+ export * from "./expression";
3
+ export * from "./search";
@@ -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
- if (this.propertyMapping.size === 0) {
44
- this.propertyMapping = buildPropertyMapping(Object.keys(frontmatter));
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
- const values = Array.from(this.propertyMapping.keys()).map((key) => frontmatter[key]);
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;
package/src/index.ts CHANGED
@@ -2,6 +2,8 @@
2
2
 
3
3
  // Async utilities
4
4
  export * from "./async";
5
+ // Components
6
+ export * from "./components";
5
7
  // Core utilities
6
8
  export * from "./core";
7
9