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

Commit 5ef9931

Browse files
committed
Merge branch 'bug-do-not-add-keys-when-change-label'
2 parents 27e5476 + ea86193 commit 5ef9931

File tree

11 files changed

+110
-35
lines changed

11 files changed

+110
-35
lines changed

docs/changelog.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,14 @@
1+
## 1.0.0
2+
*Published on 2022/05/13*
3+
### Shiny new things
4+
- New type of column: Calendar
5+
- 2 new settings to show metadata file of created and modified using a toggle button
6+
- Now metadata columns can be sorted and add columns to the right or left
7+
### Improved
8+
- Type control using Literal of dataview. this will allow with futures versions the versatility of dataview plugin (images,links,...)
9+
- Refactor of settings to control errors in case of something is missing with a marshall and unmarshall
10+
### No longer broken
11+
- Control of options as unique with Select column type
112
## 0.3.1
213
*Published on 2022/05/10*
314
### Shiny new things

manifest.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"id": "dbfolder",
33
"name": "DB Folder",
4-
"version": "0.3.1",
4+
"version": "1.0.0",
55
"minAppVersion": "0.14.6",
66
"description": "Folder with the capability to store and retrieve data from a folder like database",
77
"author": "RafaelGB",

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "obsidian-dbfolder",
3-
"version": "0.3.1",
3+
"version": "1.0.0",
44
"description": "This is a sample plugin for Obsidian (https://obsidian.md)",
55
"main": "main.js",
66
"scripts": {

src/cdm/DatabaseModel.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { RowType } from "cdm/RowTypeModel"
2+
import { Literal } from "obsidian-dataview/lib/data-model/value";
23
import { LocalSettings } from "Settings"
34

45
/** database column */
@@ -30,12 +31,17 @@ export interface DatabaseYaml {
3031
}
3132

3233
export type RowDatabaseFields = {
33-
frontmatter: Record<string, any>;
34-
inline: Record<string, any>;
34+
frontmatter: Record<string, Literal>;
35+
inline: Record<string, Literal>;
3536
}
3637

3738
export type FilterCondition = {
3839
field: string;
3940
operator: string;
4041
value?: any;
42+
}
43+
44+
export type OptionSelect = {
45+
label: string;
46+
backgroundColor: string;
4147
}

src/components/Cell.tsx

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { ActionTypes, DataTypes } from "helpers/Constants";
2-
import React, { useLayoutEffect, useRef, useState } from "react";
2+
import React, { useEffect, useLayoutEffect, useRef, useState } from "react";
33
import ContentEditable, { ContentEditableEvent } from "react-contenteditable";
44
import { LOGGER } from "services/Logger";
55
import { Cell } from "react-table";
@@ -28,6 +28,7 @@ function getFilePath(cellValue: string): string {
2828
}
2929

3030
export default function DefaultCell(cellProperties: Cell) {
31+
console.log("render cell");
3132
const dataDispatch = (cellProperties as any).dataDispatch;
3233
/** Initial state of cell */
3334
const initialValue = cellProperties.value;
@@ -44,10 +45,20 @@ export default function DefaultCell(cellProperties: Cell) {
4445
});
4546
/** state for keeping the timeout to trigger the editior */
4647
const [editNoteTimeout, setEditNoteTimeout] = useState(null);
48+
const [dirtyCell, setDirtyCell] = useState(false);
4749
/** states for selector option */
4850
LOGGER.debug(
4951
`<=> Cell.rendering dataType: ${dataType}. value: ${contextValue.value}`
5052
);
53+
// set contextValue when cell is loaded
54+
useEffect(() => {
55+
if (!dirtyCell && initialValue !== contextValue.value) {
56+
setContextValue({
57+
value: initialValue,
58+
update: false,
59+
});
60+
}
61+
}, []);
5162

5263
const handleKeyDown = (event: any) => {
5364
if (event.key === "Enter") {
@@ -62,6 +73,7 @@ export default function DefaultCell(cellProperties: Cell) {
6273
}
6374
// first update the input text as user type
6475
setContextValue({ value: event.target.value, update: false });
76+
setDirtyCell(true);
6577
// initialize a setimeout by wrapping in our editNoteTimeout so that we can clear it out using clearTimeout
6678
setEditNoteTimeout(
6779
setTimeout(() => {
@@ -81,6 +93,7 @@ export default function DefaultCell(cellProperties: Cell) {
8193
row: cellProperties.row,
8294
columnId: (cellProperties.column as any).id,
8395
});
96+
setDirtyCell(false);
8497
}
8598

8699
function getCellElement() {

src/components/portals/CalendarPortal.tsx

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -29,11 +29,10 @@ const CalendarPortal = (calendarProps: CalendarProps) => {
2929

3030
/** Note info of current Cell */
3131
const note: NoteInfo = (cellProperties.row.original as any).note;
32-
3332
const [calendarState, setCalendarState] = useState(
3433
(
3534
DataviewService.parseLiteral(
36-
contextValue.value,
35+
cellProperties.value,
3736
DataTypes.CALENDAR
3837
) as DateTime
3938
).toJSDate()
@@ -60,11 +59,19 @@ const CalendarPortal = (calendarProps: CalendarProps) => {
6059

6160
function handlerOnClick(e: any) {
6261
if (!column.isMetadata) {
62+
const portalActive = document.getElementById("unique-calendar-portal");
63+
if (!showCalendar && portalActive !== null) {
64+
// Hacky way to trigger focus event on opened calendar
65+
// react-calendar has a bug where it doesn't trigger focus event
66+
portalActive.setAttribute("tabindex", "-1");
67+
portalActive.focus();
68+
portalActive.blur();
69+
}
6370
setShowCalendar(!showCalendar);
6471
}
6572
}
6673

67-
function renderCalendar() {
74+
function displayCalendar() {
6875
return (
6976
<div
7077
className="menu"
@@ -79,26 +86,28 @@ const CalendarPortal = (calendarProps: CalendarProps) => {
7986
background: StyleVariables.BACKGROUND_SECONDARY,
8087
}}
8188
onMouseLeave={() => setShowCalendar(false)}
89+
onBlur={() => setShowCalendar(false)}
90+
id={"unique-calendar-portal"}
8291
>
8392
<Calendar onChange={handleCalendarChange} value={calendarState} />
8493
</div>
8594
);
8695
}
96+
8797
return (
8898
<>
8999
<div
90100
className="data-input calendar"
91101
ref={setCalendarRef}
92102
onClick={handlerOnClick}
93-
onBlur={() => setShowCalendar(false)}
94103
>
95104
<span>
96105
{DataviewService.parseLiteral(contextValue.value, DataTypes.TEXT)}
97106
</span>
98107
</div>
99108
{showCalendar &&
100109
ReactDOM.createPortal(
101-
renderCalendar(),
110+
displayCalendar(),
102111
document.getElementById("popper-container")
103112
)}
104113
</>

src/components/reducers/DatabaseDispatch.tsx

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,14 @@ import { ActionType } from "react-table";
1313
import { VaultManagerDB } from "services/FileManagerService";
1414
import { moveFile, updateRowFile } from "helpers/VaultManagement";
1515
import { randomColor } from "helpers/Colors";
16-
import { DatabaseColumn, RowDatabaseFields } from "cdm/DatabaseModel";
16+
import {
17+
DatabaseColumn,
18+
OptionSelect,
19+
RowDatabaseFields,
20+
} from "cdm/DatabaseModel";
1721
import NoteInfo from "services/NoteInfo";
1822
import { DataviewService } from "services/DataviewService";
23+
import { obtainUniqueOptionValues } from "helpers/SelectHelper";
1924

2025
export function databaseReducer(state: TableDataType, action: ActionType) {
2126
LOGGER.debug(
@@ -166,13 +171,13 @@ export function databaseReducer(state: TableDataType, action: ActionType) {
166171
...row,
167172
[action.columnId]: DataviewService.parseLiteral(
168173
row[action.columnId],
169-
action.dataType // Destination type to parse
174+
action.TEXT // Destination type to parse
170175
),
171176
}));
172177
// Update state
173178
switch (action.dataType) {
174179
case DataTypes.SELECT:
175-
const options: any = [];
180+
const options: OptionSelect[] = [];
176181
// Generate selected options
177182
parsedData.forEach((row) => {
178183
if (row[action.columnId]) {
@@ -191,7 +196,7 @@ export function databaseReducer(state: TableDataType, action: ActionType) {
191196
{
192197
...state.columns[typeIndex],
193198
dataType: action.dataType,
194-
options: [...state.columns[typeIndex].options, ...options],
199+
options: obtainUniqueOptionValues(options),
195200
},
196201
...state.columns.slice(typeIndex + 1, state.columns.length),
197202
],

src/helpers/SelectHelper.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import { OptionSelect } from "cdm/DatabaseModel";
2+
3+
export function obtainUniqueOptionValues(arrayOptions: OptionSelect[]): OptionSelect[] {
4+
const uniqueValues: OptionSelect[] = [];
5+
arrayOptions.forEach(option => {
6+
if (!uniqueValues.some(unique => unique.label === option.label)) {
7+
uniqueValues.push(option);
8+
}
9+
});
10+
return uniqueValues;
11+
}

src/helpers/VaultManagement.ts

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@ export async function updateRowFile(file: TFile, columnId: string, newValue: str
9494
LOGGER.info(`=>updateRowFile. file: ${file.path} | columnId: ${columnId} | newValue: ${newValue} | option: ${option}`);
9595
const rowFields = obtainRowDatabaseFields(file, state.columns);
9696
const content = await VaultManagerDB.obtainContentFromTfile(file);
97+
let currentFrontmatter = VaultManagerDB.ontainCurrentFrontmatter(content);
9798
const column = state.columns.find(c => c.key === columnId);
9899

99100
// Adds an empty frontmatter at the beginning of the file
@@ -128,12 +129,15 @@ export async function updateRowFile(file: TFile, columnId: string, newValue: str
128129
// Modify key of a column
129130
async function columnKey(): Promise<void> {
130131
if (column.isInline) {
132+
// Go to inline mode
131133
await inlineColumnKey();
132134
return;
133135
}
134-
if (!Object.prototype.hasOwnProperty.call(rowFields.frontmatter, columnId)) {
136+
// If field does not exist yet, ignore it
137+
if (!Object.prototype.hasOwnProperty.call(currentFrontmatter, columnId)) {
135138
return;
136139
}
140+
137141
// Check if the column is already in the frontmatter
138142
// assign an empty value to the new key
139143
rowFields.frontmatter[newValue] = rowFields.frontmatter[columnId] ?? "";
@@ -157,7 +161,7 @@ export async function updateRowFile(file: TFile, columnId: string, newValue: str
157161
action: 'replace',
158162
file: file,
159163
regexp: frontmatterGroupRegex,
160-
newValue: parseFrontmatterFieldsToString(rowFields, content, deletedColumn)
164+
newValue: parseFrontmatterFieldsToString(rowFields, currentFrontmatter, deletedColumn)
161165
};
162166
await VaultManagerDB.editNoteContent(noteObject);
163167
}
@@ -238,9 +242,10 @@ export async function updateRowFile(file: TFile, columnId: string, newValue: str
238242
// Execute action
239243
if (updateOptions[option]) {
240244
// Check if file has frontmatter
241-
if (!hasFrontmatterKey(content)) {
245+
if (currentFrontmatter === undefined) {
242246
// If not, add it
243247
await addFrontmatter();
248+
currentFrontmatter = {};
244249
}
245250
// Then execute the action
246251
await updateOptions[option]();

src/parsers/RowDatabaseFieldsToFile.ts

Lines changed: 13 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,25 @@
11
import { RowDatabaseFields } from "cdm/DatabaseModel";
22
import { parseYaml } from "obsidian";
3-
export const parseFrontmatterFieldsToString = (databaseFields: RowDatabaseFields, original: string, deletedColumn?: string): string => {
3+
export const parseFrontmatterFieldsToString = (databaseFields: RowDatabaseFields, original: Record<string, string>, deletedColumn?: string): string => {
44
const frontmatterFields = databaseFields.frontmatter;
55
const inlineFields = databaseFields.inline;
66
const array: string[] = [];
77
array.push(`---`);
88
Object.keys(frontmatterFields).forEach(key => {
99
array.push(`${key}: ${frontmatterFields[key]}`);
1010
});
11-
const match = original.match(/^---\s+([\w\W]+?)\s+---/);
12-
if (match) {
13-
const frontmatterRaw = match[1];
14-
const yaml = parseYaml(frontmatterRaw);
15-
Object.keys(yaml)
16-
.filter(fkey =>
17-
// Filter out duplicates and deleted columns
18-
!Object.prototype.hasOwnProperty.call(inlineFields, fkey)
19-
&& !Object.prototype.hasOwnProperty.call(frontmatterFields, fkey)
20-
&& fkey !== deletedColumn)
21-
.forEach(key => {
22-
// add frontmatter fields that are not specified as database fields
23-
array.push(`${key}: ${yaml[key] ?? ''}`);
24-
});
25-
}
11+
12+
Object.keys(original)
13+
.filter(fkey =>
14+
// Filter out duplicates and deleted columns
15+
!Object.prototype.hasOwnProperty.call(inlineFields, fkey)
16+
&& !Object.prototype.hasOwnProperty.call(frontmatterFields, fkey)
17+
&& fkey !== deletedColumn)
18+
.forEach(key => {
19+
// add frontmatter fields that are not specified as database fields
20+
array.push(`${key}: ${original[key] ?? ''}`);
21+
});
22+
2623
array.push(`---`);
2724
return array.join('\n');
2825
}

0 commit comments

Comments
 (0)