/** * Copyright (c) Tiny Technologies, Inc. All rights reserved. * Licensed under the LGPL or a commercial license. * For LGPL see License.txt in the project root for license information. * For commercial licenses see https://www.tiny.cloud/ */ import { AlloyComponent, Disabling, SketchSpec, Tabstopping, AlloyTriggers, MementoRecord } from '@ephox/alloy'; import { Toolbar, Types } from '@ephox/bridge'; import { Option, Arr, Cell } from '@ephox/katamari'; import { UiFactoryBackstage } from '../../backstage/Backstage'; import { renderCommonDropdown } from '../dropdown/CommonDropdown'; import * as NestedMenus from '../menus/menu/NestedMenus'; import ItemResponse from '../menus/item/ItemResponse'; import { ToolbarButtonClasses } from '../toolbar/button/ButtonClasses'; import { Attr, Class, Focus } from '@ephox/sugar'; import { Omit } from '../Omit'; import { formActionEvent } from 'tinymce/themes/silver/ui/general/FormEvents'; export type MenuButtonSpec = Omit; const getMenuButtonApi = (component: AlloyComponent): Toolbar.ToolbarMenuButtonInstanceApi => { return { isDisabled: () => Disabling.isDisabled(component), setDisabled: (state: boolean) => Disabling.set(component, state), setActive: (state: boolean) => { // Note: We can't use the toggling behaviour here, as the dropdown for the menu also relies on it. // As such, we'll need to do this manually const elm = component.element(); if (state) { Class.add(elm, ToolbarButtonClasses.Ticked); Attr.set(elm, 'aria-pressed', true); } else { Class.remove(elm, ToolbarButtonClasses.Ticked); Attr.remove(elm, 'aria-pressed'); } }, isActive: () => Class.has(component.element(), ToolbarButtonClasses.Ticked) }; }; const renderMenuButton = (spec: MenuButtonSpec, prefix: string, backstage: UiFactoryBackstage, role: Option): SketchSpec => { return renderCommonDropdown({ text: spec.text, icon: spec.icon, tooltip: spec.tooltip, // https://www.w3.org/TR/wai-aria-practices/examples/menubar/menubar-2/menubar-2.html role, fetch: (callback) => { spec.fetch((items) => { callback( NestedMenus.build(items, ItemResponse.CLOSE_ON_EXECUTE, backstage) ); }); }, onSetup: spec.onSetup, getApi: getMenuButtonApi, columns: 1, presets: 'normal', classes: [], dropdownBehaviours: [ Tabstopping.config({ }) ] }, prefix, backstage.shared); }; interface StoragedMenuItem extends Types.Dialog.DialogToggleMenuItem { storage: Cell; } interface StoragedMenuButton extends Omit { items: StoragedMenuItem[]; } const getFetch = (items: StoragedMenuItem[], getButton: () => MementoRecord, backstage: UiFactoryBackstage) => { const getMenuItemAction = (item) => { return (api) => { backstage.shared.getSink().each((sink) => { getButton().getOpt(sink).each((orig) => { Focus.focus(orig.element()); AlloyTriggers.emitWith(orig, formActionEvent, { name: item.name, value: item.storage.get() }); }); }); const newValue = !api.isActive(); api.setActive(newValue); item.storage.set(newValue); }; }; const getMenuItemSetup = (item) => { return (api) => { api.setActive(item.storage.get()); }; }; return (success) => { success(Arr.map(items, (item) => { const text = item.text.fold(() => { return {}; }, (text) => { return { text }; }); return { type: item.type, ...text, onAction: getMenuItemAction(item), onSetup: getMenuItemSetup(item) }; })); }; }; export { renderMenuButton, getFetch, StoragedMenuItem, StoragedMenuButton };