Skip to content

Commit

Permalink
feat: dataview input type
Browse files Browse the repository at this point in the history
  • Loading branch information
danielo515 committed Sep 13, 2023
1 parent 01bc1b1 commit 22b8949
Show file tree
Hide file tree
Showing 7 changed files with 111 additions and 1 deletion.
2 changes: 1 addition & 1 deletion .github/workflows/sync-version.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ permissions:
pull-requests: write

jobs:
build:
sync-versions:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
Expand Down
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ https://github.com/danielo515/obsidian-modal-form/assets/2270425/542974aa-c58b-4
- toggle (true/false)
- free text
- text with autocompletion for note names (from a folder or root)
- text with autocompletion from a dataview query
- select from a list
- list of fixed values
- list of notes from a folder
Expand All @@ -44,6 +45,11 @@ The mentioned plugins (templater, quickAdd) have some little convenience inputs,
All of the mentioned tools are great at their job and unleash super convenient workflows.
For that reason, rather than offering an alternative, this plugin is designed as a complement to them, offering some basic building blocks that you can integrate with your existing templates and workflows.

## Friends of modal form
- [Templater](https://github.com/SilentVoid13/Templater) to open modals from templates
- [QuickAdd](https://github.com/chhoumann/quickadd) to quickly capture data from a modal
- [dataview](https://github.com/blacksmithgu/obsidian-dataview) to provide values for autocompletion

## Scope of this plugin

This plugin is intentionally narrow in scope. As it's mentioned in the previous section, it is designed as a building block, so you can integrate it with other plugins and workflows.
Expand Down
9 changes: 9 additions & 0 deletions src/FormModal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { exhaustiveGuard } from "./safety";
import { get_tfiles_from_folder } from "./utils/files";
import type { FormDefinition } from "./core/formDefinition";
import { FileSuggest } from "./suggesters/suggestFile";
import { DataviewSuggest } from "./suggesters/suggestFromDataview";

export type SubmitFn = (formResult: FormResult) => void;

Expand Down Expand Up @@ -99,6 +100,14 @@ export class FormModal extends Modal {
this.formResult[definition.name] = value;
});
});
case "dataview":
const query = fieldInput.query;
return fieldBase.addText((element) => {
new DataviewSuggest(element.inputEl, query, this.app);
element.onChange(async (value) => {
this.formResult[definition.name] = value;
});
});
case "select":
{
const source = fieldInput.source;
Expand Down
8 changes: 8 additions & 0 deletions src/core/formDefinition.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ export type FieldType =
type selectFromNotes = { type: "select"; source: "notes", folder: string };
type inputSlider = { type: "slider"; min: number, max: number };
type inputNoteFromFolder = { type: "note"; folder: string };
type inputDataviewSource = { type: 'dataview', query: string };
type inputSelectFixed = {
type: "select";
source: "fixed";
Expand All @@ -26,12 +27,17 @@ type inputType =
| inputNoteFromFolder
| inputSlider
| selectFromNotes
| inputDataviewSource
| inputSelectFixed;


function isObject(input: unknown): input is Record<string, unknown> {
return typeof input === "object" && input !== null;
}
export function isDataViewSource(input: unknown): input is inputDataviewSource {
return isObject(input) && input.type === 'dataview' && typeof input.query === 'string';
}

export function isInputSlider(input: unknown): input is inputSlider {
if (!isObject(input)) {
return false;
Expand Down Expand Up @@ -127,6 +133,8 @@ export function isInputTypeValid(input: unknown): input is inputType {
return true;
} else if (isInputSelectFixed(input)) {
return true;
} else if (isDataViewSource(input)) {
return true;
} else {
return false;
}
Expand Down
9 changes: 9 additions & 0 deletions src/exampleModalDefinition.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,15 @@ export const exampleModalDefinition: FormDefinition = {
folder: 'People'
}
},
{
name: 'dataview_example',
label: 'Dataview example',
description: 'Only people matching the dataview query will be shown',
input: {
type: 'dataview',
query: 'dv.pages("#person").filter(p => p.age < 30).map(p => p.file.name)'
}
},
{
name: "friendship_level",
label: "Friendship level",
Expand Down
45 changes: 45 additions & 0 deletions src/suggesters/suggestFromDataview.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
// Credits go to Liam's Periodic Notes Plugin: https://github.com/liamcain/obsidian-periodic-notes
import { App, TAbstractFile, TFolder } from "obsidian";
import { TextInputSuggest } from "./suggest";
import { ModalFormError } from "src/utils/Error";

/**
* Offers suggestions based on a dataview query.
* It requires the dataview plugin to be installed and enabled.
* For now, we are not very strict with the checks and just throw errors
*/
export class DataviewSuggest extends TextInputSuggest<string> {
sandboxedQuery: (dv: any, pages: any) => string[]

constructor(
public inputEl: HTMLInputElement,
private dvQuery: string,
protected app: App,
) {
super(app, inputEl);
this.sandboxedQuery = eval(`(function sandboxedQuery(dv, pages) { return ${dvQuery} })`)

}

getSuggestions(inputStr: string): string[] {
const dv = this.app.plugins.plugins.dataview?.api
if (!dv) {
throw new ModalFormError("Dataview plugin is not enabled")
}
const result = this.sandboxedQuery(dv, dv.pages)
if (!Array.isArray(result)) {
throw new ModalFormError("The dataview query did not return an array")
}
return result
}

renderSuggestion(option: string, el: HTMLElement): void {
el.setText(option);
}

selectSuggestion(option: string): void {
this.inputEl.value = option;
this.inputEl.trigger("input");
this.close();
}
}
33 changes: 33 additions & 0 deletions src/typings/obsidian-ex.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// https://github.com/blacksmithgu/obsidian-dataview/blob/bb594a27ba1eed130d7c2ab7eff0990578e93f62/src/typings/obsidian-ex.d.ts
import type { DataviewApi } from "api/plugin-api";
import "obsidian";

declare module "obsidian" {
interface MetadataCache {
trigger(...args: Parameters<MetadataCache["on"]>): void;
trigger(name: string, ...data: any[]): void;
}

interface App {
appId?: string;
plugins: {
enabledPlugins: Set<string>;
plugins: {
dataview?: {
api: DataviewApi;
};
};
};
}

interface Workspace {
/** Sent to rendered dataview components to tell them to possibly refresh */
on(name: "dataview:refresh-views", callback: () => void, ctx?: any): EventRef;
}
}

declare global {
interface Window {
DataviewAPI?: DataviewApi;
}
}

0 comments on commit 22b8949

Please sign in to comment.