mirror of
https://github.com/zadam/trilium.git
synced 2025-10-20 15:19:01 +02:00
feat(react/ribbon): reintroduce combobox collection properties
This commit is contained in:
parent
2b8b185b5b
commit
d7e36bdf93
@ -39,15 +39,22 @@ export default function FormSelect<T>({ name, id, onChange, style, ...restProps
|
|||||||
/**
|
/**
|
||||||
* Similar to {@link FormSelect}, but the top-level elements are actually groups.
|
* Similar to {@link FormSelect}, but the top-level elements are actually groups.
|
||||||
*/
|
*/
|
||||||
export function FormSelectWithGroups<T>({ name, id, values, keyProperty, titleProperty, currentValue, onChange }: FormSelectProps<T, FormSelectGroup<T>>) {
|
export function FormSelectWithGroups<T>({ name, id, values, keyProperty, titleProperty, currentValue, onChange }: FormSelectProps<T, FormSelectGroup<T> | T>) {
|
||||||
return (
|
return (
|
||||||
<FormSelectBody name={name} id={id} onChange={onChange}>
|
<FormSelectBody name={name} id={id} onChange={onChange}>
|
||||||
{values.map(({ title, items }) => {
|
{values.map((item) => {
|
||||||
return (
|
if (!item) return <></>;
|
||||||
<optgroup label={title}>
|
if (typeof item === "object" && "items" in item) {
|
||||||
<FormSelectGroup values={items} keyProperty={keyProperty} titleProperty={titleProperty} currentValue={currentValue} />
|
return (
|
||||||
</optgroup>
|
<optgroup label={item.title}>
|
||||||
);
|
<FormSelectGroup values={item.items} keyProperty={keyProperty} titleProperty={titleProperty} currentValue={currentValue} />
|
||||||
|
</optgroup>
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return (
|
||||||
|
<FormSelectGroup values={[ item ]} keyProperty={keyProperty} titleProperty={titleProperty} currentValue={currentValue} />
|
||||||
|
)
|
||||||
|
}
|
||||||
})}
|
})}
|
||||||
</FormSelectBody>
|
</FormSelectBody>
|
||||||
)
|
)
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
import { useContext, useMemo } from "preact/hooks";
|
import { useContext, useMemo } from "preact/hooks";
|
||||||
import { t } from "../../services/i18n";
|
import { t } from "../../services/i18n";
|
||||||
import { ViewTypeOptions } from "../../services/note_list_renderer";
|
import { ViewTypeOptions } from "../../services/note_list_renderer";
|
||||||
import FormSelect from "../react/FormSelect";
|
import FormSelect, { FormSelectWithGroups } from "../react/FormSelect";
|
||||||
import { TabContext } from "./ribbon-interface";
|
import { TabContext } from "./ribbon-interface";
|
||||||
import { mapToKeyValueArray } from "../../services/utils";
|
import { mapToKeyValueArray } from "../../services/utils";
|
||||||
import { useNoteLabel, useNoteLabelBoolean } from "../react/hooks";
|
import { useNoteLabel, useNoteLabelBoolean } from "../react/hooks";
|
||||||
import { bookPropertiesConfig, BookProperty, ButtonProperty, CheckBoxProperty, NumberProperty } from "../ribbon_widgets/book_properties_config";
|
import { bookPropertiesConfig, BookProperty, ButtonProperty, CheckBoxProperty, ComboBoxGroup, ComboBoxProperty, NumberProperty } from "../ribbon_widgets/book_properties_config";
|
||||||
import Button from "../react/Button";
|
import Button from "../react/Button";
|
||||||
import { ParentComponent } from "../react/react_utils";
|
import { ParentComponent } from "../react/react_utils";
|
||||||
import FNote from "../../entities/fnote";
|
import FNote from "../../entities/fnote";
|
||||||
@ -69,6 +69,8 @@ function mapPropertyView({ note, property }: { note: FNote, property: BookProper
|
|||||||
return <CheckboxPropertyView note={note} property={property} />
|
return <CheckboxPropertyView note={note} property={property} />
|
||||||
case "number":
|
case "number":
|
||||||
return <NumberPropertyView note={note} property={property} />
|
return <NumberPropertyView note={note} property={property} />
|
||||||
|
case "combobox":
|
||||||
|
return <ComboBoxPropertyView note={note} property={property} />
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -117,4 +119,22 @@ function NumberPropertyView({ note, property }: { note: FNote, property: NumberP
|
|||||||
</label>
|
</label>
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function ComboBoxPropertyView({ note, property }: { note: FNote, property: ComboBoxProperty }) {
|
||||||
|
const [ value, setValue ] = useNoteLabel(note, property.bindToLabel);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<label>
|
||||||
|
{property.label}
|
||||||
|
|
||||||
|
<FormSelectWithGroups
|
||||||
|
values={property.options}
|
||||||
|
keyProperty="value" titleProperty="label"
|
||||||
|
currentValue={value ?? ""} onChange={setValue}
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
</>
|
||||||
|
)
|
||||||
}
|
}
|
@ -1,105 +0,0 @@
|
|||||||
import NoteContextAwareWidget from "../note_context_aware_widget.js";
|
|
||||||
import attributeService from "../../services/attributes.js";
|
|
||||||
import { t } from "../../services/i18n.js";
|
|
||||||
import type FNote from "../../entities/fnote.js";
|
|
||||||
import type { EventData } from "../../components/app_context.js";
|
|
||||||
import { bookPropertiesConfig, BookProperty } from "./book_properties_config.js";
|
|
||||||
import attributes from "../../services/attributes.js";
|
|
||||||
|
|
||||||
export default class BookPropertiesWidget extends NoteContextAwareWidget {
|
|
||||||
|
|
||||||
private $viewTypeSelect!: JQuery<HTMLElement>;
|
|
||||||
private $propertiesContainer!: JQuery<HTMLElement>;
|
|
||||||
private labelsToWatch: string[] = [];
|
|
||||||
|
|
||||||
doRender() {
|
|
||||||
|
|
||||||
this.$viewTypeSelect = this.$widget.find(".view-type-select");
|
|
||||||
this.$viewTypeSelect.on("change", () => this.toggleViewType(String(this.$viewTypeSelect.val())));
|
|
||||||
|
|
||||||
this.$propertiesContainer = this.$widget.find(".book-properties-container");
|
|
||||||
}
|
|
||||||
|
|
||||||
async refreshWithNote(note: FNote) {
|
|
||||||
if (!this.note) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const viewType = this.note.getLabelValue("viewType") || "grid";
|
|
||||||
|
|
||||||
this.$viewTypeSelect.val(viewType);
|
|
||||||
|
|
||||||
this.$propertiesContainer.empty();
|
|
||||||
|
|
||||||
const bookPropertiesData = bookPropertiesConfig[viewType];
|
|
||||||
if (bookPropertiesData) {
|
|
||||||
for (const property of bookPropertiesData.properties) {
|
|
||||||
this.$propertiesContainer.append(this.renderBookProperty(property));
|
|
||||||
this.labelsToWatch.push(property.bindToLabel);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
entitiesReloadedEvent({ loadResults }: EventData<"entitiesReloaded">) {
|
|
||||||
if (loadResults.getAttributeRows().find((attr) =>
|
|
||||||
attr.noteId === this.noteId
|
|
||||||
&& (attr.name === "viewType" || this.labelsToWatch.includes(attr.name ?? "")))) {
|
|
||||||
this.refresh();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
renderBookProperty(property: BookProperty) {
|
|
||||||
const $container = $("<div>");
|
|
||||||
$container.addClass(`type-${property.type}`);
|
|
||||||
const note = this.note;
|
|
||||||
if (!note) {
|
|
||||||
return $container;
|
|
||||||
}
|
|
||||||
switch (property.type) {
|
|
||||||
case "combobox":
|
|
||||||
const $select = $("<select>", {
|
|
||||||
class: "form-select form-select-sm"
|
|
||||||
});
|
|
||||||
const actualValue = note.getLabelValue(property.bindToLabel) ?? property.defaultValue ?? "";
|
|
||||||
for (const option of property.options) {
|
|
||||||
if ("items" in option) {
|
|
||||||
const $optGroup = $("<optgroup>", { label: option.name });
|
|
||||||
for (const item of option.items) {
|
|
||||||
buildComboBoxItem(item, actualValue).appendTo($optGroup);
|
|
||||||
}
|
|
||||||
$optGroup.appendTo($select);
|
|
||||||
} else {
|
|
||||||
buildComboBoxItem(option, actualValue).appendTo($select);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$select.on("change", () => {
|
|
||||||
const value = $select.val();
|
|
||||||
if (value === null || value === "") {
|
|
||||||
attributes.removeOwnedLabelByName(note, property.bindToLabel);
|
|
||||||
} else {
|
|
||||||
attributes.setLabel(note.noteId, property.bindToLabel, String(value));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
$container.append($("<label>")
|
|
||||||
.text(property.label)
|
|
||||||
.append(" ".repeat(2))
|
|
||||||
.append($select));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return $container;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
function buildComboBoxItem({ value, label }: { value: string, label: string }, actualValue: string) {
|
|
||||||
const $option = $("<option>", {
|
|
||||||
value,
|
|
||||||
text: label
|
|
||||||
});
|
|
||||||
if (actualValue === value) {
|
|
||||||
$option.prop("selected", true);
|
|
||||||
}
|
|
||||||
return $option;
|
|
||||||
}
|
|
@ -37,11 +37,11 @@ interface ComboBoxItem {
|
|||||||
}
|
}
|
||||||
|
|
||||||
interface ComboBoxGroup {
|
interface ComboBoxGroup {
|
||||||
name: string;
|
title: string;
|
||||||
items: ComboBoxItem[];
|
items: ComboBoxItem[];
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ComboBoxProperty {
|
export interface ComboBoxProperty {
|
||||||
type: "combobox",
|
type: "combobox",
|
||||||
label: string;
|
label: string;
|
||||||
bindToLabel: string;
|
bindToLabel: string;
|
||||||
@ -120,19 +120,19 @@ export const bookPropertiesConfig: Record<ViewTypeOptions, BookConfig> = {
|
|||||||
defaultValue: DEFAULT_MAP_LAYER_NAME,
|
defaultValue: DEFAULT_MAP_LAYER_NAME,
|
||||||
options: [
|
options: [
|
||||||
{
|
{
|
||||||
name: t("book_properties_config.raster"),
|
title: t("book_properties_config.raster"),
|
||||||
items: Object.entries(MAP_LAYERS)
|
items: Object.entries(MAP_LAYERS)
|
||||||
.filter(([_, layer]) => layer.type === "raster")
|
.filter(([_, layer]) => layer.type === "raster")
|
||||||
.map(buildMapLayer)
|
.map(buildMapLayer)
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: t("book_properties_config.vector_light"),
|
title: t("book_properties_config.vector_light"),
|
||||||
items: Object.entries(MAP_LAYERS)
|
items: Object.entries(MAP_LAYERS)
|
||||||
.filter(([_, layer]) => layer.type === "vector" && !layer.isDarkTheme)
|
.filter(([_, layer]) => layer.type === "vector" && !layer.isDarkTheme)
|
||||||
.map(buildMapLayer)
|
.map(buildMapLayer)
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: t("book_properties_config.vector_dark"),
|
title: t("book_properties_config.vector_dark"),
|
||||||
items: Object.entries(MAP_LAYERS)
|
items: Object.entries(MAP_LAYERS)
|
||||||
.filter(([_, layer]) => layer.type === "vector" && layer.isDarkTheme)
|
.filter(([_, layer]) => layer.type === "vector" && layer.isDarkTheme)
|
||||||
.map(buildMapLayer)
|
.map(buildMapLayer)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user