Skip to content

Commit

Permalink
Merge pull request #207 from danielo515/feat/multi-select-note-improved
Browse files Browse the repository at this point in the history
feat: multi select notes uses the new notes input (better UI and search)
  • Loading branch information
danielo515 authored Jan 14, 2024
2 parents 5b6c6c6 + 7e43adb commit 19fb557
Show file tree
Hide file tree
Showing 5 changed files with 132 additions and 60 deletions.
42 changes: 12 additions & 30 deletions src/FormModal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,12 @@ import type { FormDefinition, FormOptions } from "./core/formDefinition";
import { FileSuggest } from "./suggesters/suggestFile";
import { DataviewSuggest } from "./suggesters/suggestFromDataview";
import { SvelteComponent } from "svelte";
import { executeSandboxedDvQuery, sandboxedDvQuery } from "./suggesters/SafeDataviewQuery";
import { A, E, parseFunctionBody, pipe, throttle } from "@std";
import { E, parseFunctionBody, pipe, throttle } from "@std";
import { log_error, log_notice } from "./utils/Log";
import { FieldValue, FormEngine, makeFormEngine } from "./store/formStore";
import { Writable } from "svelte/store";
import { FolderSuggest } from "./suggesters/suggestFolder";
import { allowsUnknownValues } from "./core/InputDefinitionSchema";
import { MultiSelectModel, MultiSelectTags } from "./views/components/MultiSelectModel";

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

