// Copyright 2024 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import type {CrActionMenuElement} from '//resources/cr_elements/cr_action_menu/cr_action_menu.js';
import {AnchorAlignment} from '//resources/cr_elements/cr_action_menu/cr_action_menu.js';
// Determined by experimentation - can be adjusted to fine tune for different
// platforms.
export const minOverflowLengthToScroll = 75;
export const spinnerDebounceTimeout = 150;
export const playFromSelectionTimeout = spinnerDebounceTimeout + 25;
export const toastDurationMs = 10000;
// Events emitted from the toolbar to the app
export enum ToolbarEvent {
LETTER_SPACING = 'letter-spacing-change',
LINE_SPACING = 'line-spacing-change',
THEME = 'theme-change',
FONT_SIZE = 'font-size-change',
FONT = 'font-change',
RATE = 'rate-change',
PLAY_PAUSE = 'play-pause-click',
HIGHLIGHT_TOGGLE = 'highlight-toggle',
NEXT_GRANULARITY = 'next-granularity-click',
PREVIOUS_GRANULARITY = 'previous-granularity-click',
LINKS = 'links-toggle',
IMAGES = 'images-toggle',
VOICE = 'select-voice',
LANGUAGE_TOGGLE = 'voice-language-toggle',
PLAY_PREVIEW = 'preview-voice',
}
// The user settings stored in preferences and restored on re-opening Reading
// mode. Used to set the initial values for the toolbar buttons and menus.
export interface SettingsPrefs {
letterSpacing: number;
lineSpacing: number;
theme: number;
speechRate: number;
font: string;
}
const ACTIVE_CSS_CLASS = 'active';
export function getCurrentSpeechRate(): number {
return parseFloat(chrome.readingMode.speechRate.toFixed(1));
}
// Propagates a custom event with the given name and any details.
export function emitEvent(
target: EventTarget, name: string, eventDetail?: any) {
target.dispatchEvent(new CustomEvent(name, {
bubbles: true,
composed: true,
detail: eventDetail,
}));
}
export function openMenu(
menuToOpen: CrActionMenuElement, target: HTMLElement,
showAtConfig?: {minX: number, maxX: number}) {
// The button should stay active while the menu is open and deactivate when
// the menu closes.
menuToOpen.addEventListener('close', () => {
target.classList.remove(ACTIVE_CSS_CLASS);
});
target.classList.add(ACTIVE_CSS_CLASS);
// TODO(b/337058857): We shouldn't need to wrap this twice in
// requestAnimationFrame in order to get an accessible label to be read by
// ChromeVox. We should investigate more in what's going on with
// cr-action-menu to find a better long-term solution. This is sufficient
// for now.
requestAnimationFrame(() => {
requestAnimationFrame(() => {
const minY = target.getBoundingClientRect().bottom;
menuToOpen.showAt(
target,
Object.assign(
{
minY: minY,
anchorAlignmentX: AnchorAlignment.AFTER_START,
anchorAlignmentY: AnchorAlignment.AFTER_END,
noOffset: true,
},
showAtConfig));
});
});
}