Skip to content
This repository was archived by the owner on Jul 18, 2025. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion jest.config.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
/** @type {import('ts-jest/dist/types').InitialOptionsTsJest} */
/** @type {import('ts-jest/dist/types').JestConfigWithTsJest} */
module.exports = {
preset: "ts-jest",
testEnvironment: "node",
Expand Down
10 changes: 10 additions & 0 deletions src/lib/stores/translations/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -293,6 +293,12 @@
"check": "Check",
"none": "None"
},
"tooltips": {
"checklist-items": "Checklist items",
"task-points": "Story points",
"checked-total": "Cards checked/total",
"task-points-total": "Story point total"
},
"unprioritized": "Unprioritized",
"include-fields": "Include fields",
"settings": {
Expand All @@ -308,6 +314,10 @@
"order-sync-field": {
"name": "Sync card order with field",
"description": "Field to store the position of cards in the board."
},
"points-field": {
"name": "Card story points field",
"description": "Use the selected field as the card story point value."
}
},
"note": {
Expand Down
18 changes: 18 additions & 0 deletions src/ui/components/Indicator/Indicator.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<script lang="ts">
import { Icon } from "obsidian-svelte";
export let icon: string = "";
export let tooltip: string = "";
</script>

<div class="task-indicator" aria-label={tooltip}>
<Icon name={icon} size="sm" />
<slot />
</div>

<style>
div.task-indicator {
display: flex;
gap: 4px;
align-items: center;
}
</style>
1 change: 1 addition & 0 deletions src/ui/views/Board/BoardView.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -436,6 +436,7 @@
)}
{columnWidth}
checkField={fields.find((field) => field.name === config?.checkField)?.name}
pointsField={fields.find((field) => field.name === config?.pointsField)?.name}
includeFields={fields.filter((field) => includeFields.includes(field.name))}
customHeader={fields.find((field) => field.name === customHeader)}
onRecordClick={handleRecordClick}
Expand Down
2 changes: 2 additions & 0 deletions src/ui/views/Board/components/Board/Board.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
export let onColumnPin: OnColumnPin;
export let validateStatusField: () => string;
export let checkField: string | undefined;
export let pointsField: string | undefined;
export let includeFields: DataField[];
export let customHeader: DataField | undefined;

