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

Commit 7903970

Browse files
committed
Merge branch '26-fr-metadata-columns-of-created-and-updated'
2 parents 01afa2f + 73dd398 commit 7903970

27 files changed

+798
-278
lines changed

README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,3 +25,7 @@ The information you add or edit will be saved into the target obsidian note.
2525
### React UI
2626
- [react-table](https://github.com/TanStack/react-table)
2727
- [Notion Style base](https://github.com/archit-p/editable-react-table)
28+
29+
## Support
30+
If you enjoy dbfolder, consider [buy me a coffee](https://www.buymeacoffee.com/5tsytn22v9Z)
31+
[!["Buy Me A Coffee"](https://www.buymeacoffee.com/assets/img/custom_images/orange_img.png)](https://www.buymeacoffee.com/5tsytn22v9Z)

package.json

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -22,24 +22,25 @@
2222
"@rollup/plugin-json": "4.1.0",
2323
"@rollup/plugin-node-resolve": "13.3.0",
2424
"@rollup/plugin-typescript": "8.3.2",
25-
"@types/jest": "27.5.0",
26-
"@types/faker": "6.6.9",
25+
"@types/jest": "27.5.1",
2726
"@testing-library/jest-dom": "5.16.4",
2827
"@testing-library/react": "12.1.2",
2928
"@types/node": "17.0.31",
3029
"@types/react": "17.0.43",
3130
"@types/react-dom": "17.0.14",
3231
"@types/react-table": "7.7.11",
3332
"@types/react-window": "1.8.5",
33+
"@types/react-calendar": "3.5.0",
3434
"@types/react-beautiful-dnd": "13.1.2",
3535
"@types/react-csv": "1.1.2",
36-
"@typescript-eslint/eslint-plugin": "5.22.0",
37-
"@typescript-eslint/parser": "5.22.0",
38-
"eslint": "8.14.0",
36+
"@types/luxon": "2.3.2",
37+
"@typescript-eslint/eslint-plugin": "5.23.0",
38+
"@typescript-eslint/parser": "5.23.0",
39+
"eslint": "8.15.0",
3940
"jest": "27.5.1",
40-
"jest-mock-extended": "2.0.5",
41+
"jest-mock-extended": "2.0.6",
4142
"obsidian": "0.14.6",
42-
"obsidian-dataview": "0.5.18",
43+
"obsidian-dataview": "0.5.20",
4344
"rollup": "2.72.0",
4445
"ts-jest": "27.1.4",
4546
"tslib": "2.4.0",
@@ -61,6 +62,8 @@
6162
"react-popper": "2.3.0",
6263
"react-table": "7.7.0",
6364
"react-window": "1.8.7",
64-
"regenerator-runtime": "0.13.9"
65+
"react-calendar": "3.7.0",
66+
"regenerator-runtime": "0.13.9",
67+
"luxon": "2.4.0"
6568
}
6669
}

src/DatabaseView.tsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,11 +122,15 @@ export class DatabaseView extends TextFileView implements HoverParent {
122122
let yamlColumns: Record<string, DatabaseColumn> =
123123
this.diskConfig.yaml.columns;
124124
// Complete the columns with the metadata columns
125-
yamlColumns = await obtainMetadataColumns(yamlColumns);
125+
yamlColumns = await obtainMetadataColumns(
126+
yamlColumns,
127+
this.diskConfig.yaml.config
128+
);
126129
// Obtain base information about columns
127130
const columns = await obtainColumnsFromFolder(yamlColumns);
128131
const rows = await adapterTFilesToRows(
129132
this.file.parent.path,
133+
columns,
130134
this.diskConfig.yaml.filters
131135
);
132136
// Define table properties

src/Settings.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,10 @@ export interface LocalSettings {
1818
enable_show_state: boolean;
1919
group_folder_column: string;
2020
remove_field_when_delete_column: boolean;
21+
show_metadata_created: boolean;
22+
show_metadata_modified: boolean;
2123
}
24+
2225
export interface DatabaseSettings {
2326
global_settings: GlobalSettings;
2427
local_settings: LocalSettings;
@@ -32,7 +35,9 @@ export const DEFAULT_SETTINGS: DatabaseSettings = {
3235
local_settings: {
3336
enable_show_state: false,
3437
remove_field_when_delete_column: false,
35-
group_folder_column: ''
38+
group_folder_column: '',
39+
show_metadata_created: false,
40+
show_metadata_modified: false
3641
}
3742
};
3843

src/__tests__/IO/columns.test.ts

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,25 @@ describe("Columns", () => {
55
/** Metadata columns */
66
test('obtainMetadataColumns()', async () => {
77
// Check if mandatory columns exists
8-
const columnsRecord = await obtainMetadataColumns(generateYamlColumns(5));
8+
const columnsRecord = await obtainMetadataColumns(generateYamlColumns(5), {
9+
enable_show_state: false,
10+
remove_field_when_delete_column: false,
11+
group_folder_column: '',
12+
show_metadata_created: false,
13+
show_metadata_modified: false
14+
});
915
expect(columnsRecord).toHaveProperty(MetadataColumns.FILE);
1016
expect(columnsRecord).toHaveProperty(MetadataColumns.ADD_COLUMN);
1117
});
1218
/** Table columns */
1319
test('obtainColumnsFromFolder()', async () => {
14-
const columnsRecord = await obtainMetadataColumns(generateYamlColumns(5));
20+
const columnsRecord = await obtainMetadataColumns(generateYamlColumns(5), {
21+
enable_show_state: false,
22+
remove_field_when_delete_column: false,
23+
group_folder_column: '',
24+
show_metadata_created: false,
25+
show_metadata_modified: false
26+
});
1527
const tableColumns = await obtainColumnsFromFolder(columnsRecord);
1628
expect(tableColumns.length >= 5).toBeTruthy();
1729
});

src/cdm/FolderModel.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import NoteInfo from "services/NoteInfo";
77
import { TFile } from "obsidian";
88
import { DatabaseColumn } from "cdm/DatabaseModel";
99
import { Column } from "react-table";
10+
import { Literal } from "obsidian-dataview/lib/data-model/value";
1011

1112
export type Group = Parameter | Parameters | FolderModel | Models;
1213
type Parameter = {
@@ -42,7 +43,7 @@ export interface TableColumn {
4243
accessor: any;
4344
minWidth?: number;
4445
width?: number;
45-
dataType?: string;
46+
dataType: string;
4647
options?: RowSelectOption[];
4748
Cell?: any;
4849
getHeaderProps?: any;
@@ -56,7 +57,7 @@ export interface TableColumn {
5657
export type RowDataType = {
5758
id: number,
5859
note: NoteInfo,
59-
[key: string]: RowType
60+
[key: string]: Literal | NoteInfo
6061
}
6162

6263
export type TableDataType = {

src/components/Cell.tsx

Lines changed: 28 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ import NoteInfo from "services/NoteInfo";
88
import PopperSelectPortal from "components/portals/PopperSelectPortal";
99
import { CellContext } from "components/contexts/CellContext";
1010
import { c } from "helpers/StylesHelper";
11+
import CalendarPortal from "./portals/CalendarPortal";
12+
import { TableColumn } from "cdm/FolderModel";
1113

1214
/**
1315
* Obtain the path of the file inside cellValue
@@ -36,12 +38,15 @@ export default function DefaultCell(cellProperties: Cell) {
3638
/** Note info of current Cell */
3739
const note: NoteInfo = (cellProperties.row.original as any).note;
3840
/** state of cell value */
39-
const [value, setValue] = useState({ value: initialValue, update: false });
41+
const [contextValue, setContextValue] = useState({
42+
value: initialValue,
43+
update: false,
44+
});
4045
/** state for keeping the timeout to trigger the editior */
4146
const [editNoteTimeout, setEditNoteTimeout] = useState(null);
4247
/** states for selector option */
4348
LOGGER.debug(
44-
`<=> Cell.rendering dataType: ${dataType}. value: ${value.value}`
49+
`<=> Cell.rendering dataType: ${dataType}. value: ${contextValue.value}`
4550
);
4651

4752
const handleKeyDown = (event: any) => {
@@ -56,7 +61,7 @@ export default function DefaultCell(cellProperties: Cell) {
5661
clearTimeout(editNoteTimeout);
5762
}
5863
// first update the input text as user type
59-
setValue({ value: event.target.value, update: false });
64+
setContextValue({ value: event.target.value, update: false });
6065
// initialize a setimeout by wrapping in our editNoteTimeout so that we can clear it out using clearTimeout
6166
setEditNoteTimeout(
6267
setTimeout(() => {
@@ -82,13 +87,15 @@ export default function DefaultCell(cellProperties: Cell) {
8287
switch (dataType) {
8388
/** Plain text option */
8489
case DataTypes.TEXT:
85-
return (
90+
return (cellProperties.column as any).isMetadata ? (
91+
<span className="data-input">{contextValue.value.toString()}</span>
92+
) : (
8693
<ContentEditable
87-
html={(value.value && value.value.toString()) || ""}
94+
html={(contextValue.value && contextValue.value.toString()) || ""}
8895
onChange={handleOnChange}
8996
onKeyDown={handleKeyDown}
9097
onBlur={() =>
91-
setValue((old) => ({ value: old.value, update: true }))
98+
setContextValue((old) => ({ value: old.value, update: true }))
9299
}
93100
className="data-input"
94101
/>
@@ -97,10 +104,10 @@ export default function DefaultCell(cellProperties: Cell) {
97104
case DataTypes.NUMBER:
98105
return (
99106
<ContentEditable
100-
html={(value.value && value.value.toString()) || ""}
107+
html={(contextValue.value && contextValue.value.toString()) || ""}
101108
onChange={handleOnChange}
102109
onBlur={() =>
103-
setValue((old) => ({ value: old.value, update: true }))
110+
setContextValue((old) => ({ value: old.value, update: true }))
104111
}
105112
className="data-input text-align-right"
106113
/>
@@ -119,11 +126,11 @@ export default function DefaultCell(cellProperties: Cell) {
119126
);
120127
});
121128

122-
return <span ref={containerRef} className={`${c("md_cell")}`} ></span>;
129+
return <span ref={containerRef} className={`${c("md_cell")}`}></span>;
123130
/** Selector option */
124131
case DataTypes.SELECT:
125132
return (
126-
<CellContext.Provider value={{ value, setValue }}>
133+
<CellContext.Provider value={{ contextValue, setContextValue }}>
127134
<PopperSelectPortal
128135
dispatch={dataDispatch}
129136
row={cellProperties.row}
@@ -134,7 +141,17 @@ export default function DefaultCell(cellProperties: Cell) {
134141
/>
135142
</CellContext.Provider>
136143
);
137-
144+
/** Calendar option */
145+
case DataTypes.CALENDAR:
146+
return (
147+
<CellContext.Provider value={{ contextValue, setContextValue }}>
148+
<CalendarPortal
149+
intialState={(cellProperties as any).initialState}
150+
column={cellProperties.column as unknown as TableColumn}
151+
cellProperties={cellProperties}
152+
/>
153+
</CellContext.Provider>
154+
);
138155
/** Default option */
139156
default:
140157
LOGGER.warn(`Unknown data type: ${dataType}`);

src/components/Columns.tsx

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,20 +8,43 @@ import { LOGGER } from "services/Logger";
88
import { DatabaseColumn } from "cdm/DatabaseModel";
99
import { RowSelectOption } from "cdm/RowSelectModel";
1010
import { dbTrim } from "helpers/StylesHelper";
11+
import { LocalSettings } from "Settings";
1112

1213
/**
1314
* Add mandatory columns to the table
1415
* @param columns
1516
* @returns
1617
*/
1718
export async function obtainMetadataColumns(
18-
yamlColumns: Record<string, DatabaseColumn>
19+
yamlColumns: Record<string, DatabaseColumn>,
20+
localSetting: LocalSettings
1921
): Promise<Record<string, DatabaseColumn>> {
2022
// If File is not already in the table, add it
2123
yamlColumns[MetadataColumns.FILE] = {
2224
...MetadataDatabaseColumns.FILE,
2325
...(yamlColumns[MetadataColumns.FILE] ?? {}),
2426
};
27+
28+
if (localSetting.show_metadata_created) {
29+
// If Created is not already in the table, add it
30+
yamlColumns[MetadataColumns.CREATED] = {
31+
...MetadataDatabaseColumns.CREATED,
32+
...(yamlColumns[MetadataColumns.CREATED] ?? {}),
33+
};
34+
} else {
35+
delete yamlColumns[MetadataColumns.CREATED];
36+
}
37+
38+
if (localSetting.show_metadata_modified) {
39+
// If Modified is not already in the table, add it
40+
yamlColumns[MetadataColumns.MODIFIED] = {
41+
...MetadataDatabaseColumns.MODIFIED,
42+
...(yamlColumns[MetadataColumns.MODIFIED] ?? {}),
43+
};
44+
} else {
45+
delete yamlColumns[MetadataColumns.MODIFIED];
46+
}
47+
2548
yamlColumns[MetadataColumns.ADD_COLUMN] = MetadataDatabaseColumns.ADD_COLUMN;
2649
return yamlColumns;
2750
}
@@ -53,6 +76,7 @@ function parseDatabaseToTableColumn(
5376
const tableColumn: TableColumn = {
5477
...(databaseColumn as Partial<TableColumn>),
5578
id: columnKey,
79+
dataType: databaseColumn.input,
5680
position: databaseColumn.position ?? index,
5781
key: databaseColumn.key ?? columnKey,
5882
label: databaseColumn.label,
@@ -134,13 +158,23 @@ async function columnOptions(
134158
};
135159
}
136160

161+
function isCalendar(): TableColumn {
162+
LOGGER.debug(`<= columnOptions`, `return calendar column`);
163+
return {
164+
...tableRow,
165+
dataType: DataTypes.CALENDAR,
166+
options: options,
167+
};
168+
}
169+
137170
// Record of options
138171
let inputs: Record<string, any> = {};
139172
inputs[DataTypes.TEXT] = isText;
140173
inputs[DataTypes.NUMBER] = isNumber;
141174
inputs[DataTypes.SELECT] = isSelect;
142175
inputs[DataTypes.MARKDOWN] = isMarkdown;
143176
inputs[DataTypes.NEW_COLUMN] = isNewColumn;
177+
inputs[DataTypes.CALENDAR] = isCalendar;
144178
if (inputs.hasOwnProperty(column.input)) {
145179
return await inputs[column.input]();
146180
} else {

src/components/Header.tsx

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import MultiIcon from "components/img/Multi";
55
import HashIcon from "components/img/Hash";
66
import PlusIcon from "components/img/Plus";
77
import HeaderMenu from "components/HeaderMenu";
8+
import CalendarIcon from "components/img/CalendarIcon";
89
import MarkdownObsidian from "components/img/Markdown";
910
import {
1011
ActionTypes,
@@ -56,7 +57,6 @@ export default function Header(headerProps: DatabaseHeaderProps) {
5657
const [expanded, setExpanded] = useState(created || false);
5758
const [domReady, setDomReady] = useState(false);
5859
const [referenceElement, setReferenceElement] = useState(null);
59-
const [isMetadata, setIsMetadata] = useState(headerProps.column.isMetadata);
6060
const [isInline, setIsInline] = useState(headerProps.column.isInline);
6161
const [labelState, setLabelState] = useState(headerProps.column.label);
6262
React.useEffect(() => {
@@ -75,6 +75,9 @@ export default function Header(headerProps: DatabaseHeaderProps) {
7575
setOptionsOfSelectDataType(options, rows, id);
7676
propertyIcon = <MultiIcon />;
7777
break;
78+
case DataTypes.CALENDAR:
79+
propertyIcon = <CalendarIcon />;
80+
break;
7881
case DataTypes.MARKDOWN:
7982
// TODO : add a markdown icon
8083
propertyIcon = <MarkdownObsidian />;
@@ -117,7 +120,7 @@ export default function Header(headerProps: DatabaseHeaderProps) {
117120
{labelState}
118121
{isInline && <span>*</span>}
119122
</div>
120-
{!isMetadata && domReady
123+
{domReady
121124
? ReactDOM.createPortal(
122125
<HeaderMenu
123126
headerProps={headerProps}

0 commit comments

Comments
 (0)