Skip to content
This repository was archived by the owner on Jul 28, 2025. It is now read-only.

Commit 094e47e

Browse files
committed
npm versions
1 parent 46a0708 commit 094e47e

File tree

11 files changed

+389
-232
lines changed

11 files changed

+389
-232
lines changed

package-lock.json

Lines changed: 230 additions & 178 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -23,25 +23,25 @@
2323
"@rollup/plugin-json": "5.0.2",
2424
"@rollup/plugin-node-resolve": "15.0.1",
2525
"@rollup/plugin-replace": "5.0.1",
26-
"@rollup/plugin-typescript": "10.0.0",
27-
"@rollup/plugin-terser": "0.1.0",
26+
"@rollup/plugin-typescript": "10.0.1",
27+
"@rollup/plugin-terser": "0.2.0",
2828
"@testing-library/jest-dom": "5.16.5",
2929
"@testing-library/react": "13.4.0",
30-
"@types/jest": "29.2.3",
30+
"@types/jest": "29.2.4",
3131
"@types/luxon": "3.1.0",
32-
"@types/node": "18.11.9",
33-
"@types/react": "18.0.25",
32+
"@types/node": "18.11.11",
33+
"@types/react": "18.0.26",
3434
"@types/react-csv": "1.1.3",
3535
"@types/react-datepicker": "4.8.0",
3636
"@types/react-dom": "18.0.9",
3737
"@types/react-window": "1.8.5",
38-
"@typescript-eslint/eslint-plugin": "5.45.0",
39-
"@typescript-eslint/parser": "5.45.0",
40-
"eslint": "8.28.0",
38+
"@typescript-eslint/eslint-plugin": "5.45.1",
39+
"@typescript-eslint/parser": "5.45.1",
40+
"eslint": "8.29.0",
4141
"jest": "29.3.1",
4242
"jest-mock-extended": "3.0.1",
4343
"obsidian": "0.16.3",
44-
"rollup": "3.5.0",
44+
"rollup": "3.6.0",
4545
"rollup-plugin-typescript2": "0.34.1",
4646
"ts-jest": "29.0.3",
4747
"tslib": "2.4.1",
@@ -50,7 +50,7 @@
5050
"dependencies": {
5151
"@emotion/styled": "11.10.5",
5252
"@mui/icons-material": "5.10.16",
53-
"@mui/material": "5.10.16",
53+
"@mui/material": "5.10.17",
5454
"@popperjs/core": "2.11.6",
5555
"@tanstack/match-sorter-utils": "8.7.0",
5656
"@tanstack/react-table": "8.7.0",
@@ -64,8 +64,8 @@
6464
"react-csv": "2.2.2",
6565
"react-datepicker": "4.8.0",
6666
"react-dom": "18.2.0",
67-
"react-select": "5.6.1",
67+
"react-select": "5.7.0",
6868
"react-window": "1.8.8",
69-
"zustand": "4.1.4"
69+
"zustand": "4.1.5"
7070
}
7171
}

src/automations/Footer.ts

