From 1cf155951b1c38af59abb84cf16e6c852bd87abd Mon Sep 17 00:00:00 2001 From: Danielo Rodriguez Date: Tue, 3 Oct 2023 14:57:30 +0200 Subject: [PATCH] feat: add multi-line text area fixes #46 --- src/FormModal.ts | 16 ++- src/core/formDefinition.ts | 30 +++-- src/exampleModalDefinition.ts | 208 +++++++++++++++++----------------- styles.css | 34 ++++-- 4 files changed, 163 insertions(+), 125 deletions(-) diff --git a/src/FormModal.ts b/src/FormModal.ts index 97e71066..d86ce8a2 100644 --- a/src/FormModal.ts +++ b/src/FormModal.ts @@ -1,4 +1,4 @@ -import { App, Modal, Setting } from "obsidian"; +import { App, Modal, Platform, Setting } from "obsidian"; import MultiSelect from "./views/components/MultiSelect.svelte"; import FormResult, { formDataFromFormOptions, type ModalFormData } from "./FormResult"; import { exhaustiveGuard } from "./safety"; @@ -35,6 +35,20 @@ export class FormModal extends Modal { const type = fieldInput.type; const initialValue = this.formResult[definition.name]; switch (type) { + case "textarea": + fieldBase.setClass('modal-form-textarea') + return fieldBase.addTextArea((textEl) => { + textEl.onChange(value => { + + this.formResult[definition.name] = value; + }); + textEl.inputEl.rows = 6; + if (Platform.isIosApp) + textEl.inputEl.style.width = "100%"; + else if (Platform.isDesktopApp) { + textEl.inputEl.rows = 10; + } + }) case "text": return fieldBase.addText((text) => { initialValue !== undefined && text.setValue(String(initialValue)); diff --git a/src/core/formDefinition.ts b/src/core/formDefinition.ts index 98294028..0371b49c 100644 --- a/src/core/formDefinition.ts +++ b/src/core/formDefinition.ts @@ -4,13 +4,17 @@ * Here are the types, validators, rules etc. */ -export type FieldType = - | "text" - | "number" - | "date" - | "time" - | "datetime" - | "toggle"; +const fieldTypeMap = { + text: "Text", + number: "Number", + date: "Date", + time: "Time", + datetime: "DateTime", + textarea: "Text area", + toggle: "Toggle" +} as const; + +type FieldType = keyof typeof fieldTypeMap type selectFromNotes = { type: "select"; source: "notes", folder: string }; type inputSlider = { type: "slider"; min: number, max: number }; @@ -33,12 +37,7 @@ type inputType = | inputSelectFixed; export const FieldTypeReadable: Record = { - "text": "Text", - "number": "Number", - "date": "Date", - "time": "Time", - "datetime": "DateTime", - "toggle": "Toggle", + ...fieldTypeMap, "note": "Note", "slider": "Slider", "select": "Select", @@ -136,10 +135,10 @@ export type EditableFormDefinition = { }; export function isValidBasicInput(input: unknown): input is basicInput { - if (!isObject(input)) { + if (!isObject(input) || typeof input.type !== "string") { return false; } - return ["text", "number", "date", "time", "datetime", "toggle"].includes(input.type as string); + return input.type in fieldTypeMap; } export function isMultiSelect(input: unknown): input is multiselect { @@ -162,7 +161,6 @@ export function isInputTypeValid(input: unknown): input is inputType { return true; default: return false; - } } diff --git a/src/exampleModalDefinition.ts b/src/exampleModalDefinition.ts index 07b2f9c1..a7917fbd 100644 --- a/src/exampleModalDefinition.ts +++ b/src/exampleModalDefinition.ts @@ -1,105 +1,111 @@ import type { FormDefinition } from "./core/formDefinition"; export const exampleModalDefinition: FormDefinition = { - title: "Example form", - name: "example-form", - fields: [ - { - name: "Name", - description: "It is named how?", - input: { type: "text" }, - }, - { - name: "age", - label: "Age", - description: "How old", - input: { type: "number" }, - }, - { - name: "dateOfBirth", - label: "Date of Birth", - description: "When were you born?", - input: { type: "date" }, - }, - { - name: "timeOfDay", - label: "Time of day", - description: "The time you can do this", - input: { type: "time" }, - }, - { - name: "is_family", - label: "Is family", - description: "If it is part of the family", - input: { type: "toggle" }, - }, - { - name: "favorite_book", - label: "Favorite book", - description: "Pick one", - input: { type: "note", folder: "Books" }, - }, - { - name: "multi_example", - label: "Multi select folder", - description: "Allows to pick many notes from a folder", - input: { type: "multiselect", source: "notes", folder: "Books" }, - }, - { - name: "multi_example_2", - label: "Multi select fixed", - description: "Allows to pick many notes from a fixed list", - input: { type: "multiselect", source: "fixed", options: ['Android', 'iOS', 'Windows', 'MacOS', 'Linux', 'Solaris', 'MS2'] }, - }, - { - name: "best_fried", - label: "Best friend", - description: "Pick one", - input: { - type: 'select', - source: 'notes', - 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", - description: "How good friends are you?", - input: { - type: 'slider', - min: 0, - max: 10 - } - }, - { - name: "favorite_meal", - label: "Favorite meal", - description: "Pick one option", - input: { - type: "select", source: "fixed", options: [ - { value: "pizza", label: "🍕 Pizza" }, - { value: "pasta", label: "🍝 Pasta" }, - { value: "burger", label: "🍔 Burger" }, - { value: "salad", label: "🥗 Salad" }, - { value: "steak", label: "🥩 Steak" }, - { value: "sushi", label: "🍣 Sushi" }, - { value: "ramen", label: "🍜 Ramen" }, - { value: "tacos", label: "🌮 Tacos" }, - { value: "fish", label: "🐟 Fish" }, - { value: "chicken", label: "🍗 Chicken" } - ] - }, - }, - - - ], + title: "Example form", + name: "example-form", + fields: [ + { + name: "Name", + description: "It is named how?", + input: { type: "text" }, + }, + { + name: "age", + label: "Age", + description: "How old", + input: { type: "number" }, + }, + { + name: "dateOfBirth", + label: "Date of Birth", + description: "When were you born?", + input: { type: "date" }, + }, + { + name: "timeOfDay", + label: "Time of day", + description: "The time you can do this", + input: { type: "time" }, + }, + { + name: "is_family", + label: "Is family", + description: "If it is part of the family", + input: { type: "toggle" }, + }, + { + name: "favorite_book", + label: "Favorite book", + description: "Pick one", + input: { type: "note", folder: "Books" }, + }, + { + name: "multi_example", + label: "Multi select folder", + description: "Allows to pick many notes from a folder", + input: { type: "multiselect", source: "notes", folder: "Books" }, + }, + { + name: "multi_example_2", + label: "Multi select fixed", + description: "Allows to pick many notes from a fixed list", + input: { type: "multiselect", source: "fixed", options: ['Android', 'iOS', 'Windows', 'MacOS', 'Linux', 'Solaris', 'MS2'] }, + }, + { + name: "best_fried", + label: "Best friend", + description: "Pick one", + input: { + type: 'select', + source: 'notes', + 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", + description: "How good friends are you?", + input: { + type: 'slider', + min: 0, + max: 10 + } + }, + { + name: "favorite_meal", + label: "Favorite meal", + description: "Pick one option", + input: { + type: "select", source: "fixed", options: [ + { value: "pizza", label: "🍕 Pizza" }, + { value: "pasta", label: "🍝 Pasta" }, + { value: "burger", label: "🍔 Burger" }, + { value: "salad", label: "🥗 Salad" }, + { value: "steak", label: "🥩 Steak" }, + { value: "sushi", label: "🍣 Sushi" }, + { value: "ramen", label: "🍜 Ramen" }, + { value: "tacos", label: "🌮 Tacos" }, + { value: "fish", label: "🐟 Fish" }, + { value: "chicken", label: "🍗 Chicken" } + ] + }, + }, + { + name: "some notes", + label: "Multi line notes", + description: "Put your thouhts here", + input: { + type: "textarea", + } + } + ], }; diff --git a/styles.css b/styles.css index fd9e0739..c64171a6 100644 --- a/styles.css +++ b/styles.css @@ -7,37 +7,57 @@ If your plugin does not need CSS, delete this file. */ +:root { + --mf-spacing: 0.75rem; +} + /* Utilities to remove styles from native obsidian elements when wrapped like this
...
*/ .remove-border div:first-of-type { - border: none; + border: none; } .remove-padding div:first-of-type { - padding: 0; + padding: 0; } .fix-suggest .setting-item-info { - display: none; + display: none; } .modal-form-danger { - color: var(--text-error); + color: var(--text-error); } .modal-form-primary { - color: var(--text-accent); + color: var(--text-accent); } .modal-form-hint { - color: var(--text-muted); + color: var(--text-muted); } .modal-form { - --shadow-bottom: 0 0.8rem 0.8rem -0.8rem; + --shadow-bottom: 0 0.8rem 0.8rem -0.8rem; } /* .modal-form-hint code { color: var(--text-normal); background-color: var(--background-secondary); } */ + +.modal-form-textarea { + display: flex; + flex-direction: column; + align-items: flex-start; + gap: var(--mf-spacing); +} + +.modal-form-textarea .setting-item-control { + flex: 1; + width: 100%; +} + +.modal-form-textarea .setting-item-control textarea { + width: 100%; +}