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

Commit 87d8438

Browse files
committed
Merge branch '17-fr-image-column-type'
2 parents 4140c68 + 59ea10e commit 87d8438

File tree

5 files changed

+866
-180
lines changed

5 files changed

+866
-180
lines changed

package.json

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@
3030
"@types/react-dom": "17.0.14",
3131
"@types/react-table": "7.7.11",
3232
"@types/react-window": "1.8.5",
33-
"@types/react-calendar": "3.5.0",
33+
"@types/react-datepicker": "4.4.1",
3434
"@types/react-beautiful-dnd": "13.1.2",
3535
"@types/react-csv": "1.1.2",
3636
"@types/luxon": "2.3.2",
@@ -57,13 +57,12 @@
5757
"obsidian-dataview": "0.5.20",
5858
"react": "17.0.2",
5959
"react-beautiful-dnd": "13.1.0",
60-
"react-contenteditable": "3.3.6",
6160
"react-dom": "17.0.2",
6261
"react-csv": "2.2.2",
6362
"react-popper": "2.3.0",
6463
"react-table": "7.7.0",
6564
"react-window": "1.8.7",
66-
"react-calendar": "3.7.0",
65+
"react-datepicker": "4.7.0",
6766
"regenerator-runtime": "0.13.9",
6867
"luxon": "2.4.0"
6968
}

src/components/Cell.tsx

