diff --git a/frontend/css/tree-table.css b/frontend/css/tree-table.css index 4acf08daf..1f16ad8d0 100644 --- a/frontend/css/tree-table.css +++ b/frontend/css/tree-table.css @@ -1,23 +1,202 @@ /* Collapsible trees * - * some of the shared styles are in `journal-table.css` + * some of the shared styles are in `journal-table.css`. + * + * The styles for .tree-table are not used in Fava anymore but are again provided + * for extensions for the near future. */ -.tree-table p { +.tree-table-new p { margin-top: -1px; } -.tree-table p > span { +.tree-table-new p > span { margin-right: -1px; border: 1px solid var(--table-border); } -.tree-table .num { +.tree-table-new .num { /** Space for about 14 characters, so a number like 1234512345.123 fits. */ width: 9em; } -.tree-table .other { +.tree-table-new .other { /** Space for about 18 (four more) characters, so an amount like 1234512345.123 USD fits. */ width: 12em; } + +.tree-table.fullwidth { + display: block; + max-width: 100%; + overflow-x: auto; +} + +.tree-table p { + margin-top: -1px; +} + +.tree-table p > span { + margin-right: -1px; + border: 1px solid var(--table-border); +} + +.tree-table .account-cell { + display: flex; + flex: 1; + align-items: center; + min-width: 14em; + max-width: 30em; +} + +.tree-table .account-cell.depth-1 { + min-width: 13em; + max-width: 29em; + margin-left: 1em; +} + +.tree-table .account-cell.depth-2 { + min-width: 12em; + max-width: 28em; + margin-left: 2em; +} + +.tree-table .account-cell.depth-3 { + min-width: 11em; + max-width: 27em; + margin-left: 3em; +} + +.tree-table .account-cell.depth-4 { + min-width: 10em; + max-width: 26em; + margin-left: 4em; +} + +.tree-table .account-cell.depth-5 { + min-width: 9em; + max-width: 25em; + margin-left: 5em; +} + +.tree-table .account-cell.depth-6 { + min-width: 8em; + max-width: 24em; + margin-left: 6em; +} + +.tree-table .account-cell.depth-7 { + min-width: 7em; + max-width: 23em; + margin-left: 7em; +} + +.tree-table .account-cell.depth-8 { + min-width: 6em; + max-width: 22em; + margin-left: 8em; +} + +.tree-table .account-cell.depth-9 { + min-width: 5em; + max-width: 21em; + margin-left: 9em; +} + +.tree-table .account-cell a { + margin-left: 1em; +} + +.tree-table .has-children { + cursor: pointer; +} + +.tree-table .has-children::before { + margin: 0 -10px 0 0; + content: ""; + border-top: 5px solid var(--treetable-expander); + border-right: 5px solid transparent; + border-left: 5px solid transparent; +} + +.tree-table .num { + width: 10em; +} + +.tree-table .num a { + display: block; + color: inherit; +} + +.tree-table .other { + width: 13em; +} + +.tree-table .other a { + display: block; + color: inherit; +} + +.tree-table .balance-children { + display: block; + opacity: 0.7; +} + +.tree-table .has-balance .balance { + display: block; +} + +.tree-table .has-balance .balance-children { + display: none; +} + +.tree-table .toggled ol { + display: none; +} + +.tree-table .toggled .balance { + display: none; +} + +.tree-table .toggled .balance-children { + display: block; + color: var(--text-color); +} + +.tree-table .toggled .has-children::before { + transform: rotate(270deg); +} + +.tree-table .expand-all { + margin-left: 15px; + font-weight: normal; + color: inherit; + opacity: 0.5; +} + +.tree-table .diff { + margin-right: 3px; + font-size: 0.9em; + color: var(--budget-zero); + white-space: nowrap; +} + +.tree-table .diff.negative { + color: var(--budget-negative); +} + +.tree-table .diff.positive { + color: var(--budget-positive); +} + +/* For two or more operating currencies, set a slightly smaller size. */ +.two-currencies { + font-size: 0.9em; +} + +.two-currencies .num { + width: 8em; +} + +.two-currencies .other { + width: 11em; +} diff --git a/frontend/src/main.ts b/frontend/src/main.ts index bf2692d48..4ba4808ff 100644 --- a/frontend/src/main.ts +++ b/frontend/src/main.ts @@ -45,6 +45,7 @@ import { SortableTable } from "./sort"; import { errors, fava_options, ledgerData } from "./stores"; import { ledger_mtime, read_mtime } from "./stores/mtime"; import { SvelteCustomElement } from "./svelte-custom-elements"; +import { TreeTableCustomElement } from "./tree-table/tree-table-custom-element"; /** * Define the custom elements that Fava uses. @@ -57,6 +58,9 @@ function defineCustomElements() { customElements.define("fava-journal", FavaJournal); customElements.define("sortable-table", SortableTable, { extends: "table" }); customElements.define("svelte-component", SvelteCustomElement); + + // for extension compatibility + customElements.define("tree-table", TreeTableCustomElement); } router.on("page-loaded", () => { diff --git a/frontend/src/tree-table/IntervalTreeTable.svelte b/frontend/src/tree-table/IntervalTreeTable.svelte index 444d6549c..e62799022 100644 --- a/frontend/src/tree-table/IntervalTreeTable.svelte +++ b/frontend/src/tree-table/IntervalTreeTable.svelte @@ -44,7 +44,7 @@ }); -
    +
    1. diff --git a/frontend/src/tree-table/TreeTable.svelte b/frontend/src/tree-table/TreeTable.svelte index 1f72da2cc..c00014169 100644 --- a/frontend/src/tree-table/TreeTable.svelte +++ b/frontend/src/tree-table/TreeTable.svelte @@ -23,7 +23,10 @@ $: $not_shown = $get_not_shown(tree, end); -

        1}> +
          1} +>
        1. diff --git a/frontend/src/tree-table/tree-table-custom-element.ts b/frontend/src/tree-table/tree-table-custom-element.ts new file mode 100644 index 000000000..f873529a8 --- /dev/null +++ b/frontend/src/tree-table/tree-table-custom-element.ts @@ -0,0 +1,53 @@ +import { delegate } from "../lib/events"; + +/** + * Account trees. + * + * This handles the toggling of accounts in the accounts trees. + * + * This is not used in Fava anymore but provided for extension compatibility. + */ +export class TreeTableCustomElement extends HTMLElement { + constructor() { + super(); + + const expandAllLink = this.querySelector(".expand-all"); + expandAllLink?.addEventListener("click", () => { + expandAllLink.classList.add("hidden"); + this.querySelectorAll(".toggled").forEach((el) => { + el.classList.remove("toggled"); + }); + }); + + delegate(this, "click", "span.has-children", (event: MouseEvent) => { + const { target } = event; + if ( + !(target instanceof HTMLElement) || + target instanceof HTMLAnchorElement + ) { + return; + } + const row = target.closest("li"); + if (!row) { + return; + } + const willShow = row.classList.contains("toggled"); + if (event.shiftKey) { + this.querySelectorAll("li").forEach((el) => { + el.classList.toggle("toggled", !willShow); + }); + } + if (event.ctrlKey || event.metaKey) { + this.querySelectorAll("li").forEach((el) => { + el.classList.toggle("toggled", willShow); + }); + } + row.classList.toggle("toggled"); + + expandAllLink?.classList.toggle( + "hidden", + !this.querySelectorAll(".toggled").length, + ); + }); + } +}