import { FilterLabelsByType } from "@triliumnext/commons"; import { Fragment, VNode } from "preact"; import { useContext } from "preact/hooks"; import Component from "../../components/component"; import FNote from "../../entities/fnote"; import NoteContextAwareWidget from "../note_context_aware_widget"; import { FormDropdownDivider, FormDropdownSubmenu, FormListItem, FormListToggleableItem } from "./FormList"; import FormTextBox from "./FormTextBox"; import { useNoteLabelBoolean, useNoteLabelWithDefault } from "./hooks"; import { ParentComponent } from "./react_utils"; export interface ClickContext { note: FNote; triggerCommand: NoteContextAwareWidget["triggerCommand"]; } export interface CheckBoxProperty { type: "checkbox", label: string; bindToLabel: FilterLabelsByType; icon?: string; } export interface ButtonProperty { type: "button", label: string; title?: string; icon?: string; onClick(context: ClickContext): void; } export interface SplitButtonProperty extends Omit { type: "split-button"; items({ note, parentComponent }: { note: FNote, parentComponent: Component }): VNode; } export interface NumberProperty { type: "number", label: string; bindToLabel: FilterLabelsByType; width?: number; min?: number; icon?: string; disabled?: (note: FNote) => boolean; } export interface ComboBoxItem { value: string; label: string; } interface ComboBoxGroup { title: string; items: ComboBoxItem[]; } interface ComboBoxSeparator { type: "separator" } export interface ComboBoxProperty { type: "combobox", label: string; icon?: string; bindToLabel: FilterLabelsByType; /** * The default value is used when the label is not set. */ defaultValue?: string; options: (ComboBoxItem | ComboBoxSeparator | ComboBoxGroup)[]; dropStart?: boolean; } export type BookProperty = CheckBoxProperty | ButtonProperty | NumberProperty | ComboBoxProperty | SplitButtonProperty; export function ViewProperty({ note, property }: { note: FNote, property: BookProperty }) { switch (property.type) { case "button": return ; case "split-button": return ; case "checkbox": return ; case "number": return ; case "combobox": return ; } } function ButtonPropertyView({ note, property }: { note: FNote, property: ButtonProperty }) { const parentComponent = useContext(ParentComponent); return ( { if (!parentComponent) return; property.onClick({ note, triggerCommand: parentComponent.triggerCommand.bind(parentComponent) }); }} >{property.label} ); } function SplitButtonPropertyView({ note, property }: { note: FNote, property: SplitButtonProperty }) { const parentComponent = useContext(ParentComponent); const ItemsComponent = property.items; const clickContext = parentComponent && { note, triggerCommand: parentComponent.triggerCommand.bind(parentComponent) }; return (parentComponent && clickContext && property.onClick(clickContext)} > ); } function NumberPropertyView({ note, property }: { note: FNote, property: NumberProperty }) { //@ts-expect-error Interop with text box which takes in string values even for numbers. const [ value, setValue ] = useNoteLabel(note, property.bindToLabel); const disabled = property.disabled?.(note); return ( e.stopPropagation()} > {property.label} ); } function ComboBoxPropertyView({ note, property }: { note: FNote, property: ComboBoxProperty }) { const [ value, setValue ] = useNoteLabelWithDefault(note, property.bindToLabel, property.defaultValue ?? ""); function renderItem(option: ComboBoxItem) { return ( setValue(option.value)} > {option.label} ); } return ( {(property.options).map((option, index) => { if ("items" in option) { return ( {option.title} {option.items.map(renderItem)} {index < property.options.length - 1 && } ); } if ("type" in option) { return ; } return renderItem(option); })} ); } function CheckBoxPropertyView({ note, property }: { note: FNote, property: CheckBoxProperty }) { const [ value, setValue ] = useNoteLabelBoolean(note, property.bindToLabel); return ( ); }