chore(react/ribbon): bring back tab filtering

This commit is contained in:
Elian Doran 2025-08-22 15:53:52 +03:00
parent bf0213907e
commit b99d01ad7b
No known key found for this signature in database
5 changed files with 45 additions and 68 deletions

View File

@ -788,6 +788,22 @@ export function arrayEqual<T>(a: T[], b: T[]) {
return true;
}
type Indexed<T extends object> = T & { index: number };
/**
* Given an object array, alters every object in the array to have an index field assigned to it.
*
* @param items the objects to be numbered.
* @returns the same object for convenience, with the type changed to indicate the new index field.
*/
export function numberObjectsInPlace<T extends object>(items: T[]): Indexed<T>[] {
let index = 0;
for (const item of items) {
(item as Indexed<T>).index = index++;
}
return items as Indexed<T>[];
}
export default {
reloadFrontendApp,
restartDesktopApp,

View File

@ -3,7 +3,7 @@ import Dropdown from "../react/Dropdown";
import { NOTE_TYPES } from "../../services/note_types";
import { FormDropdownDivider, FormListBadge, FormListItem } from "../react/FormList";
import { getAvailableLocales, t } from "../../services/i18n";
import { useNoteContext, useNoteLabel, useNoteLabelBoolean, useNoteProperty, useTriliumEventBeta, useTriliumOption, useTriliumOptionBeta, useTriliumOptionJson } from "../react/hooks";
import { useNoteLabel, useNoteLabelBoolean, useNoteProperty, useTriliumEventBeta, useTriliumOption, useTriliumOptionBeta, useTriliumOptionJson } from "../react/hooks";
import mime_types from "../../services/mime_types";
import { Locale, NoteType, ToggleInParentResponse } from "@triliumnext/commons";
import server from "../../services/server";
@ -17,10 +17,9 @@ import branches from "../../services/branches";
import sync from "../../services/sync";
import appContext from "../../components/app_context";
import HelpButton from "../react/HelpButton";
import { TabContext } from "./ribbon-interface";
export default function BasicPropertiesTab() {
const { note } = useNoteContext();
export default function BasicPropertiesTab({ note }: TabContext) {
return (
<div className="basic-properties-widget">
<NoteTypeWidget note={note} />

View File

@ -1,24 +1,21 @@
import { useState } from "preact/hooks";
import { useMemo, useState } from "preact/hooks";
import FNote from "../../entities/fnote";
import { t } from "../../services/i18n";
import { useNoteContext } from "../react/hooks";
import "./style.css";
import { VNode } from "preact";
import BasicPropertiesTab from "./BasicPropertiesTab";
type TitleFn = string | ((context: TabContext) => string);
interface TabContext {
note: FNote | null | undefined;
}
import { numberObjectsInPlace } from "../../services/utils";
import { TabContext } from "./ribbon-interface";
interface TabConfiguration {
title: TitleFn;
title: string | ((context: TabContext) => string);
icon: string;
content?: () => VNode;
// TODO: Mark as required after porting them all.
content?: (context: TabContext) => VNode;
show?: (context: TabContext) => boolean;
}
const TAB_CONFIGURATION: TabConfiguration[] = [
const TAB_CONFIGURATION = numberObjectsInPlace<TabConfiguration>([
{
title: t("classic_editor_toolbar.title"),
icon: "bx bx-text"
@ -63,7 +60,8 @@ const TAB_CONFIGURATION: TabConfiguration[] = [
// BasicProperties
title: t("basic_properties.basic_properties"),
icon: "bx bx-slider",
content: BasicPropertiesTab
content: BasicPropertiesTab,
show: ({note}) => !note?.isLaunchBarConfig()
},
{
// OwnedAttributeListWidget
@ -94,31 +92,31 @@ const TAB_CONFIGURATION: TabConfiguration[] = [
// NoteInfoWidget
title: t("note_info_widget.title"),
icon: "bx bx-info-circle"
},
];
}
]);
export default function Ribbon() {
const { note } = useNoteContext();
const context: TabContext = { note };
const [ activeTab, setActiveTab ] = useState<number | undefined>(8);
const activeTabConfiguration = activeTab ? TAB_CONFIGURATION[activeTab] : undefined;
const [ activeTabIndex, setActiveTabIndex ] = useState<number | undefined>();
const filteredTabs = useMemo(() => TAB_CONFIGURATION.filter(tab => tab.show?.(context)), [ context, note ])
const activeTabConfiguration = activeTabIndex ? filteredTabs.find(tab => tab.index === activeTabIndex) : undefined;
return (
<div class="ribbon-container" style={{ contain: "none" }}>
<div className="ribbon-top-row">
<div className="ribbon-tab-container">
{TAB_CONFIGURATION.map(({ title, icon }, i) => (
{filteredTabs.map(({ title, icon, index }) => (
<RibbonTab
icon={icon}
title={typeof title === "string" ? title : title(context)}
active={i === activeTab}
active={index === activeTabIndex}
onClick={() => {
if (activeTab !== i) {
setActiveTab(i);
if (activeTabIndex !== index) {
setActiveTabIndex(index);
} else {
// Collapse
setActiveTab(undefined);
setActiveTabIndex(undefined);
}
}}
/>
@ -129,7 +127,7 @@ export default function Ribbon() {
<div className="ribbon-body-container">
<div className="ribbon-body">
{activeTabConfiguration?.content && activeTabConfiguration.content()}
{activeTabConfiguration?.content && activeTabConfiguration.content({ note })}
</div>
</div>
</div>

View File

@ -0,0 +1,5 @@
import FNote from "../../entities/fnote";
export interface TabContext {
note: FNote | null | undefined;
}

View File

@ -1,41 +0,0 @@
import { t } from "../services/i18n.js";
import NoteContextAwareWidget from "./note_context_aware_widget.js";
export default class SwitchWidget extends NoteContextAwareWidget {
doRender() {
this.$widget = $(TPL);
this.$switchButton = this.$widget.find(".switch-button");
this.$switchToggle = this.$widget.find(".switch-toggle");
this.$switchName = this.$widget.find(".switch-name");
this.$helpButton = this.$widget.find(".switch-help-button");
}
switchOff() {}
switchOn() {}
/** Gets or sets whether the switch is toggled. */
get isToggled() {
return this.currentState;
}
set isToggled(state) {
this.currentState = !!state;
this.$switchButton.toggleClass("on", this.currentState);
}
/** Gets or sets whether the switch is enabled. */
get canToggle() {
return !this.$switchButton.hasClass("disabled");
}
set canToggle(isEnabled) {
if (isEnabled) {
this.isToggled = this.currentState; // Reapply the correct tooltip
} else {
this.$switchButton.attr("title", this.disabledTooltip);
}
}
}