Expand Down Expand Up @@ -164,55 +163,38 @@ export class FormModal extends Modal {
slider.onChange(fieldStore.value.set);
});
case "multiselect": {
const source = fieldInput.source;
const allowUnknownValues = allowsUnknownValues(fieldInput);
const options =
source == "fixed"
? fieldInput.multi_select_options
: source == "notes"
? pipe(
get_tfiles_from_folder(fieldInput.folder, this.app),
E.map(A.map((file) => file.basename)),
E.getOrElse((err) => {
log_error(err);
return [] as string[];
}),
)
: executeSandboxedDvQuery(
sandboxedDvQuery(fieldInput.query),
this.app,
);
fieldStore.value.set(initialValue ?? []);
this.svelteComponents.push(
new MultiSelect({
target: fieldBase.controlEl,
props: {
model: MultiSelectModel(
fieldInput,
this.app,
fieldStore.value as Writable<string[]>,
),
values: fieldStore.value as Writable<string[]>,
availableOptions: options,
errors: fieldStore.errors,
setting: fieldBase,
app: this.app,
allowUnknownValues,
},
}),
);
return;
}
case "tag": {
const options = Object.keys(this.app.metadataCache.getTags()).map((tag) =>
tag.slice(1),
); // remove the #
fieldStore.value.set(initialValue ?? []);
this.svelteComponents.push(
new MultiSelect({
target: fieldBase.controlEl,
props: {
values: fieldStore.value as Writable<string[]>,
availableOptions: options,
setting: fieldBase,
errors: fieldStore.errors,
app: this.app,
allowUnknownValues: true,
model: MultiSelectTags(
fieldInput,
this.app,
fieldStore.value as Writable<string[]>,
),
},
}),
);
Expand Down
1 change: 1 addition & 0 deletions src/core/InputDefinitionSchema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -164,4 +164,5 @@ export type inputDataviewSource = Output<typeof InputDataviewSourceSchema>;
export type inputSelectFixed = Output<typeof InputSelectFixedSchema>;
export type basicInput = Output<typeof InputBasicSchema>;
export type multiselect = Output<typeof MultiselectSchema>;
export type inputTag = Output<typeof InputTagSchema>;
export type inputType = Output<typeof InputTypeSchema>;
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { AbstractInputSuggest, App } from "obsidian";

export class MultiSuggest extends AbstractInputSuggest<string> {
export class StringSuggest extends AbstractInputSuggest<string> {
content: Set<string>;

constructor(
Expand Down
33 changes: 4 additions & 29 deletions src/views/components/MultiSelect.svelte
Original file line number Diff line number Diff line change
@@ -1,46 +1,21 @@
<script lang="ts">
import type { App, Setting } from "obsidian";
import { MultiSuggest } from "../../suggesters/MultiSuggest";
import { StringSuggest } from "../../suggesters/StringSuggest";
import { A, pipe } from "@std";
import { Readable, Writable } from "svelte/store";
import { MultiSelectModel } from "./MultiSelectModel";
export let availableOptions: string[] = [];
export let model: MultiSelectModel;
export let errors: Readable<string[]>;
export let values: Writable<string[]>;
export let allowUnknownValues: boolean = false;
// We take the setting to make it consistent with the other input components
export let setting: Setting;
export let app: App;
setting.settingEl.setCssStyles({
alignItems: "baseline",
});
$: remainingOptions = new Set(availableOptions);
function createInput(element: HTMLInputElement) {
new MultiSuggest(
element,
remainingOptions,
(selected) => {
remainingOptions.delete(selected);
remainingOptions = remainingOptions;
values.update((x) => [...x, selected]);
},
app,
allowUnknownValues,
);
}
function removeValue(value: string) {
remainingOptions.add(value);
remainingOptions = remainingOptions;
values.update((xs) =>
pipe(
xs,
A.filter((x) => x !== value),
),
);
}
const { createInput, removeValue } = model;
</script>

<div class="multi-select-root">
Expand Down
114 changes: 114 additions & 0 deletions src/views/components/MultiSelectModel.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
import { A, pipe } from "@std";
import { absurd } from "fp-ts/function";
import { App } from "obsidian";
import { multiselect, inputTag } from "src/core/InputDefinitionSchema";
import { executeSandboxedDvQuery, sandboxedDvQuery } from "src/suggesters/SafeDataviewQuery";
import { StringSuggest } from "src/suggesters/StringSuggest";
import { FileSuggest } from "src/suggesters/suggestFile";
import { Writable } from "svelte/store";

export interface MultiSelectModel {
createInput(element: HTMLInputElement): void;
removeValue(value: string): void;
}

export function MultiSelectModel(
fieldInput: multiselect,
app: App,
values: Writable<string[]>,
): MultiSelectModel {
const source = fieldInput.source;
const removeValue = (value: string) =>
values.update((xs) =>
pipe(
xs,
A.filter((x) => x !== value),
),
);
switch (source) {
case "dataview":
case "fixed": {
const remainingOptions = new Set(
source === "fixed"
? fieldInput.multi_select_options
: executeSandboxedDvQuery(sandboxedDvQuery(fieldInput.query), app),
);
return {
createInput(element: HTMLInputElement) {
new StringSuggest(
element,
remainingOptions,
(selected) => {
remainingOptions.delete(selected);
values.update((x) => [...x, selected]);
},
app,
fieldInput.allowUnknownValues,
);
},
removeValue(value: string) {
remainingOptions.add(value);
removeValue(value);
},
};
}
case "notes": {
return {
createInput(element: HTMLInputElement) {
new FileSuggest(
app,
element,
{
renderSuggestion(file) {
return file.basename;
},
selectSuggestion(file) {
values.update((x) => [...x, file.basename]);
return "";
},
},
fieldInput.folder,
);
},
removeValue,
};
}
default:
return absurd(source);
}
}

export function MultiSelectTags(
fieldInput: inputTag,
app: App,
values: Writable<string[]>,
): MultiSelectModel {
const remainingOptions = new Set(
Object.keys(app.metadataCache.getTags()).map(
(tag) => tag.slice(1) /** remove the leading # */,
),
);
return {
createInput(element: HTMLInputElement) {
new StringSuggest(
element,
remainingOptions,
(selected) => {
remainingOptions.delete(selected);
values.update((x) => [...x, selected]);
},
app,
false,
);
},
removeValue(value: string) {
remainingOptions.add(value);
values.update((x) =>
pipe(
x,
A.filter((x) => x !== value),
),
);
},
};
}

0 comments on commit 19fb557

Please sign in to comment.