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

Commit 821a5a7

Browse files
committed
Merge branch '65-fr-top-bar-icon-for-sorting'
2 parents 5aefd2e + fff51cf commit 821a5a7

23 files changed

+309
-22
lines changed

docs/docs/changelog.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
## 1.4.1
2+
### Visual
3+
- Text column style now justify the content of cells
14
## 1.4.0
25
*Published on 2022/05/25*
36
### Shiny new things

src/DatabaseView.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
import { DatabaseColumn } from "cdm/DatabaseModel";
2-
import { TableDataType } from "cdm/FolderModel";
2+
import { InitialState, TableDataType } from "cdm/FolderModel";
33
import {
44
obtainColumnsFromFolder,
55
obtainMetadataColumns,
66
} from "components/Columns";
77
import { createDatabase } from "components/index/Database";
88
import { DbFolderError } from "errors/AbstractError";
99
import { DatabaseCore, DataTypes, StyleClasses } from "helpers/Constants";
10+
import obtainInitialState from "helpers/InitialState";
1011
import { adapterTFilesToRows, isDatabaseNote } from "helpers/VaultManagement";
1112
import DBFolderPlugin from "main";
1213

@@ -133,6 +134,7 @@ export class DatabaseView extends TextFileView implements HoverParent {
133134
columns,
134135
this.diskConfig.yaml.filters
135136
);
137+
const initialState: InitialState = obtainInitialState(columns, rows);
136138
// Define table properties
137139
const tableProps: TableDataType = {
138140
columns: columns,
@@ -141,6 +143,7 @@ export class DatabaseView extends TextFileView implements HoverParent {
141143
skipReset: false,
142144
view: this,
143145
stateManager: this.plugin.getStateManager(this.file),
146+
initialState: initialState,
144147
};
145148
// Render database
146149
const table = createDatabase(tableProps);

src/cdm/DatabaseModel.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,3 +47,8 @@ export type CalendarProps = {
4747
column: TableColumn;
4848
cellProperties: Cell;
4949
};
50+
51+
export type SortedType = {
52+
id: string;
53+
desc: boolean;
54+
}

src/cdm/FolderModel.ts

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,8 @@ export interface BaseColumn {
5252
config: ConfigColumn;
5353
}
5454
export interface TableColumn extends BaseColumn {
55+
isSortedDesc?: boolean;
56+
isSorted?: boolean;
5557
id: string;
5658
minWidth?: number;
5759
width?: number;
@@ -68,14 +70,23 @@ export type RowDataType = {
6870
[key: string]: Literal | NoteInfo
6971
}
7072

73+
export type SortByElement = {
74+
id: string;
75+
desc: boolean;
76+
}
77+
export type InitialState = {
78+
sortBy?: SortByElement[],
79+
}
80+
7181
export type TableDataType = {
7282
columns: TableColumn[],
7383
shadowColumns: TableColumn[],
7484
data: Array<RowDataType>,
7585
skipReset: boolean,
7686
view: DatabaseView,
7787
stateManager: StateManager,
78-
dispatch?: Dispatch<any>
88+
dispatch?: Dispatch<any>,
89+
initialState?: InitialState,
7990
}
8091
export interface DatabaseHeaderProps {
8192
columns: any,

src/components/Cell.tsx

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,11 @@ import PopperSelectPortal from "components/portals/PopperSelectPortal";
77
import { CellContext } from "components/contexts/CellContext";
88
import { c } from "helpers/StylesHelper";
99
import CalendarPortal from "./portals/CalendarPortal";
10-
import { TableColumn } from "cdm/FolderModel";
10+
import { TableColumn, TableDataType } from "cdm/FolderModel";
1111
import CalendarTimePortal from "components/portals/CalendarTimePortal";
1212
import { renderMarkdown } from "components/markdown/MarkdownRenderer";
1313
import { DataviewService } from "services/DataviewService";
14+
import { CheckboxCell } from "./Checkbox";
1415

1516
export default function DefaultCell(cellProperties: Cell) {
1617
const dataDispatch = (cellProperties as any).dataDispatch;
@@ -135,7 +136,7 @@ export default function DefaultCell(cellProperties: Cell) {
135136
onChange={handleOnChange}
136137
onKeyDown={handleKeyDown}
137138
onBlur={handlerEditableOnBlur}
138-
className={"data-input"}
139+
className="data-input"
139140
ref={editableMdRef}
140141
/>
141142
) : (
@@ -198,7 +199,9 @@ export default function DefaultCell(cellProperties: Cell) {
198199
return (
199200
<CellContext.Provider value={{ contextValue, setContextValue }}>
200201
<CalendarPortal
201-
intialState={(cellProperties as any).initialState}
202+
intialState={
203+
(cellProperties as any).initialState as unknown as TableDataType
204+
}
202205
column={cellProperties.column as unknown as TableColumn}
203206
cellProperties={cellProperties}
204207
/>
@@ -208,6 +211,18 @@ export default function DefaultCell(cellProperties: Cell) {
208211
case DataTypes.TASK:
209212
return <div ref={taskRef} className="data-input"></div>;
210213

214+
case DataTypes.CHECKBOX:
215+
return (
216+
<CellContext.Provider value={{ contextValue, setContextValue }}>
217+
<CheckboxCell
218+
intialState={
219+
(cellProperties as any).initialState as unknown as TableDataType
220+
}
221+
column={cellProperties.column as unknown as TableColumn}
222+
cellProperties={cellProperties}
223+
/>
224+
</CellContext.Provider>
225+
);
211226
/** Default option */
212227
default:
213228
LOGGER.warn(`Unknown data type: ${dataType}`);

src/components/Checkbox.tsx

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import React, { useContext, useState } from "react";
2+
import { CellContext } from "components/contexts/CellContext";
3+
import { ActionTypes } from "helpers/Constants";
4+
import { TableColumn, TableDataType } from "cdm/FolderModel";
5+
import { Cell } from "react-table";
6+
import NoteInfo from "services/NoteInfo";
7+
8+
type CheckboxProps = {
9+
intialState: TableDataType;
10+
column: TableColumn;
11+
cellProperties: Cell;
12+
};
13+
export function CheckboxCell(props: CheckboxProps) {
14+
const { intialState, column, cellProperties } = props;
15+
const dataDispatch = (cellProperties as any).dataDispatch;
16+
/** Note info of current Cell */
17+
const note: NoteInfo = (cellProperties.row.original as any).note;
18+
/** state of cell value */
19+
const { contextValue, setContextValue } = useContext(CellContext);
20+
const [checked, setChecked] = useState(contextValue.value === 1);
21+
const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
22+
const newValue = event.target.checked ? 1 : 0;
23+
// save on disk
24+
dataDispatch({
25+
type: ActionTypes.UPDATE_CELL,
26+
file: note.getFile(),
27+
key: column.key,
28+
value: newValue,
29+
row: cellProperties.row,
30+
columnId: column.id,
31+
});
32+
setChecked(event.target.checked);
33+
setContextValue({ value: event.target.checked ? 1 : 0, update: true });
34+
};
35+
return (
36+
<div className="data-input-checkbox">
37+
<input type="checkbox" checked={checked} onChange={handleChange} />
38+
</div>
39+
);
40+
}

src/components/Header.tsx

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ import TextIcon from "components/img/Text";
44
import MultiIcon from "components/img/Multi";
55
import HashIcon from "components/img/Hash";
66
import PlusIcon from "components/img/Plus";
7+
import ArrowDown from "components/img/ArrowDown";
8+
import ArrowUp from "components/img/ArrowUp";
79
import HeaderMenu from "components/HeaderMenu";
810
import CalendarIcon from "components/img/CalendarIcon";
911
import MarkdownObsidian from "components/img/Markdown";
@@ -94,6 +96,7 @@ export default function Header(headerProps: DatabaseHeaderProps) {
9496
propertyIcon = <MarkdownObsidian />;
9597
break;
9698
case DataTypes.TASK:
99+
case DataTypes.CHECKBOX:
97100
propertyIcon = <TaskIcon />;
98101
break;
99102
default:
@@ -133,6 +136,18 @@ export default function Header(headerProps: DatabaseHeaderProps) {
133136
<span className="svg-icon svg-gray icon-margin">{propertyIcon}</span>
134137
{labelState}
135138
{headerProps.column.config.isInline && <span>*</span>}
139+
{/* Add a sort direction indicator */}
140+
<span className="svg-icon svg-gray icon-margin">
141+
{headerProps.column.isSorted ? (
142+
headerProps.column.isSortedDesc ? (
143+
<ArrowDown />
144+
) : (
145+
<ArrowUp />
146+
)
147+
) : (
148+
""
149+
)}
150+
</span>
136151
</div>
137152
{domReady
138153
? ReactDOM.createPortal(

src/components/HeaderMenu.tsx

Lines changed: 50 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,11 @@ import { Column } from "react-table";
2121
import { usePopper } from "react-popper";
2222
import { HeaderContext } from "components/contexts/HeaderContext";
2323
import { getColumnWidthStyle } from "components/styles/ColumnWidthStyle";
24+
import { generateSortedColumns } from "components/behavior/SortingColumns";
2425
import { ColumnModal } from "./modals/ColumnModal";
2526
import { HeaderMenuProps } from "cdm/HeaderModel";
2627
import TaskIcon from "components/img/TaskIcon";
28+
import CrossIcon from "components/img/CrossIcon";
2729

2830
const HeaderMenu = (headerMenuProps: HeaderMenuProps) => {
2931
/** state of width columns */
@@ -86,19 +88,47 @@ const HeaderMenu = (headerMenuProps: HeaderMenuProps) => {
8688
buttons.push(
8789
{
8890
onClick: (e: any) => {
89-
setSortBy([{ id: column.id, desc: false }]);
91+
const sortArray = generateSortedColumns(initialState, column, false);
92+
// Update state
93+
dispatch({
94+
type: ActionTypes.SET_SORT_BY,
95+
sortArray: sortArray,
96+
});
97+
setSortBy(sortArray);
9098
setExpanded(false);
9199
},
92-
icon: <ArrowUpIcon />,
93-
label: "Sort ascending",
100+
icon:
101+
column.isSorted && !column.isSortedDesc ? (
102+
<CrossIcon />
103+
) : (
104+
<ArrowUpIcon />
105+
),
106+
label:
107+
column.isSorted && !column.isSortedDesc
108+
? "Remove ascending sort"
109+
: "Sort ascending",
94110
},
95111
{
96112
onClick: (e: any) => {
97-
setSortBy([{ id: column.id, desc: true }]);
113+
const sortArray = generateSortedColumns(initialState, column, true);
114+
// Update state
115+
dispatch({
116+
type: ActionTypes.SET_SORT_BY,
117+
sortArray: sortArray,
118+
});
119+
setSortBy(sortArray);
98120
setExpanded(false);
99121
},
100-
icon: <ArrowDownIcon />,
101-
label: "Sort descending",
122+
icon:
123+
column.isSorted && column.isSortedDesc ? (
124+
<CrossIcon />
125+
) : (
126+
<ArrowDownIcon />
127+
),
128+
label:
129+
column.isSorted && column.isSortedDesc
130+
? "Remove descending sort"
131+
: "Sort descending",
102132
}
103133
);
104134
}
@@ -218,6 +248,20 @@ const HeaderMenu = (headerMenuProps: HeaderMenuProps) => {
218248
icon: <CalendarTimeIcon />,
219249
label: MetadataLabels.CALENDAR_TIME,
220250
},
251+
,
252+
{
253+
onClick: (e: any) => {
254+
dispatch({
255+
type: ActionTypes.UPDATE_COLUMN_TYPE,
256+
columnId: column.id,
257+
dataType: DataTypes.CHECKBOX,
258+
});
259+
setShowType(false);
260+
setExpanded(false);
261+
},
262+
icon: <TaskIcon />,
263+
label: DataTypes.CHECKBOX,
264+
},
221265
];
222266

223267
const typePopper = usePopper(typeReferenceElement, typePopperElement, {

src/components/Table.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ import { HeaderContext } from "components/contexts/HeaderContext";
2626
import { getDndListStyle, getDndItemStyle } from "./styles/DnDStyle";
2727

2828
const defaultColumn = {
29-
minWidth: 50,
29+
minWidth: 25,
3030
maxWidth: 400,
3131
Cell: DefaultCell,
3232
Header: Header,
@@ -92,6 +92,7 @@ export function Table(initialState: TableDataType) {
9292
dataDispatch,
9393
sortTypes,
9494
};
95+
propsUseTable.initialState = initialState.initialState;
9596
/** Obsidian event to show page preview */
9697
const onMouseOver = React.useCallback(
9798
(e: React.MouseEvent<HTMLDivElement, MouseEvent>) => {
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import { SortedType } from "cdm/DatabaseModel";
2+
import { TableColumn, TableDataType } from "cdm/FolderModel";
3+
import { LOGGER } from "services/Logger";
4+
5+
export function generateSortedColumns(tableDataType: TableDataType, currentCol: TableColumn, isSortedDesc: boolean): SortedType[] {
6+
LOGGER.debug(`=>generateSortedColumns currentCol ${currentCol.id} isSortedDesc ${isSortedDesc}`);
7+
const sortArray: SortedType[] = [];
8+
tableDataType.columns
9+
// Filter if col is already sorted or is current col
10+
.filter(col => col.id === currentCol.id || col.isSorted)
11+
.forEach((col) => {
12+
if (currentCol.id === col.id) {
13+
// When is current col
14+
if (currentCol.isSorted && currentCol.isSortedDesc === isSortedDesc) {
15+
// If sort direction is the same, remove sort
16+
tableDataType.view.diskConfig.updateColumnProperties(
17+
currentCol.id,
18+
{
19+
isSorted: false,
20+
}
21+
);
22+
} else {
23+
// If sort direction is different or not sorted, set sort
24+
sortArray.push({ id: currentCol.id, desc: isSortedDesc });
25+
tableDataType.view.diskConfig.updateColumnProperties(
26+
currentCol.id,
27+
{
28+
isSorted: true,
29+
isSortedDesc: isSortedDesc,
30+
}
31+
);
32+
}
33+
34+
} else {
35+
// When is not current col
36+
sortArray.push({ id: col.id, desc: col.isSortedDesc });
37+
}
38+
});
39+
LOGGER.debug(`<=generateSortedColumns`, `sortArray ${JSON.stringify(sortArray)}`);
40+
return sortArray;
41+
}

0 commit comments

Comments
 (0)