(core) Show default context menu on link

Summary:
also:
  - closes opened menu if any when click on a custom widget
  - closes opened menu if any when F2

Test Plan: Include test case

Reviewers: georgegevoian

Reviewed By: georgegevoian

Differential Revision: https://phab.getgrist.com/D3269
This commit is contained in:
Cyprien P 2022-02-16 15:15:27 +01:00
parent f224afcc62
commit afa90cc365
6 changed files with 31 additions and 5 deletions

View File

@ -22,6 +22,7 @@ const {copyToClipboard} = require('app/client/lib/copyToClipboard');
const {setTestState} = require('app/client/lib/testState');
const {ExtraRows} = require('app/client/models/DataTableModelWithDiff');
const {createFilterMenu} = require('app/client/ui/ColumnFilterMenu');
const {closeRegisteredMenu} = require('app/client/ui2018/menus');
/**
* BaseView forms the basis for ViewSection classes.
@ -219,7 +220,7 @@ BaseView.commonCommands = {
this.scrollToCursor(true).catch(reportError);
this.activateEditorAtCursor({init});
},
editField: function() { this.scrollToCursor(true); this.activateEditorAtCursor(); },
editField: function() { closeRegisteredMenu(); this.scrollToCursor(true); this.activateEditorAtCursor(); },
insertRecordBefore: function() { this.insertRow(this.cursor.rowIndex()); },
insertRecordAfter: function() { this.insertRow(this.cursor.rowIndex() + 1); },

View File

@ -20,6 +20,7 @@ import {dom as grains} from 'grainjs';
import * as ko from 'knockout';
import defaults = require('lodash/defaults');
import {AccessLevel} from 'app/common/CustomWidget';
import {closeRegisteredMenu} from 'app/client/ui2018/menus';
/**
* CustomView components displays arbitrary html. There are two modes available, in the "url" mode
@ -218,6 +219,8 @@ export class CustomView extends Disposable {
if (!this.viewSection.isDisposed() && !this.viewSection.hasFocus()) {
this.viewSection.hasFocus(true);
}
// allow menus to close if any
closeRegisteredMenu();
})
});

View File

@ -29,7 +29,7 @@ const {onDblClickMatchElem} = require('app/client/lib/dblclick');
// Grist UI Components
const {dom: grainjsDom, Holder, Computed} = require('grainjs');
const {menu} = require('../ui2018/menus');
const {closeRegisteredMenu, menu} = require('../ui2018/menus');
const {calcFieldsCondition} = require('../ui/GridViewMenus');
const {ColumnAddMenu, ColumnContextMenu, MultiColumnMenu, freezeAction} = require('../ui/GridViewMenus');
const {RowContextMenu} = require('../ui/RowContextMenu');
@ -273,7 +273,7 @@ GridView.gridCommands = {
fieldEditSave: function() { this.cursor.rowIndex(this.cursor.rowIndex() + 1); },
// Re-define editField after fieldEditSave to make it take precedence for the Enter key.
editField: function() { this.scrollToCursor(true); this.activateEditorAtCursor(); },
editField: function() { closeRegisteredMenu(); this.scrollToCursor(true); this.activateEditorAtCursor(); },
deleteRecords: function() {
const saved = this.cursor.getCursorPos();

View File

@ -8,7 +8,7 @@
* `dom.on('contextmenu', ev => ev.preventDefault())`
*/
import { Disposable, dom, DomArg, DomContents, Holder } from "grainjs";
import { cssMenuElem } from 'app/client/ui2018/menus';
import { cssMenuElem, registerMenuOpen } from 'app/client/ui2018/menus';
import { IOpenController, Menu } from 'popweasel';
export type IContextMenuContentFunc = (ctx: ContextMenuController) => DomContents;
@ -50,6 +50,8 @@ class ContextMenuController extends Disposable implements IOpenController {
dom.domDispose(content);
content.remove();
});
registerMenuOpen(this);
}
public close() {

View File

@ -24,6 +24,9 @@ export function gristLink(href: string|Observable<string>, ...args: IDomArgs<HTM
dom.attr("href", href),
dom.attr("target", "_blank"),
dom.on("click", ev => onClickHyperLink(ev, typeof href === 'string' ? href : href.get())),
// stop propagation to prevent the grist custom context menu to show up and let the default one
// to show up instead.
dom.on("contextmenu", ev => ev.stopPropagation()),
// As per Google and Mozilla recommendations to prevent opened links
// from running on the same process as Grist:
// https://developers.google.com/web/tools/lighthouse/audits/noopener

View File

@ -17,11 +17,28 @@ export interface IOptionFull<T> {
icon?: IconName;
}
let _lastOpenedController: weasel.IOpenController|null = null;
// Close opened menu if any, otherwise do nothing.
export function closeRegisteredMenu() {
if (_lastOpenedController) { _lastOpenedController.close(); }
}
// Register `ctl` to make sure it is closed when `closeMenu()` is called.
export function registerMenuOpen(ctl: weasel.IOpenController) {
_lastOpenedController = ctl;
ctl.onDispose(() => _lastOpenedController = null);
}
// For string options, we can use a string for label and value without wrapping into an object.
export type IOption<T> = (T & string) | IOptionFull<T>;
export function menu(createFunc: weasel.MenuCreateFunc, options?: weasel.IMenuOptions): DomElementMethod {
return weasel.menu(createFunc, {...defaults, ...options});
const wrappedCreateFunc = (ctl: weasel.IOpenController) => {
registerMenuOpen(ctl);
return createFunc(ctl);
};
return weasel.menu(wrappedCreateFunc, {...defaults, ...options});
}
// TODO Weasel doesn't allow other options for submenus, but probably should.