feat(react/ribbon): reintroduce button collection properties

This commit is contained in:
Elian Doran 2025-08-23 23:22:07 +03:00
parent ce1f5c6204
commit ea1397de63
No known key found for this signature in database
4 changed files with 51 additions and 41 deletions

View File

@ -19,9 +19,10 @@ export interface ButtonProps {
size?: "normal" | "small" | "micro"; size?: "normal" | "small" | "micro";
style?: CSSProperties; style?: CSSProperties;
triggerCommand?: CommandNames; triggerCommand?: CommandNames;
title?: string;
} }
const Button = memo(({ name, buttonRef: _buttonRef, className, text, onClick, keyboardShortcut, icon, primary, disabled, size, style, triggerCommand }: ButtonProps) => { const Button = memo(({ name, buttonRef: _buttonRef, className, text, onClick, keyboardShortcut, icon, primary, disabled, size, style, triggerCommand, ...restProps }: ButtonProps) => {
// Memoize classes array to prevent recreation // Memoize classes array to prevent recreation
const classes = useMemo(() => { const classes = useMemo(() => {
const classList: string[] = ["btn"]; const classList: string[] = ["btn"];
@ -65,6 +66,7 @@ const Button = memo(({ name, buttonRef: _buttonRef, className, text, onClick, ke
disabled={disabled} disabled={disabled}
style={style} style={style}
data-trigger-command={triggerCommand} data-trigger-command={triggerCommand}
{...restProps}
> >
{icon && <span className={`bx ${icon}`}></span>} {icon && <span className={`bx ${icon}`}></span>}
{text} {shortcutElements} {text} {shortcutElements}

View File

@ -1,12 +1,14 @@
import { 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 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 } from "../react/hooks"; import { useNoteLabel } from "../react/hooks";
import { bookPropertiesConfig, BookProperty, ButtonProperty } from "../ribbon_widgets/book_properties_config";
import Button from "../react/Button";
import { ParentComponent } from "../react/react_utils";
import FNote from "../../entities/fnote"; import FNote from "../../entities/fnote";
import FormGroup from "../react/FormGroup";
const VIEW_TYPE_MAPPINGS: Record<ViewTypeOptions, string> = { const VIEW_TYPE_MAPPINGS: Record<ViewTypeOptions, string> = {
grid: t("book_properties.grid"), grid: t("book_properties.grid"),
@ -18,19 +20,20 @@ const VIEW_TYPE_MAPPINGS: Record<ViewTypeOptions, string> = {
}; };
export default function CollectionPropertiesTab({ note }: TabContext) { export default function CollectionPropertiesTab({ note }: TabContext) {
const [ viewType, setViewType ] = useNoteLabel(note, "viewType");
const viewTypeWithDefault = viewType ?? "grid";
const properties = bookPropertiesConfig[viewTypeWithDefault].properties;
return (note && return (note &&
<div className="book-properties-widget"> <div className="book-properties-widget">
<CollectionTypeSwitcher note={note} /> <CollectionTypeSwitcher viewType={viewTypeWithDefault} setViewType={setViewType} />
<BookProperties note={note} properties={properties} />
<div className="book-properties-container">
</div>
</div> </div>
); );
} }
function CollectionTypeSwitcher({ note }: { note: FNote }) { function CollectionTypeSwitcher({ viewType, setViewType }: { viewType: string, setViewType: (newValue: string) => void }) {
const collectionTypes = useMemo(() => mapToKeyValueArray(VIEW_TYPE_MAPPINGS), []); const collectionTypes = useMemo(() => mapToKeyValueArray(VIEW_TYPE_MAPPINGS), []);
const [ viewType, setViewType ] = useNoteLabel(note, "viewType");
return ( return (
<div style={{ display: "flex", alignItems: "baseline" }}> <div style={{ display: "flex", alignItems: "baseline" }}>
@ -43,3 +46,39 @@ function CollectionTypeSwitcher({ note }: { note: FNote }) {
</div> </div>
) )
} }
function BookProperties({ note, properties }: { note: FNote, properties: BookProperty[] }) {
return (
<div className="book-properties-container">
{properties.map(property => (
<div className={`type-${property}`}>
{mapPropertyView({ note, property })}
</div>
))}
</div>
)
}
function mapPropertyView({ note, property }: { note: FNote, property: BookProperty }) {
switch (property.type) {
case "button":
return <ButtonPropertyView note={note} property={property} />
}
}
function ButtonPropertyView({ note, property }: { note: FNote, property: ButtonProperty }) {
const parentComponent = useContext(ParentComponent);
return <Button
text={property.label}
title={property.title}
icon={property.icon}
onClick={() => {
if (!parentComponent) return;
property.onClick({
note,
triggerCommand: parentComponent.triggerCommand.bind(parentComponent)
});
}}
/>
}

View File

@ -40,18 +40,6 @@ export default class BookPropertiesWidget extends NoteContextAwareWidget {
} }
} }
async toggleViewType(type: string) {
if (!this.noteId) {
return;
}
if (!VIEW_TYPE_MAPPINGS.hasOwnProperty(type)) {
throw new Error(t("book_properties.invalid_view_type", { type }));
}
await attributeService.setLabel(this.noteId, "viewType", type);
}
entitiesReloadedEvent({ loadResults }: EventData<"entitiesReloaded">) { entitiesReloadedEvent({ loadResults }: EventData<"entitiesReloaded">) {
if (loadResults.getAttributeRows().find((attr) => if (loadResults.getAttributeRows().find((attr) =>
attr.noteId === this.noteId attr.noteId === this.noteId
@ -85,25 +73,6 @@ export default class BookPropertiesWidget extends NoteContextAwareWidget {
$label.prepend($checkbox); $label.prepend($checkbox);
$container.append($label); $container.append($label);
break; break;
case "button":
const $button = $("<button>", {
type: "button",
class: "btn btn-sm"
}).text(property.label);
if (property.title) {
$button.attr("title", property.title);
}
if (property.icon) {
$button.prepend($("<span>", { class: property.icon }));
}
$button.on("click", () => {
property.onClick({
note,
triggerCommand: this.triggerCommand.bind(this)
});
});
$container.append($button);
break;
case "number": case "number":
const $numberInput = $("<input>", { const $numberInput = $("<input>", {
type: "number", type: "number",

View File

@ -15,7 +15,7 @@ interface CheckBoxProperty {
bindToLabel: string bindToLabel: string
} }
interface ButtonProperty { export interface ButtonProperty {
type: "button", type: "button",
label: string; label: string;
title?: string; title?: string;