Expand Down Expand Up @@ -83,6 +84,7 @@
records={column.records}
{onRecordClick}
{checkField}
{pointsField}
{onRecordCheck}
onRecordAdd={() => onRecordAdd(column.id)}
onDrop={(record, records, trigger) => {
Expand Down
7 changes: 7 additions & 0 deletions src/ui/views/Board/components/Board/BoardColumn.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
export let readonly: boolean;
export let richText: boolean;
export let checkField: string | undefined;
export let pointsField: string | undefined;
export let includeFields: DataField[];
export let customHeader: DataField | undefined;
export let pinned: boolean;
Expand All @@ -42,6 +43,9 @@

$: count = records.length;
$: checkedCount = records.filter((r) => r.values[checkField ?? ""]).length;
$: pointsCount = records.map((r) => r.values[pointsField ?? ""] as number)
.filter(Number.isFinite)
.reduce((sum, p) => sum + p, 0);

function onColumnMenu() {
const menu = new Menu();
Expand Down Expand Up @@ -109,10 +113,12 @@
value={name}
{count}
{checkedCount}
{pointsCount}
bind:editing
{richText}
{collapse}
{checkField}
{pointsField}
{onColumnMenu}
{onColumnRename}
{onValidate}
Expand All @@ -125,6 +131,7 @@
{customHeader}
{onRecordClick}
{checkField}
{pointsField}
{onRecordCheck}
{onDrop}
{includeFields}
Expand Down
30 changes: 29 additions & 1 deletion src/ui/views/Board/components/Board/CardList.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,18 @@
// import { Checkbox, InternalLink} from "obsidian-svelte";
import { Checkbox } from "obsidian-svelte";
import InternalLink from "src/ui/components/InternalLink.svelte";

import {
isString,
type DataField,
type DataRecord,
} from "src/lib/dataframe/dataframe";
import { app } from "src/lib/stores/obsidian";
import { settings } from "src/lib/stores/settings";
import { i18n } from "src/lib/stores/i18n";
import { get } from "svelte/store";
import CardMetadata from "src/ui/components/CardMetadata/CardMetadata.svelte";
import ColorItem from "src/ui/components/ColorItem/ColorItem.svelte";
import Indicator from "src/ui/components/Indicator/Indicator.svelte";
import {
getRecordColorContext,
handleHoverLink,
Expand All @@ -24,6 +26,7 @@
} from "svelte-dnd-action";
import { flip } from "svelte/animate";
import { getDisplayName } from "./boardHelpers";
import { getTaskProgress } from "./taskHelpers";
import type {
DropTrigger,
OnRecordClick,
Expand All @@ -37,6 +40,10 @@
export let onDrop: OnRecordDrop;
export let includeFields: DataField[];
export let checkField: string | undefined;
export let pointsField: string | undefined;
const getTaskPoints = (item: DataRecord) =>
pointsField ? (item.values[pointsField] as number) : null;

export let customHeader: DataField | undefined;
export let boardEditing: boolean;

Expand Down Expand Up @@ -88,6 +95,8 @@
>
{#each items as item (item.id)}
{@const color = getRecordColor(item)}
{@const taskPoints = getTaskPoints(item)}
{@const taskProgress = getTaskProgress(item.id)}

<article
class="projects--board--card"
Expand Down Expand Up @@ -139,6 +148,18 @@
{/if}
</div>
<CardMetadata fields={includeFields} record={item} />
<div class="task-indicators">
{#if taskProgress}
<Indicator icon="check-square" tooltip={get(i18n).t("views.board.tooltips.checklist-items")}>
{taskProgress}
</Indicator>
{/if}
{#if taskPoints}
<Indicator icon="weight" tooltip={get(i18n).t("views.board.tooltips.task-points")}>
{taskPoints}
</Indicator>
{/if}
</div>
</ColorItem>
</article>
{/each}
Expand All @@ -151,6 +172,13 @@
align-items: center;
}

div.task-indicators {
display: flex;
gap: 4px;
align-items: center;
gap: 10px;
}

.checkbox-wrapper {
display: flex;
flex-direction: column;
Expand Down
15 changes: 13 additions & 2 deletions src/ui/views/Board/components/Board/ColumnHeader.svelte
Original file line number Diff line number Diff line change
@@ -1,15 +1,19 @@
<script lang="ts">
import { MarkdownRenderer, Menu } from "obsidian";
import { app, view } from "src/lib/stores/obsidian";
import { i18n } from "src/lib/stores/i18n";
import { get } from "svelte/store";
import { getContext } from "svelte";
import { TextInput, IconButton } from "obsidian-svelte";
import { TextInput, IconButton, Icon } from "obsidian-svelte";
import { Flair } from "src/ui/components/Flair";
import { handleHoverLink } from "src/ui/views/helpers";

export let value: string;
export let count: number;
export let checkedCount: number;
export let checkField: string | undefined;
export let pointsCount: number;
export let pointsField: string | undefined;
export let collapse: boolean = false;
export let richText: boolean = false;
const sourcePath = getContext<string>("sourcePath") ?? "";
Expand Down Expand Up @@ -128,8 +132,15 @@
</span>
{/if}
<div>
{#if pointsField && pointsCount}
<Flair variant="primary" tooltip={get(i18n).t("views.board.tooltips.task-points-total")}>
<Icon name="weight" size="xs" --icon-color="var(--text-color)"/>
{pointsCount}
</Flair>
{/if}
{#if collapse || checkField}
<Flair variant="primary">
<Flair variant="primary" tooltip={get(i18n).t("views.board.tooltips.checked-total")}>
<Icon name="wallet-cards" size="xs" --icon-color="var(--text-color)"/>
{checkField ? `${checkedCount}/${count}` : count}
</Flair>
{/if}
Expand Down
17 changes: 17 additions & 0 deletions src/ui/views/Board/components/Board/taskHelpers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { app } from "src/lib/stores/obsidian";
import { get } from "svelte/store";

export function getTaskProgress(recordId: string): string {
let progress = "";

const totalTasks = get(app)
.metadataCache.getCache(recordId)
?.listItems?.filter((item) => item.task !== undefined);

if (totalTasks?.length) {
const completedTasks = totalTasks?.filter((item) => item.task !== " ");
progress = `${completedTasks.length}/${totalTasks.length}`;
}

return progress;
}
22 changes: 19 additions & 3 deletions src/ui/views/Board/settings/BoardSettings.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,10 @@
let columnWidthValue = config.columnWidth ?? null;

$: headerField = config.headerField ?? "";

$: orderSyncField = config.orderSyncField ?? "";
$: validOrderSyncFields = getFieldsByType(fields, DataFieldType.Number);
$: pointsField = config.pointsField ?? "";

$: numberFields = getFieldsByType(fields, DataFieldType.Number);

const updateConfig = <T extends keyof BoardConfig>(
key: T,
Expand Down Expand Up @@ -69,7 +70,7 @@
>
<Select
value={orderSyncField ?? ""}
options={validOrderSyncFields.map(fieldToSelectableValue)}
options={numberFields.map(fieldToSelectableValue)}
placeholder={$i18n.t("views.board.fields.none") ?? ""}
allowEmpty
on:change={(event) => {
Expand All @@ -78,5 +79,20 @@
}}
/>
</SettingItem>
<SettingItem
name={$i18n.t("views.board.settings.points-field.name")}
description={$i18n.t("views.board.settings.points-field.description")}
>
<Select
value={pointsField ?? ""}
options={numberFields.map(fieldToSelectableValue)}
placeholder={$i18n.t("views.board.fields.none") ?? ""}
allowEmpty
on:change={(event) => {
pointsField = event.detail;
updateConfig("pointsField", pointsField);
}}
/>
</SettingItem>
</ModalContent>
</ModalLayout>
1 change: 1 addition & 0 deletions src/ui/views/Board/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ export interface BoardConfig {
readonly checkField?: string;
readonly headerField?: string;
readonly orderSyncField?: string;
readonly pointsField?: string;
readonly columnWidth?: number;
readonly columns?: ColumnSettings;
readonly includeFields?: string[];
Expand Down