feat: add timeline view

This commit is contained in:
liufree 2025-04-24 23:14:53 +08:00
parent 027c64290e
commit 5bea138f64
5 changed files with 168 additions and 3 deletions

View file

@ -8,6 +8,7 @@ QueryDash的目标是开发一款类似Notion Database的Obsidian插件但不
**现有功能**
1. **多视图支持**:提供表格和列表两种视图,满足不同场景需求。
- **时间轴视图**: 使用时间轴来展示数据
2. **Dataview SQL支持**兼容Dataview的SQL语法。
3. **增强功能**
- **搜索**:快速定位所需内容。
@ -27,6 +28,24 @@ file.mtime as "mtime" ,file.tags as "tags" from #clippings
![demo.gif](docs/demo.gif)
**timeline**
**simple mode**
~~~markdown
```querydash
timeline from #start
```
~~~
![timeline.png](docs/timeline.png)
**full mode**
If you want to append the time later, alias a specific field as "time".
~~~markdown
```querydash
timeline file.ctime as "time" from #start where file.ctime<=date(today) sort file.mtime desc
```
~~~
**远景规划**
1. **首页**:将常用功能整合到首页,提供一站式便捷操作。

View file

@ -9,6 +9,7 @@ In the future, it will gradually expand with more practical features to help use
**Current Features**
1. **Multi-view Support**: Provides table and list views to meet different scenario needs.
- **timeline**: use timeline view to display data.
2. **Dataview SQL Support**: Compatible with Dataview's SQL syntax .
3. **Enhanced Features**:
- **Search**: Quickly locate the content you need.
@ -18,6 +19,7 @@ In the future, it will gradually expand with more practical features to help use
**Tutorial**
**table**
~~~markdown
```querydash
table file.name , file.outlinks as "links" ,file.ctime as "ctime",
@ -27,6 +29,24 @@ file.mtime as "mtime" ,file.tags as "tags" from #clippings
![demo.gif](docs/demo.gif)
**timeline**
**simple mode**
~~~markdown
```querydash
timeline from #start
```
~~~
![timeline.png](docs/timeline.png)
**full mode**
If you want to append the time later, alias a specific field as "time".
~~~markdown
```querydash
timeline file.ctime as "time" from #start where file.ctime<=date(today) sort file.mtime desc
```
~~~
**Future Vision**
1. **Homepage**: Integrate frequently used functions into the homepage for one-stop convenience.
2. **Multi-view Support**: Supports timeline, gallery, table, and other views to meet diverse scenario needs.

BIN
docs/timeline.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

View file

@ -2,8 +2,9 @@ import React, {useEffect} from 'react';
import {App} from "obsidian";
import TableView from "./tableview/TableView";
import ListView from "./listview/ListView";
import {ConfigProvider, ConfigProviderProps} from "antd";
import {ConfigProvider, ConfigProviderProps, Timeline} from "antd";
import enUS from 'antd/locale/en_US';
import TimeLineView from "./timelineview/TimeLineView";
interface QueryDashViewDashs {
@ -18,15 +19,18 @@ const QueryDashView: React.FC<QueryDashViewDashs> = ({app, source}) => {
const [sourceType, setSourceType] = React.useState<string>("table");
useEffect(() => {
const sourceType = source.toLowerCase().startsWith("list") ? "list" : "table";
// 获取第一个单词
const sourceType = source.split(" ")[0].toLowerCase();
setSourceType(sourceType);
}, []);
const getView = (app: App, source: string) => {
if (sourceType === "table") {
return <TableView app={app} source={source}/>;
} else {
} else if (sourceType === "list") {
return <ListView app={app} source={source}/>;
} else if (sourceType === "timeline") {
return <TimeLineView app={app} source={source}/>;
}
}

View file

@ -0,0 +1,122 @@
import {ProList} from '@ant-design/pro-components';
import React, {useEffect, useState} from "react";
import {formatValue} from "../GenerateColumns";
import {getAPI} from 'obsidian-dataview';
import {ViewProps} from "../../models/ViewProps";
import {Radio, RadioChangeEvent, Timeline} from "antd";
import {TFile} from "obsidian";
const TimeLineView: React.FC<ViewProps> = ({app, source}) => {
const [data, setData] = React.useState<any>([]);
const dv = getAPI(app);
const [mode, setMode] = useState<'left' | 'alternate' | 'right'>('alternate');
const onChange = (e: RadioChangeEvent) => {
setMode(e.target.value);
};
const renderItem = (itemFile: any, itemCtime: any) => {
const date = itemCtime?.display.split(" ")[0] || "";
return (
<span>
<a onClick={() => {
const file = app.vault.getAbstractFileByPath(itemFile.path);
if (file && file instanceof TFile) {
const leaf = app.workspace.getLeaf();
leaf.openFile(file);
}
}}>
{itemFile.display}
</a>
{date ? " " + date : ""}
</span>
)
}
useEffect(() => {
let resData: any[] = [];
const response = executeTableQuery(dv, source, {});
response.then((res) => {
const {tableData} = res;
tableData.forEach((item: any) => {
const file = item["File"];
const ctime = item["time"];
const finalDisplay = renderItem(file, ctime);
resData.push({"children": finalDisplay});
});
setData(resData);
}).catch((error) => {
console.error("Error executing query:", error);
});
// console.log(response, response);
}, [app, source]);
function parseTableResult(value: any, params: any): Array<Record<string, any>> {
const headers: string[] = value.headers;
const rows: Array<Record<string, any>> = [];
value.values.forEach((row: any) => {
const values: Record<string, any> = {};
headers.forEach((header, index) => {
const value = row[index];
// console.log("list value", value);
const resValue = formatValue(value);
// console.log("list resValue", resValue);
values[header] = resValue;
});
rows.push(values);
}); // console.log("list rows", rows);
return rows;
}
function replaceFirstWord(source: string): string {
const words = source.split(' ');
if (words[0] === 'timeline') {
words[0] = 'table';
}
return words.join(' ');
}
const executeTableQuery = async (dvApi: any, source: any, params: any) => {
const sql = replaceFirstWord(source);
const queryResult = await dvApi.query(sql);
console.log("queryResult", queryResult);
const tableData: any = parseTableResult(queryResult.value, params);
return {tableData: tableData};
}
return (
<>
<h4>Timeline</h4>
<br/>
<Radio.Group
onChange={onChange}
value={mode}
style={{
marginBottom: 20,
}}
>
<Radio value="left">Left</Radio>
<Radio value="right">Right</Radio>
<Radio value="alternate">Alternate</Radio>
</Radio.Group>
<Timeline
mode={mode}
items={data}
/>
</>)
}
export default TimeLineView;