Skip to content

Commit

Permalink
Merge branch 'feat/tasks' into feat/editable-vanilla-columns
Browse files Browse the repository at this point in the history
  • Loading branch information
GamerGirlandCo committed Oct 2, 2024
2 parents dfe02d3 + 313c0ee commit 732dd81
Show file tree
Hide file tree
Showing 6 changed files with 94 additions and 77 deletions.
19 changes: 18 additions & 1 deletion src/api/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,34 @@ import { IndexQuery } from "index/types/index-query";
import { Indexable } from "index/types/indexable";
import { MarkdownPage } from "index/types/markdown";
import { Result } from "./result";
import { Component, MarkdownPostProcessorContext, MarkdownRenderChild } from "obsidian";
import { App, Component, MarkdownPostProcessorContext, MarkdownRenderChild } from "obsidian";
import { DatacoreJSRenderer } from "ui/javascript";
import { DatacoreLocalApi } from "./local-api";
import Parsimmon from "parsimmon";
import { Coerce } from "./coerce";
import { DataArray } from "./data-array";
import * as luxon from "luxon";
import * as preact from "preact";

/** Exterally visible API for datacore. */
export class DatacoreApi {
public constructor(public core: Datacore) {}

/** Get acess to luxon functions. */
get luxon(): typeof luxon {
return luxon;
}

/** Get access to preact functions. */
get preact(): typeof preact {
return preact;
}

/** Central Obsidian app object. */
get app(): App {
return this.core.app;
}

///////////////
// Local API //
///////////////
Expand Down
3 changes: 1 addition & 2 deletions src/api/local-api.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -173,8 +173,7 @@ export class DatacoreLocalApi {
public useInterning = useInterning;

public useSetField = useSetField;

/** Memoize the input automatically and process it using a Data Array; returns a vanilla array back. */
/** Memoize the input automatically and process it using a DataArray; returns a vanilla array back. */
public useArray<T, U>(input: T[] | DataArray<T>, process: (data: DataArray<T>) => DataArray<U>, deps?: any[]): U[] {
return hooks.useMemo(() => process(DataArray.wrap(input)).array(), [input, ...(deps ?? [])]);
}
Expand Down
12 changes: 0 additions & 12 deletions src/api/ui/views/list.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,18 +34,6 @@ export function ListView<T>(state: ListState<T>) {
const elements = state.rows ?? [];
const renderer = state.renderer ?? identity;

/*
const pageSize = useMemo(() => {
if (state.paging === undefined) {
if (settings.defaultPagingEnabled) return settings.defaultPageSize;
else return undefined;
} else if (state.paging == false) return undefined;
else if (state.paging == true) return settings.defaultPageSize;
else return state.paging;
}, [settings.defaultPageSize, settings.defaultPagingEnabled, state.paging]);
const [page, setPage] = useState<number>(0);
*/

if (type == "none") {
return (
<div className="datacore-list datacore-list-none">
Expand Down
28 changes: 25 additions & 3 deletions src/api/ui/views/paging.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { useCallback, useContext, useMemo, useState } from "preact/hooks";
import { Fragment } from "preact";
import { Fragment, RefObject } from "preact";
import React from "preact/compat";
import { SETTINGS_CONTEXT } from "ui/markdown";

Expand Down Expand Up @@ -94,17 +94,22 @@ export interface Paging {
setPage: (page: number) => void;
}

/** */
/**
* Central paging hook which extracts page metadata out of Datacore settings, handles page overflow, current page state, and updating the page
* if the elements change. If a container is specified, also supports scrolling the container view on page changes.
*/
export function useDatacorePaging({
initialPage = 0,
paging,
scrollOnPageChange,
elements,
container,
}: {
initialPage: number;
paging: number | boolean | undefined;
scrollOnPageChange?: boolean | number;
elements: number;
container?: RefObject<HTMLElement>;
}): Paging {
const settings = useContext(SETTINGS_CONTEXT);

Expand All @@ -114,7 +119,24 @@ export function useDatacorePaging({
(typeof scrollOnPageChange === "number" && scrollOnPageChange >= pageSize) ||
!!(scrollOnPageChange ?? settings.scrollOnPageChange);

const [page, totalPages, setPage] = usePaging({ initialPage, pageSize, elements });
const [page, totalPages, rawSetPage] = usePaging({ initialPage, pageSize, elements });

// Handle auto-scroll if a container is provided.
const setPage = useCallback(
(newPage: number) => {
if (page != newPage && container && shouldScroll) {
container.current?.scrollIntoView({
behavior: "smooth",
block: "start",
inline: "nearest",
});
}

rawSetPage(page);
},
[page, container, shouldScroll, rawSetPage]
);

return { enabled: pagingEnabled, scroll: shouldScroll, page, pageSize, totalPages, setPage };
}

Expand Down
24 changes: 7 additions & 17 deletions src/api/ui/views/table.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ export interface VanillaTableProps<T> {
scrollOnPaging?: boolean | number;
}

/** A simple table which supports grouping, sorting, paging, and custom columns. */
export function VanillaTable<T>(props: VanillaTableProps<T>) {
// Cache columns by reference equality of the specific columns. Columns have various function references
// inside them and so cannot be compared by value equality.
Expand All @@ -78,29 +79,16 @@ export function VanillaTable<T>(props: VanillaTableProps<T>) {
return a.every((value, index) => value == b[index]);
});

// Count total elements and then page appropriately.
const tableRef = useRef<HTMLDivElement>(null);
const totalElements = useMemo(() => Groupings.count(props.rows), [props.rows]);
const paging = useDatacorePaging({
initialPage: 0,
paging: props.paging,
scrollOnPageChange: props.scrollOnPaging,
elements: totalElements,
container: tableRef,
});
const tableRef = useRef<HTMLDivElement>(null);

const setPage = useCallback(
(page: number) => {
if (page != paging.page && paging.scroll) {
tableRef.current?.scrollIntoView({
behavior: "smooth",
block: "start",
inline: "nearest",
});
}

paging.setPage(page);
},
[paging.page, paging.setPage, paging.scroll, tableRef]
);

const pagedRows = useMemo(() => {
if (paging.enabled)
Expand Down Expand Up @@ -132,7 +120,9 @@ export function VanillaTable<T>(props: VanillaTableProps<T>) {
))}
</tbody>
</table>
{paging.enabled && <ControlledPager page={paging.page} totalPages={paging.totalPages} setPage={setPage} />}
{paging.enabled && (
<ControlledPager page={paging.page} totalPages={paging.totalPages} setPage={paging.setPage} />
)}
</div>
);
}
Expand Down
85 changes: 43 additions & 42 deletions src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,18 +67,17 @@ export default class DatacorePlugin extends Plugin {
this.addCommand({
id: "datacore-add-view-page",
name: "Create View Page",
callback: () => {
const newLeaf = this.app.workspace.getLeaf("tab");
newLeaf.setViewState({type: VIEW_TYPE_DATACORE, active: true});
this.app.workspace.setActiveLeaf(newLeaf, {focus: true});
(newLeaf.view as DatacoreQueryView).toggleSettings(true);
},
callback: () => {
const newLeaf = this.app.workspace.getLeaf("tab");
newLeaf.setViewState({ type: VIEW_TYPE_DATACORE, active: true });
this.app.workspace.setActiveLeaf(newLeaf, { focus: true });
(newLeaf.view as DatacoreQueryView).toggleSettings(true);
},
});

// Make the API globally accessible from any context.
window.datacore = this.api;


// bon appetit
console.log(`Datacore: version ${this.manifest.version} (requires obsidian ${this.manifest.minAppVersion})`);
}
Expand Down Expand Up @@ -263,49 +262,51 @@ class GeneralSettingsTab extends PluginSettingTab {
await this.plugin.updateSettings({ importerUtilization: limited });
});
});

new Setting(this.containerEl)
.setName("Recursive subtask completion")
.setDesc("Whether or not subtasks should be completed along with their parent in datacore task views")
.addToggle((tb) => {
tb.setValue(this.plugin.settings.recursiveTaskCompletion).onChange(async (val) => {
await this.plugin.updateSettings({ recursiveTaskCompletion: val });
.setName("Maximum Recursive Render Depth")
.setDesc(
"Maximum depth that objects will be rendered to (i.e., how many levels of subproperties" +
"will be rendered by default). This avoids infinite recursion due to self-referential objects" +
"and ensures that rendering objects is acceptably performant."
)
.addText((text) => {
text.setValue(this.plugin.settings.maxRecursiveRenderDepth.toString()).onChange(async (value) => {
const parsed = parseInt(value);
if (isNaN(parsed)) return;
await this.plugin.updateSettings({ maxRecursiveRenderDepth: parsed });
});
});

this.containerEl.createEl("h2", "Tasks");

new Setting(this.containerEl)
.setName("Task Completion Text")
.setDesc("Name of inline field in which to store task completion date/time")
.addText((text) => {
text.setValue(this.plugin.settings.taskCompletionText).onChange(async (value) => {
await this.plugin.updateSettings({ taskCompletionText: value });
});
});

new Setting(this.containerEl)
.setName("Maximum Recursive Render Depth")
.setDesc("Maximum depth that objects will be rendered to (i.e., how many levels of subproperties" +
"will be rendered by default). This avoids infinite recursion due to self-referential objects" +
"and ensures that rendering objects is acceptably performant.")
.addText(text => {
text.setValue(this.plugin.settings.maxRecursiveRenderDepth.toString()).onChange(async value => {
const parsed = parseInt(value)
if(isNaN(parsed)) return;
await this.plugin.updateSettings({maxRecursiveRenderDepth: parsed})
})
});

this.containerEl.createEl("h2", "Tasks")

new Setting(this.containerEl)
.setName("Task Completion Text")
.setDesc("Name of inline field in which to store task completion date/time")
.addText(text => {
text.setValue(this.plugin.settings.taskCompletionText).onChange(async value => {
await this.plugin.updateSettings({taskCompletionText: value})
})
})

new Setting(this.containerEl)
.setName("Use Emoji Shorthand for Task Completion")
.setDesc("If enabled, automatic completion will use an emoji shorthand ✅ YYYY-MM-DD" +
"instead of [completion:: date].")
.addToggle((tb) => {
.setName("Use Emoji Shorthand for Task Completion")
.setDesc(
"If enabled, automatic completion will use an emoji shorthand ✅ YYYY-MM-DD" +
"instead of [completion:: date]."
)
.addToggle((tb) => {
tb.setValue(this.plugin.settings.taskCompletionUseEmojiShorthand).onChange(async (val) => {
await this.plugin.updateSettings({ taskCompletionUseEmojiShorthand: val });
});
});


new Setting(this.containerEl)
.setName("Recursive subtask completion")
.setDesc("Whether or not subtasks should be completed along with their parent in datacore task views")
.addToggle((tb) => {
tb.setValue(this.plugin.settings.recursiveTaskCompletion).onChange(async (val) => {
await this.plugin.updateSettings({ recursiveTaskCompletion: val });
});
});
}
}

0 comments on commit 732dd81

Please sign in to comment.