Lines changed: 36 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import { ActionTypes, DataTypes } from "helpers/Constants";
2-
import React, { useEffect, useLayoutEffect, useRef, useState } from "react";
3-
import ContentEditable, { ContentEditableEvent } from "react-contenteditable";
2+
import React, { useEffect, useRef, useState } from "react";
43
import { LOGGER } from "services/Logger";
54
import { Cell } from "react-table";
65
import { MarkdownRenderer } from "obsidian";
@@ -23,6 +22,7 @@ export default function DefaultCell(cellProperties: Cell) {
2322
const note: NoteInfo = (cellProperties.row.original as any).note;
2423
/** Ref to cell container */
2524
const containerCellRef = useRef<HTMLDivElement>();
25+
const editableMdRef = useRef<HTMLInputElement>();
2626
/** state of cell value */
2727
const [contextValue, setContextValue] = useState({
2828
value: initialValue,
@@ -45,6 +45,12 @@ export default function DefaultCell(cellProperties: Cell) {
4545
}
4646
}, []);
4747

48+
useEffect(() => {
49+
if (editableMdRef.current) {
50+
editableMdRef.current.focus();
51+
}
52+
}, [editableMdRef, dirtyCell]);
53+
4854
useEffect(() => {
4955
if (!dirtyCell && containerCellRef.current) {
5056
//TODO - this is a hack. find why is layout effect called twice
@@ -53,7 +59,7 @@ export default function DefaultCell(cellProperties: Cell) {
5359
contextValue.value,
5460
containerCellRef.current,
5561
note.getFile().path,
56-
null
62+
(cellProperties as any).initialState.view
5763
);
5864
}
5965
}, [dirtyCell]);
@@ -63,8 +69,18 @@ export default function DefaultCell(cellProperties: Cell) {
6369
event.target.blur();
6470
}
6571
};
72+
73+
const handleEditableOnclick = (event: any) => {
74+
setDirtyCell(true);
75+
};
76+
77+
const handlerEditableOnBlur = (event: any) => {
78+
setDirtyCell(false);
79+
setContextValue((event) => ({ value: event.value, update: true }));
80+
};
81+
6682
// onChange handler
67-
const handleOnChange = (event: ContentEditableEvent) => {
83+
const handleOnChange = (event: any) => {
6884
// cancelling previous timeouts
6985
if (editNoteTimeout) {
7086
clearTimeout(editNoteTimeout);
@@ -100,29 +116,33 @@ export default function DefaultCell(cellProperties: Cell) {
100116
case DataTypes.TEXT:
101117
return (cellProperties.column as any).isMetadata ? (
102118
<span className="data-input">{contextValue.value.toString()}</span>
103-
) : (
104-
<ContentEditable
105-
html={(contextValue.value && contextValue.value.toString()) || ""}
119+
) : dirtyCell ? (
120+
<input
121+
value={(contextValue.value && contextValue.value.toString()) || ""}
106122
onChange={handleOnChange}
107123
onKeyDown={handleKeyDown}
108-
onBlur={() =>
109-
setContextValue((old) => ({ value: old.value, update: true }))
110-
}
124+
onBlur={handlerEditableOnBlur}
111125
className={"data-input"}
112-
//innerRef={containerCellRef}
126+
ref={editableMdRef}
113127
/>
128+
) : (
129+
<span
130+
ref={containerCellRef}
131+
className="data-input"
132+
onClick={handleEditableOnclick}
133+
>
134+
{(contextValue.value && contextValue.value.toString()) || ""}
135+
</span>
114136
);
115137

116138
/** Number option */
117139
case DataTypes.NUMBER:
118140
return (
119-
<ContentEditable
120-
html={(contextValue.value && contextValue.value.toString()) || ""}
141+
<input
142+
value={(contextValue.value && contextValue.value.toString()) || ""}
121143
onChange={handleOnChange}
122144
onKeyDown={handleKeyDown}
123-
onBlur={() =>
124-
setContextValue((old) => ({ value: old.value, update: true }))
125-
}
145+
onBlur={handlerEditableOnBlur}
126146
className="data-input text-align-right"
127147
/>
128148
);
Lines changed: 25 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,12 @@
11
import { TableColumn, TableDataType } from "cdm/FolderModel";
22
import { CellContext } from "components/contexts/CellContext";
3-
import { ActionTypes, DataTypes, StyleVariables } from "helpers/Constants";
3+
import { ActionTypes } from "helpers/Constants";
44
import React, { useContext, useState } from "react";
5-
import Calendar from "react-calendar";
6-
import ReactDOM from "react-dom";
75
import { DateTime } from "luxon";
8-
import { usePopper } from "react-popper";
96
import { Cell } from "react-table";
7+
import DatePicker from "react-datepicker";
108
import NoteInfo from "services/NoteInfo";
11-
import { DataviewService } from "services/DataviewService";
12-
import { Literal } from "obsidian-dataview/lib/data-model/value";
9+
import { Portal } from "@material-ui/core";
1310

1411
type CalendarProps = {
1512
intialState: TableDataType;
@@ -19,102 +16,55 @@ type CalendarProps = {
1916
const CalendarPortal = (calendarProps: CalendarProps) => {
2017
const { column, cellProperties } = calendarProps;
2118
const dataDispatch = (cellProperties as any).dataDispatch;
22-
const [showCalendar, setShowCalendar] = useState(false);
2319
// Selector reference state
24-
const [calendarRef, setCalendarRef] = useState(null);
20+
2521
// Selector popper state
26-
const [selectPop, setSelectPop] = useState(null);
27-
const { styles, attributes } = usePopper(calendarRef, selectPop);
2822
/** state of cell value */
2923
const { contextValue, setContextValue } = useContext(CellContext);
3024

3125
/** Note info of current Cell */
3226
const note: NoteInfo = (cellProperties.row.original as any).note;
33-
const [calendarState, setCalendarState] = useState(null);
27+
const [calendarState, setCalendarState] = useState(
28+
DateTime.isDateTime(contextValue.value)
29+
? contextValue.value.toJSDate()
30+
: null
31+
);
3432

3533
function handleCalendarChange(date: Date) {
3634
const newValue = DateTime.fromJSDate(date);
3735
// save on disk
3836
dataDispatch({
3937
type: ActionTypes.UPDATE_CELL,
4038
file: note.getFile(),
41-
key: (cellProperties.column as any).key,
39+
key: column.key,
4240
value: DateTime.fromJSDate(date).toFormat("yyyy-MM-dd"),
4341
row: cellProperties.row,
44-
columnId: (cellProperties.column as any).id,
42+
columnId: column.id,
4543
});
4644

4745
setCalendarState(date);
48-
setShowCalendar(false);
4946
setContextValue({
5047
value: newValue,
5148
update: true,
5249
});
5350
}
5451

55-
function handlerOnClick(e: any) {
56-
if (!column.isMetadata) {
57-
const portalActive = document.getElementById("unique-calendar-portal");
58-
if (!showCalendar && portalActive !== null) {
59-
// Hacky way to trigger focus event on opened calendar
60-
// react-calendar has a bug where it doesn't trigger focus event
61-
portalActive.setAttribute("tabindex", "-1");
62-
portalActive.focus();
63-
portalActive.blur();
64-
}
65-
// Check value when click on calendar to parse it if its not a date
66-
setCalendarState(
67-
DateTime.isDateTime(contextValue.value)
68-
? contextValue.value.toJSDate()
69-
: new Date()
70-
);
71-
setShowCalendar(!showCalendar);
72-
}
73-
}
74-
75-
function displayCalendar() {
76-
return (
77-
<div
78-
className="menu"
79-
ref={setSelectPop}
80-
{...attributes.popper}
81-
style={{
82-
...styles.popper,
83-
zIndex: 4,
84-
minWidth: 200,
85-
maxWidth: 320,
86-
padding: "0.5rem",
87-
background: StyleVariables.BACKGROUND_SECONDARY,
88-
}}
89-
onMouseLeave={() => setShowCalendar(false)}
90-
onBlur={() => setShowCalendar(false)}
91-
id={"unique-calendar-portal"}
92-
>
93-
<Calendar onChange={handleCalendarChange} value={calendarState} />
94-
</div>
95-
);
96-
}
52+
const CalendarContainer = (containerProps: any) => {
53+
const el = document.getElementById("popper-container");
54+
return <Portal container={el}>{containerProps.children}</Portal>;
55+
};
9756

9857
return (
99-
<>
100-
<div
101-
className="data-input calendar"
102-
ref={setCalendarRef}
103-
onClick={handlerOnClick}
104-
>
105-
<span>
106-
{DataviewService.parseLiteral(
107-
contextValue.value as Literal,
108-
DataTypes.TEXT
109-
)}
110-
</span>
111-
</div>
112-
{showCalendar &&
113-
ReactDOM.createPortal(
114-
displayCalendar(),
115-
document.getElementById("popper-container")
116-
)}
117-
</>
58+
<div className="data-input calendar">
59+
<DatePicker
60+
dateFormat="yyyy-MM-dd"
61+
selected={calendarState}
62+
onChange={handleCalendarChange}
63+
popperContainer={CalendarContainer}
64+
placeholderText="Pick a date..."
65+
/>
66+
</div>
11867
);
11968
};
69+
12070
export default CalendarPortal;

src/services/DataviewService.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -97,10 +97,14 @@ class DataviewProxy {
9797
}
9898

9999
private parseToCalendar(literal: WrappedLiteral): Literal {
100-
if (literal.type === 'string') {
101-
return DateTime.fromISO(literal.value);
100+
if (DateTime.isDateTime(literal.value)) {
101+
if (literal.type === 'string') {
102+
return DateTime.fromISO(literal.value);
103+
} else {
104+
return literal.value;
105+
}
102106
} else {
103-
return literal.value;
107+
return null;
104108
}
105109
}
106110

0 commit comments

Comments
 (0)