feat(drag-reorder): make sort_order property name configurable via settings

The drag-to-reorder feature hardcoded "sort_order" as the frontmatter
property name. This wires it through the existing FieldMapping system
so users can customise it in Settings > Task Properties, just like
every other property.

- sortOrderUtils: replace 3 hardcoded "sort_order" references with
  plugin.settings.fieldMapping.sortOrder
- isSortOrderInSortConfig: accept sortOrderField param so the sort
  config check uses the mapped name
- TaskListView / KanbanView: pass the mapped field name to all
  isSortOrderInSortConfig calls
- taskPropertiesTab: add Sort Order property card in Metadata section
- en.ts: add translation keys for the new settings card

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
ac8318740 2026-02-17 03:02:39 +00:00 committed by callumalpass
parent a18f58d3e4
commit 26fab9ec98
5 changed files with 23 additions and 10 deletions

View file

@ -1904,7 +1904,7 @@ export class KanbanView extends BasesViewBase {
// Compute and write sort_order if we have a drop position
// and the view's sort config includes sort_order
const hasSortOrder = isSortOrderInSortConfig(this.dataAdapter);
const hasSortOrder = isSortOrderInSortConfig(this.dataAdapter, this.plugin.settings.fieldMapping.sortOrder);
if (dropTarget && hasSortOrder) {
const cleanGroupByForSort = stripPropertyPrefix(groupByPropertyId);
for (const path of pathsToUpdate) {

View file

@ -504,7 +504,7 @@ export class TaskListView extends BasesViewBase {
const card = createTaskCard(taskInfo, this.plugin, visibleProperties, cardOptions);
// Attach drag handlers for sort_order reordering
if (isSortOrderInSortConfig(this.dataAdapter)) {
if (isSortOrderInSortConfig(this.dataAdapter, this.plugin.settings.fieldMapping.sortOrder)) {
card.setAttribute("draggable", "true");
this.setupCardDragHandlers(card, taskInfo, null);
}
@ -570,7 +570,7 @@ export class TaskListView extends BasesViewBase {
}
// Attach drag handlers when the card was (re)created
if (needsUpdate && isSortOrderInSortConfig(this.dataAdapter)) {
if (needsUpdate && isSortOrderInSortConfig(this.dataAdapter, this.plugin.settings.fieldMapping.sortOrder)) {
cardEl!.setAttribute("draggable", "true");
this.setupCardDragHandlers(cardEl!, taskInfo, null);
}
@ -851,7 +851,7 @@ export class TaskListView extends BasesViewBase {
} else {
const cardEl = createTaskCard(item.task, this.plugin, visibleProperties, cardOptions);
// Attach drag handlers for sort_order reordering
if (isSortOrderInSortConfig(this.dataAdapter)) {
if (isSortOrderInSortConfig(this.dataAdapter, this.plugin.settings.fieldMapping.sortOrder)) {
cardEl.setAttribute("draggable", "true");
this.setupCardDragHandlers(cardEl, item.task, item.groupKey);
}
@ -898,7 +898,7 @@ export class TaskListView extends BasesViewBase {
this.itemsContainer!.appendChild(headerEl);
} else {
const cardEl = createTaskCard(item.task, this.plugin, visibleProperties, cardOptions);
if (isSortOrderInSortConfig(this.dataAdapter)) {
if (isSortOrderInSortConfig(this.dataAdapter, this.plugin.settings.fieldMapping.sortOrder)) {
cardEl.setAttribute("draggable", "true");
this.setupCardDragHandlers(cardEl, item.task, item.groupKey);
}

View file

@ -23,7 +23,7 @@ export function stripPropertyPrefix(propertyId: string): string {
* Check whether `sort_order` is present in the view's base sort configuration.
* Drag-to-reorder should only activate when this returns true.
*/
export function isSortOrderInSortConfig(dataAdapter: any): boolean {
export function isSortOrderInSortConfig(dataAdapter: any, sortOrderField: string): boolean {
try {
const sortConfig = dataAdapter.getSortConfig();
if (!sortConfig) return false;
@ -36,7 +36,7 @@ export function isSortOrderInSortConfig(dataAdapter: any): boolean {
// Check all possible keys the sort property might be under
const candidate = s.property || s.column || s.field || s.id || s.name || "";
const clean = String(candidate).replace(/^(note\.|file\.|task\.)/, "");
return clean === "sort_order";
return clean === sortOrderField;
});
} catch (e) {
return false;
@ -97,8 +97,9 @@ export function getGroupTasks(
// When groupKey is null, include ALL tasks (flat/ungrouped mode)
// Read sort_order
const sortOrder = fm["sort_order"] !== undefined
? (typeof fm["sort_order"] === "number" ? fm["sort_order"] : Number(fm["sort_order"]))
const sortOrderField = plugin.settings.fieldMapping.sortOrder;
const sortOrder = fm[sortOrderField] !== undefined
? (typeof fm[sortOrderField] === "number" ? fm[sortOrderField] : Number(fm[sortOrderField]))
: undefined;
// Use cached TaskInfo if available, otherwise create minimal object
@ -179,8 +180,9 @@ async function renumberAndInsert(
for (const w of writes) {
const file = plugin.app.vault.getAbstractFileByPath(w.path);
if (file && file instanceof TFile) {
const sortOrderField = plugin.settings.fieldMapping.sortOrder;
await plugin.app.fileManager.processFrontMatter(file, (frontmatter) => {
frontmatter["sort_order"] = w.order;
frontmatter[sortOrderField] = w.order;
});
}
}

View file

@ -985,6 +985,11 @@ export const en: TranslationTree = {
description:
"Links to tasks that must be completed before this one. Stored as wikilinks. Blocked tasks display a visual indicator.",
},
sortOrder: {
name: "Sort Order",
description:
"Frontmatter property used for manual task ordering via drag-and-drop.",
},
pomodoros: {
name: "Pomodoros",
description:
@ -1144,6 +1149,7 @@ export const en: TranslationTree = {
timeEntries: "Time entries",
completeInstances: "Complete instances",
blockedBy: "Blocked by",
sortOrder: "Sort order",
pomodoros: "Pomodoros",
icsEventId: "ICS Event ID",
icsEventTag: "ICS Event Tag",

View file

@ -207,6 +207,11 @@ export function renderTaskPropertiesTab(
translate("settings.taskProperties.properties.blockedBy.name"),
translate("settings.taskProperties.properties.blockedBy.description"));
// Sort Order Property Card
renderMetadataPropertyCard(container, plugin, save, translate, "sortOrder",
translate("settings.taskProperties.properties.sortOrder.name"),
translate("settings.taskProperties.properties.sortOrder.description"));
// ===== FEATURE PROPERTIES SECTION =====
createSectionHeader(container, translate("settings.taskProperties.sections.featureProperties"));
createHelpText(container, translate("settings.taskProperties.sections.featurePropertiesDesc"));