').attr("data-ribbon-component-id", ribbonWidget.componentId).append(ribbonWidget.render()));
}
for (const buttonWidget of this.buttonWidgets) {
this.$buttonContainer.append(buttonWidget.render());
}
this.$tabContainer.on("click", ".ribbon-tab-title", (e) => {
const $ribbonTitle = $(e.target).closest(".ribbon-tab-title");
this.toggleRibbonTab($ribbonTitle);
});
}
toggleRibbonTab($ribbonTitle: JQuery
, refreshActiveTab = true) {
const activate = !$ribbonTitle.hasClass("active");
this.$tabContainer.find(".ribbon-tab-title").removeClass("active");
this.$bodyContainer.find(".ribbon-body").removeClass("active");
if (activate) {
const ribbonComponendId = $ribbonTitle.attr("data-ribbon-component-id");
const wasAlreadyActive = this.lastActiveComponentId === ribbonComponendId;
this.lastActiveComponentId = ribbonComponendId;
this.$tabContainer.find(`.ribbon-tab-title[data-ribbon-component-id="${ribbonComponendId}"]`).addClass("active");
this.$bodyContainer.find(`.ribbon-body[data-ribbon-component-id="${ribbonComponendId}"]`).addClass("active");
const activeChild = this.getActiveRibbonWidget();
if (activeChild && (refreshActiveTab || !wasAlreadyActive) && this.noteContext && this.notePath) {
const handleEventPromise = activeChild.handleEvent("noteSwitched", { noteContext: this.noteContext, notePath: this.notePath });
if (refreshActiveTab) {
if (handleEventPromise) {
handleEventPromise.then(() => (activeChild as any).focus()); // TODO: Base class
} else {
// TODO: Base class
(activeChild as any)?.focus();
}
}
}
} else {
this.lastActiveComponentId = null;
}
}
async noteSwitched() {
this.lastActiveComponentId = null;
await super.noteSwitched();
}
async refreshWithNote(note: FNote, noExplicitActivation = false) {
this.lastNoteType = note.type;
let $ribbonTabToActivate, $lastActiveRibbon;
this.$tabContainer.empty();
for (const ribbonWidget of this.ribbonWidgets) {
// TODO: Base class for ribbon widget
const ret = await (ribbonWidget as any).getTitle(note);
if (!ret.show) {
continue;
}
const $ribbonTitle = $('')
.attr("data-ribbon-component-id", ribbonWidget.componentId)
.attr("data-ribbon-component-name", (ribbonWidget as any).name as string) // TODO: base class for ribbon widgets
.append(
$('
')
.addClass(ret.icon)
.attr("title", ret.title)
.attr("data-toggle-command", (ribbonWidget as any).toggleCommand)
) // TODO: base class
.append(" ")
.append($('').text(ret.title));
this.$tabContainer.append($ribbonTitle);
this.$tabContainer.append('');
if (ret.activate && !this.lastActiveComponentId && !$ribbonTabToActivate && !noExplicitActivation) {
$ribbonTabToActivate = $ribbonTitle;
}
if (this.lastActiveComponentId === ribbonWidget.componentId) {
$lastActiveRibbon = $ribbonTitle;
}
}
keyboardActionsService.getActions().then((actions) => {
this.$tabContainer.find(".ribbon-tab-title-icon").tooltip({
title: () => {
const toggleCommandName = $(this).attr("data-toggle-command");
const action = actions.find((act) => act.actionName === toggleCommandName);
const title = $(this).attr("data-title");
if (action && action.effectiveShortcuts.length > 0) {
return `${title} (${action.effectiveShortcuts.join(", ")})`;
} else {
return title ?? "";
}
}
});
});
if (!$ribbonTabToActivate) {
$ribbonTabToActivate = $lastActiveRibbon;
}
if ($ribbonTabToActivate) {
this.toggleRibbonTab($ribbonTabToActivate, false);
} else {
this.$bodyContainer.find(".ribbon-body").removeClass("active");
}
}
isRibbonTabActive(name: string) {
const $ribbonComponent = this.$widget.find(`.ribbon-tab-title[data-ribbon-component-name='${name}']`);
return $ribbonComponent.hasClass("active");
}
ensureOwnedAttributesAreOpen(ntxId: string | null | undefined) {
if (ntxId && this.isNoteContext(ntxId) && !this.isRibbonTabActive("ownedAttributes")) {
this.toggleRibbonTabWithName("ownedAttributes", ntxId);
}
}
addNewLabelEvent({ ntxId }: EventData<"addNewLabel">) {
this.ensureOwnedAttributesAreOpen(ntxId);
}
addNewRelationEvent({ ntxId }: EventData<"addNewRelation">) {
this.ensureOwnedAttributesAreOpen(ntxId);
}
toggleRibbonTabWithName(name: string, ntxId?: string) {
if (!this.isNoteContext(ntxId)) {
return false;
}
const $ribbonComponent = this.$widget.find(`.ribbon-tab-title[data-ribbon-component-name='${name}']`);
if ($ribbonComponent) {
this.toggleRibbonTab($ribbonComponent);
}
}
handleEvent
(name: T, data: EventData) {
const PREFIX = "toggleRibbonTab";
if (name.startsWith(PREFIX)) {
let componentName = name.substr(PREFIX.length);
componentName = componentName[0].toLowerCase() + componentName.substr(1);
this.toggleRibbonTabWithName(componentName, (data as any).ntxId);
} else {
return super.handleEvent(name, data);
}
}
async handleEventInChildren(name: T, data: EventData) {
if (["activeContextChanged", "setNoteContext"].includes(name)) {
// won't trigger .refresh();
await super.handleEventInChildren("setNoteContext", data as EventData<"activeContextChanged" | "setNoteContext">);
} else if (this.isEnabled() || name === "initialRenderComplete") {
const activeRibbonWidget = this.getActiveRibbonWidget();
// forward events only to active ribbon tab, inactive ones don't need to be updated
if (activeRibbonWidget) {
await activeRibbonWidget.handleEvent(name, data);
}
for (const buttonWidget of this.buttonWidgets) {
await buttonWidget.handleEvent(name, data);
}
}
}
entitiesReloadedEvent({ loadResults }: EventData<"entitiesReloaded">) {
if (!this.note) {
return;
}
if (this.noteId && loadResults.isNoteReloaded(this.noteId) && this.lastNoteType !== this.note.type) {
// note type influences the list of available ribbon tabs the most
// check for the type is so that we don't update on each title rename
this.lastNoteType = this.note.type;
this.refresh();
} else if (loadResults.getAttributeRows(this.componentId).find((attr) => attributeService.isAffecting(attr, this.note))) {
this.refreshWithNote(this.note, true);
}
}
async noteTypeMimeChangedEvent() {
// We are ignoring the event which triggers a refresh since it is usually already done by a different
// event and causing a race condition in which the items appear twice.
}
/**
* Executed as soon as the user presses the "Edit" floating button in a read-only text note.
*
*
* We need to refresh the ribbon for cases such as the classic editor which relies on the read-only state.
*/
readOnlyTemporarilyDisabledEvent() {
this.refresh();
}
getActiveRibbonWidget() {
return this.ribbonWidgets.find((ch) => ch.componentId === this.lastActiveComponentId);
}
}