Lines changed: 51 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -7,30 +7,41 @@ export default class Footer {
77

88
public dispatch(footerType: string, key: string): string {
99
let footerInfo: string;
10-
switch (footerType) {
11-
case FooterType.COUNT_UNIQUE:
12-
footerInfo = this.countUnique(key);
13-
break;
14-
case FooterType.COUNT_EMPTY:
15-
footerInfo = this.percentEmpty(key);
16-
break;
17-
case FooterType.COUNT_FILLED:
18-
footerInfo = this.percentFilled(key);
19-
break;
20-
case FooterType.SUM:
21-
footerInfo = this.sum(key);
22-
break;
23-
case FooterType.MIN:
24-
footerInfo = this.min(key);
25-
break;
26-
case FooterType.MAX:
27-
footerInfo = this.max(key);
28-
break;
29-
case FooterType.NONE:
30-
default:
31-
footerInfo = "";
10+
try {
11+
switch (footerType) {
12+
case FooterType.COUNT_UNIQUE:
13+
footerInfo = this.countUnique(key);
14+
break;
15+
case FooterType.COUNT_EMPTY:
16+
footerInfo = this.countEmpty(key);
17+
break;
18+
case FooterType.PERCENT_EMPTY:
19+
footerInfo = this.percentEmpty(key);
20+
break;
21+
case FooterType.COUNT_FILLED:
22+
footerInfo = this.countFilled(key);
23+
break;
24+
case FooterType.PERCENT_FILLED:
25+
footerInfo = this.percentFilled(key);
26+
break;
27+
case FooterType.SUM:
28+
footerInfo = this.sum(key);
29+
break;
30+
case FooterType.MIN:
31+
footerInfo = this.min(key);
32+
break;
33+
case FooterType.MAX:
34+
footerInfo = this.max(key);
35+
break;
36+
case FooterType.NONE:
37+
default:
38+
footerInfo = "";
39+
}
40+
} catch (e) {
41+
footerInfo = `Error: ${e.message}`;
42+
} finally {
43+
return footerInfo;
3244
}
33-
return footerInfo;
3445
}
3546

3647
public sum(key: string): string {
@@ -50,20 +61,32 @@ export default class Footer {
5061

5162
public countUnique(key: string): string {
5263
const uniqueValues = new Set();
53-
this.rows.forEach((row) => {
54-
uniqueValues.add(row.getValue(key));
55-
});
64+
this.rows
65+
.filter((row) => row.getValue(key) !== undefined)
66+
.forEach((row) => {
67+
uniqueValues.add(row.getValue(key));
68+
});
5669
return `Unique: ${uniqueValues.size}`;
5770
}
5871

72+
public countEmpty(key: string): string {
73+
const empty = this.rows.filter((row) => !row.getValue(key)).length;
74+
return `Empty: ${empty}`;
75+
}
76+
5977
public percentEmpty(key: string): string {
6078
const empty = this.rows.filter((row) => !row.getValue(key)).length;
61-
return `Empty: ${empty} (${(empty / this.rows.length * 100).toFixed(2)}%)`;
79+
return `Empty: ${(empty / this.rows.length * 100).toFixed(2)}%`;
80+
}
81+
82+
public countFilled(key: string): string {
83+
const filled = this.rows.filter((row) => row.getValue(key)).length;
84+
return `Filled: ${filled}`;
6285
}
6386

6487
public percentFilled(key: string): string {
6588
const filled = this.rows.filter((row) => row.getValue(key)).length;
66-
return `Filled: ${filled} (${(filled / this.rows.length * 100).toFixed(2)}%)`;
89+
return `Filled: ${(filled / this.rows.length * 100).toFixed(2)}%`;
6790
}
6891

6992
}

src/cdm/TableStateInterface.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { SortingState } from "@tanstack/react-table";
1+
import { Row, SortingState } from "@tanstack/react-table";
22
import { ConfigColumn, RowDataType, TableColumn } from "cdm/FolderModel";
33
import { FilterSettings, GlobalSettings, LocalSettings } from "cdm/SettingsModel";
44
import { DatabaseView } from "DatabaseView";
@@ -101,7 +101,8 @@ export interface AutomationState {
101101
info: {
102102
getFormula: (name: string) => unknown;
103103
runFormula: (input: string, row: RowDataType, dbbConfig: LocalSettings) => Literal;
104-
dispatchRollup: (configColumn: ConfigColumn, relation: Literal, dbbConfig: LocalSettings) => Literal;
104+
dispatchFooter: (column: TableColumn, rows: Row<RowDataType>[]) => Literal;
105+
dispatchRollup: (configColumn: ConfigColumn, relation: Literal, ddbbConfig: LocalSettings) => Literal;
105106
},
106107
actions: {
107108
loadFormulas: (ddbbConfig: LocalSettings) => Promise<void>;

src/components/DefaultFooter.tsx

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { FooterType } from "helpers/Constants";
44
import { c, getAlignmentClassname } from "helpers/StylesHelper";
55
import React, { MouseEventHandler, useState } from "react";
66
import { showFooterMenu } from "components/obsidianArq/commands";
7+
import { Literal } from "obsidian-dataview";
78

89
export default function DefaultFooter(headerProps: DatabaseHeaderProps) {
910
/** Properties of footer */
@@ -14,18 +15,25 @@ export default function DefaultFooter(headerProps: DatabaseHeaderProps) {
1415

1516
/** Column values */
1617
const tableColumn = header.column.columnDef as TableColumn;
18+
const formulaInfo = tableState.automations((state) => state.info);
1719
const [footerType, setFooterValue] = useState(tableColumn.config.footer_type);
1820
const { config } = tableColumn;
19-
20-
const footerInfo = new Footer(table.getRowModel().rows).dispatch(
21-
footerType,
22-
header.id
23-
);
24-
21+
let footerInfo: Literal = "";
22+
if (config.footer_type === FooterType.FORMULA) {
23+
footerInfo = formulaInfo.dispatchFooter(
24+
tableColumn,
25+
table.getRowModel().rows
26+
);
27+
} else {
28+
footerInfo = new Footer(table.getRowModel().rows).dispatch(
29+
footerType,
30+
header.id
31+
);
32+
}
2533
const handlerFooterOptions: MouseEventHandler<HTMLDivElement> = async (
2634
event: React.MouseEvent
2735
) => {
28-
showFooterMenu(
36+
await showFooterMenu(
2937
event.nativeEvent,
3038
tableColumn,
3139
columnActions,
@@ -45,7 +53,7 @@ export default function DefaultFooter(headerProps: DatabaseHeaderProps) {
4553
minHeight: "20px",
4654
}}
4755
>
48-
{footerInfo}
56+
{footerInfo?.toString()}
4957
</div>
5058
);
5159
}

src/components/modals/PromptModal.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { c } from "helpers/StylesHelper";
12
import {
23
Modal,
34
TextComponent,
@@ -30,11 +31,11 @@ export class PromptModal extends Modal {
3031

3132
createForm(): void {
3233
const div = this.contentEl.createDiv();
33-
div.addClass("templater-prompt-div");
34+
div.addClass(c("prompt-modal"));
3435
let textInput;
3536
textInput = new TextComponent(div);
3637
this.value = this.default_value ?? "";
37-
textInput.inputEl.addClass("templater-prompt-input");
38+
textInput.inputEl.addClass(c("prompt-input"));
3839
textInput.setPlaceholder("Type text here");
3940
textInput.setValue(this.value);
4041
textInput.onChange((value) => (this.value = value));

src/components/obsidianArq/commands.ts

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
import { Row } from "@tanstack/react-table";
22
import { RowDataType, TableColumn } from "cdm/FolderModel";
33
import { ColumnsState, DataState } from "cdm/TableStateInterface";
4+
import { PromptModal } from "components/modals/PromptModal";
45
import { FooterType, InputType } from "helpers/Constants";
56
import { t } from "lang/helpers";
6-
import { Menu, TFile } from "obsidian";
7+
import { Menu, Notice, TFile } from "obsidian";
78
import { Dispatch, SetStateAction } from "react";
89

910
/**
@@ -53,7 +54,7 @@ export function showFileMenu(file: TFile, event: MouseEvent, row: Row<RowDataTyp
5354
}
5455

5556

56-
export function showFooterMenu(
57+
export async function showFooterMenu(
5758
event: MouseEvent,
5859
tableColumn: TableColumn,
5960
columnActions: ColumnsState["actions"],
@@ -70,6 +71,16 @@ export function showFooterMenu(
7071
setFooterType(type);
7172
}
7273
};
74+
75+
const handleFOrmulaOption = async () => {
76+
77+
const prompt_filename = new PromptModal("Footer formula", tableColumn.config.footer_formula);
78+
await prompt_filename.openAndGetValue(
79+
(value) => {
80+
handleFooterOption(FooterType.FORMULA, value)();
81+
},
82+
() => { new Notice("Formula edition cancelled") });
83+
};
7384
footerMenu.addItem((item) => item
7485
.setTitle(t("footer_menu_none"))
7586
.onClick(handleFooterOption(FooterType.NONE)));
@@ -79,9 +90,18 @@ export function showFooterMenu(
7990
footerMenu.addItem((item) => item
8091
.setTitle(t("footer_menu_count_empty"))
8192
.onClick(handleFooterOption(FooterType.COUNT_EMPTY)));
93+
footerMenu.addItem((item) => item
94+
.setTitle(t("footer_menu_percent_empty"))
95+
.onClick(handleFooterOption(FooterType.PERCENT_EMPTY)));
8296
footerMenu.addItem((item) => item
8397
.setTitle(t("footer_menu_count_filled"))
8498
.onClick(handleFooterOption(FooterType.COUNT_FILLED)));
99+
footerMenu.addItem((item) => item
100+
.setTitle(t("footer_menu_percent_empty"))
101+
.onClick(handleFooterOption(FooterType.PERCENT_FILLED)));
102+
footerMenu.addItem((item) => item
103+
.setTitle(t("footer_menu_formula"))
104+
.onClick(handleFOrmulaOption));
85105
// Custom footer menu
86106
switch (tableColumn.input) {
87107
case InputType.NUMBER:

src/helpers/Constants.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,10 +84,13 @@ export const FooterType = Object.freeze({
8484
NONE: 'none',
8585
COUNT_UNIQUE: 'count_unique',
8686
COUNT_EMPTY: 'count_empty',
87+
PERCENT_EMPTY: 'percent_empty',
8788
COUNT_FILLED: 'count_filled',
89+
PERCENT_FILLED: 'percent_filled',
8890
SUM: 'sum',
8991
MIN: 'min',
9092
MAX: 'max',
93+
FORMULA: 'formula',
9194
});
9295

9396
/******************************************************************************

src/lang/locale/en.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,9 +37,12 @@ export default {
3737
"footer_menu_none": "None",
3838
"footer_menu_count_unique": "Count unique values",
3939
"footer_menu_count_empty": "Count empty values",
40+
"footer_menu_percent_empty": "Percent empty values",
4041
"footer_menu_count_filled": "Count filled values",
42+
"footer_menu_percent_filled": "Percent filled values",
4143
"footer_menu_average": "Average",
4244
"footer_menu_sum": "Sum",
4345
"footer_menu_min": "Min",
4446
"footer_menu_max": "Max",
47+
"footer_menu_formula": "Formula",
4548
};
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
import { Row } from "@tanstack/react-table";
2+
import Rollup from "automations/Rollup";
3+
import { ConfigColumn, RowDataType, TableColumn } from "cdm/FolderModel";
4+
import { LocalSettings } from "cdm/SettingsModel";
5+
import { AutomationState, TableActionResponse } from "cdm/TableStateInterface";
6+
import { Link, Literal } from "obsidian-dataview";
7+
import { DataviewService } from "services/DataviewService";
8+
import { LOGGER } from "services/Logger";
9+
import { AbstractTableAction } from "stateManagement/AbstractTableAction";
10+
11+
export default class DispatchFooterHandlerAcgion extends AbstractTableAction<AutomationState> {
12+
handle(tableActionResponse: TableActionResponse<AutomationState>): TableActionResponse<AutomationState> {
13+
const { implementation, get } = tableActionResponse;
14+
implementation.info.dispatchFooter = (column: TableColumn, rows: Row<RowDataType>[]) => {
15+
return this.proxyFooter(column, rows, get().formula);
16+
};
17+
18+
tableActionResponse.implementation = implementation;
19+
return this.goNext(tableActionResponse);
20+
}
21+
22+
private evalInput(column: TableColumn, rows: Row<RowDataType>[], db: {
23+
[key: string]: unknown;
24+
}): Literal {
25+
const input = column.config.footer_formula;
26+
const dynamicJS = 'return `' + input + '`';
27+
const func = new Function('column', 'rows', 'db', dynamicJS);
28+
const result = func(column, rows, db);
29+
if (result === "undefined" || result === "null") {
30+
return '';
31+
}
32+
return result;
33+
}
34+
35+
private proxyFooter(column: TableColumn, rows: Row<RowDataType>[], db: {
36+
[key: string]: unknown;
37+
}): Literal {
38+
try {
39+
return this.evalInput(column, rows, db);
40+
} catch (e) {
41+
LOGGER.error(`Error evaluating footer formula from column ${column.key}: `, e);
42+
return "";
43+
}
44+
}
45+
}

0 commit comments

Comments
 (0)