diff --git a/README.md b/README.md index 1f2bd6a..81a993a 100644 --- a/README.md +++ b/README.md @@ -85,6 +85,20 @@ display: Visual examples and more info: https://github.com/joplin/plugin-kanban/pull/19 +### Sort + +By default, the kanban plugin sorts each column based on the user's dragging and dropping of notes across the kanban board, with new notes going at the top. To specify a fixed sort pattern based on note properties instead, use the following config: + +```yaml +```kanban +sort: + by: title +``` + +Descending sort order may be specified by prefixing `-`, e.g., `-title`. + +The configured sort order will apply to all columns. + ## Further information If you want to know more about the workings of the plugin or its development check out the [original proposal](https://discourse.joplinapp.org/t/kanban-board-project/17469) and the [progress reports](https://discourse.joplinapp.org/t/kanban-board-project/17469) diff --git a/src/board.ts b/src/board.ts index 8d17498..52c5b9f 100644 --- a/src/board.ts +++ b/src/board.ts @@ -213,6 +213,7 @@ export default class Board { * i.e. sorted columns, messages, and hidden tags. */ async getBoardState(allNotes: NoteData[]): Promise { + const config = this.parsedConfig; const state: BoardState = { name: this.boardName, messages: [], @@ -231,8 +232,28 @@ export default class Board { sortedNotes ).map(([name, notes]) => ({ name, notes })); + let sortCol = config ? config.sort?.by : undefined; + let sortDir = 1; + if (sortCol !== undefined) { + if (sortCol.startsWith('-')) { + sortDir = -1; + sortCol = sortCol.substring(1); + } + } Object.values(sortedColumns).forEach((col) => col.notes.sort((a, b) => { + if (sortCol !== undefined) { + const va = (a as any)[sortCol]; + const vb = (b as any)[sortCol]; + const v = ( + (typeof va === "string") + ? va.toLocaleLowerCase().localeCompare(vb.toLocaleLowerCase()) + : va - vb + ); + return v * sortDir; + } + + // Otherwise, use user-order specified on Kanban board if (a.order < b.order) return +1; if (a.order > b.order) return -1; return a.createdTime < b.createdTime ? +1 : -1; diff --git a/src/parser.ts b/src/parser.ts index a12ceb4..c2f563b 100644 --- a/src/parser.ts +++ b/src/parser.ts @@ -61,6 +61,15 @@ export const validateConfig = (config: Config | {} | null): Message | null => { } } + if ("sort" in config) { + if (typeof config.sort !== "object" || config.sort.by === undefined) + return configErr("Sort must be a dictionary with a single 'by' field"); + let cs = config.sort.by; + if (cs.startsWith('-')) cs = cs.substring(1); + if (['createdTime', 'title'].indexOf(cs) === -1) + return configErr("Sort must be one of 'createdTime', 'title'; optionally prefix by '-' for descending order"); + } + for (const col of config.columns) { if (typeof col !== "object" || Array.isArray(col)) return configErr( diff --git a/src/types.ts b/src/types.ts index 0fab0f7..f9f5a14 100644 --- a/src/types.ts +++ b/src/types.ts @@ -25,6 +25,9 @@ export interface Config { [ruleName: string]: RuleValue; rootNotebookPath?: string; }; + sort: { + by?: string; + }; columns: { [ruleName: string]: RuleValue; name: string;