文件
mynode/packages/lib/services/commands/ToolbarButtonUtils.ts
chen 301194963b
一些检查失败了
automerge / automerge (push) Has been cancelled
react-native-android-build-apk / AssembleRelease (push) Has been cancelled
Build macOS M1 / Main (push) Has been cancelled
Joplin Continuous Integration / Main (macos-15-intel) (push) Has been cancelled
Joplin Continuous Integration / Main (ubuntu-22.04) (push) Has been cancelled
Joplin Continuous Integration / Main (ubuntu-22.04-arm) (push) Has been cancelled
Joplin Continuous Integration / Main (windows-11-vs2026-arm) (push) Has been cancelled
Joplin Continuous Integration / Main (windows-2025) (push) Has been cancelled
Joplin Continuous Integration / ServerDockerImage (ubuntu-22.04) (push) Has been cancelled
Joplin Continuous Integration / ServerDockerImage (ubuntu-22.04-arm) (push) Has been cancelled
Joplin UI tests / Main (ubuntu-22.04) (push) Has been cancelled
Joplin UI tests / Main (windows-2025) (push) Has been cancelled
Joplin UI tests / Mobile (push) Has been cancelled
Close stale issues / ProcessStaleIssues (push) Has been cancelled
feat: Initial clean release of CHM
2026-11-04 11:29:39 +08:00

132 行
4.2 KiB
TypeScript

import CommandService from '../CommandService';
import { stateUtils } from '../../reducer';
import focusEditorIfEditorCommand from './focusEditorIfEditorCommand';
import { WhenClauseContext } from './stateToWhenClauseContext';
import Logger from '@joplin/utils/Logger';
import KeymapService from '../KeymapService';
import { _ } from '../../locale';
const logger = Logger.create('ToolbarButtonUtils');
export interface ToolbarButtonInfo {
type: 'button';
name: string;
tooltip: string;
iconName: string;
enabled: boolean;
visible: boolean;
onClick(): void;
title: string;
}
interface SeparatorItem extends Omit<Partial<ToolbarButtonInfo>, 'type'> {
type: 'separator';
}
export const separatorItem: SeparatorItem = {
type: 'separator',
};
export type ToolbarItem = ToolbarButtonInfo|SeparatorItem;
export type ToolbarButtonOverride = Pick<ToolbarButtonInfo, 'enabled'>;
interface ToolbarButtonCacheItem {
info: ToolbarButtonInfo;
}
interface ToolbarButtonCache {
[key: string]: ToolbarButtonCacheItem;
}
export default class ToolbarButtonUtils {
private service_: CommandService;
private toolbarButtonCache_: ToolbarButtonCache = {};
public constructor(service: CommandService) {
this.service_ = service;
}
private get service(): CommandService {
return this.service_;
}
private commandToToolbarButton(commandName: string, whenClauseContext: WhenClauseContext, keymapService: KeymapService | null, override?: ToolbarButtonOverride): ToolbarButtonInfo {
const newEnabled = override?.enabled ?? this.service.isEnabled(commandName, whenClauseContext);
const newVisible = this.service.isVisible(commandName, whenClauseContext);
const newTitle = this.service.title(commandName);
const newIcon = this.service.iconName(commandName);
let newLabel = this.service.label(commandName);
if (keymapService && keymapService.acceleratorExists(commandName)) {
const accelerator = keymapService.getDefaultAccelerator(commandName);
if (accelerator) {
const label = _('%s (%s)', newLabel, accelerator);
newLabel = newLabel ? label : accelerator;
}
}
if (
this.toolbarButtonCache_[commandName] &&
this.toolbarButtonCache_[commandName].info.enabled === newEnabled &&
this.toolbarButtonCache_[commandName].info.visible === newVisible &&
this.toolbarButtonCache_[commandName].info.title === newTitle &&
this.toolbarButtonCache_[commandName].info.iconName === newIcon &&
this.toolbarButtonCache_[commandName].info.tooltip === newLabel
) {
return this.toolbarButtonCache_[commandName].info;
}
const output: ToolbarButtonInfo = {
type: 'button',
name: commandName,
tooltip: newLabel,
iconName: newIcon,
enabled: newEnabled,
visible: newVisible,
onClick: async () => {
await this.service.execute(commandName);
void focusEditorIfEditorCommand(commandName, this.service);
},
title: newTitle,
};
this.toolbarButtonCache_[commandName] = {
info: output,
};
return this.toolbarButtonCache_[commandName].info;
}
// This method ensures that if the provided commandNames and state hasn't changed
// the output also won't change. Invididual toolbarButtonInfo also won't changed
// if the state they use hasn't changed. This is to avoid useless renders of the toolbars.
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- WhenClauseContext can be partial in tests
public commandsToToolbarButtons(commandNames: string[], whenClauseContext: any, keymapService: (KeymapService | null) = null, overrides: Record<string, ToolbarButtonOverride> = {}): ToolbarItem[] {
const output: ToolbarItem[] = [];
for (const commandName of commandNames) {
if (commandName === '-') {
const lastItem = output.length > 0 && output[output.length - 1];
if (lastItem !== separatorItem) {
output.push(separatorItem);
}
continue;
}
try {
const button = this.commandToToolbarButton(commandName, whenClauseContext, keymapService, overrides[commandName]);
if (button.visible) {
output.push(button);
}
} catch (error) {
logger.error('Unable to add toolbar button for command', commandName, '. Error: ', error);
}
}
return stateUtils.selectArrayShallow({ array: output }, commandNames.join('_'));
}
}