mirror of
https://github.com/gristlabs/grist-core.git
synced 2024-10-27 20:44:07 +00:00
(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:
parent
f224afcc62
commit
afa90cc365
@ -22,6 +22,7 @@ const {copyToClipboard} = require('app/client/lib/copyToClipboard');
|
|||||||
const {setTestState} = require('app/client/lib/testState');
|
const {setTestState} = require('app/client/lib/testState');
|
||||||
const {ExtraRows} = require('app/client/models/DataTableModelWithDiff');
|
const {ExtraRows} = require('app/client/models/DataTableModelWithDiff');
|
||||||
const {createFilterMenu} = require('app/client/ui/ColumnFilterMenu');
|
const {createFilterMenu} = require('app/client/ui/ColumnFilterMenu');
|
||||||
|
const {closeRegisteredMenu} = require('app/client/ui2018/menus');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* BaseView forms the basis for ViewSection classes.
|
* BaseView forms the basis for ViewSection classes.
|
||||||
@ -219,7 +220,7 @@ BaseView.commonCommands = {
|
|||||||
this.scrollToCursor(true).catch(reportError);
|
this.scrollToCursor(true).catch(reportError);
|
||||||
this.activateEditorAtCursor({init});
|
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()); },
|
insertRecordBefore: function() { this.insertRow(this.cursor.rowIndex()); },
|
||||||
insertRecordAfter: function() { this.insertRow(this.cursor.rowIndex() + 1); },
|
insertRecordAfter: function() { this.insertRow(this.cursor.rowIndex() + 1); },
|
||||||
|
@ -20,6 +20,7 @@ import {dom as grains} from 'grainjs';
|
|||||||
import * as ko from 'knockout';
|
import * as ko from 'knockout';
|
||||||
import defaults = require('lodash/defaults');
|
import defaults = require('lodash/defaults');
|
||||||
import {AccessLevel} from 'app/common/CustomWidget';
|
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
|
* 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()) {
|
if (!this.viewSection.isDisposed() && !this.viewSection.hasFocus()) {
|
||||||
this.viewSection.hasFocus(true);
|
this.viewSection.hasFocus(true);
|
||||||
}
|
}
|
||||||
|
// allow menus to close if any
|
||||||
|
closeRegisteredMenu();
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -29,7 +29,7 @@ const {onDblClickMatchElem} = require('app/client/lib/dblclick');
|
|||||||
|
|
||||||
// Grist UI Components
|
// Grist UI Components
|
||||||
const {dom: grainjsDom, Holder, Computed} = require('grainjs');
|
const {dom: grainjsDom, Holder, Computed} = require('grainjs');
|
||||||
const {menu} = require('../ui2018/menus');
|
const {closeRegisteredMenu, menu} = require('../ui2018/menus');
|
||||||
const {calcFieldsCondition} = require('../ui/GridViewMenus');
|
const {calcFieldsCondition} = require('../ui/GridViewMenus');
|
||||||
const {ColumnAddMenu, ColumnContextMenu, MultiColumnMenu, freezeAction} = require('../ui/GridViewMenus');
|
const {ColumnAddMenu, ColumnContextMenu, MultiColumnMenu, freezeAction} = require('../ui/GridViewMenus');
|
||||||
const {RowContextMenu} = require('../ui/RowContextMenu');
|
const {RowContextMenu} = require('../ui/RowContextMenu');
|
||||||
@ -273,7 +273,7 @@ GridView.gridCommands = {
|
|||||||
|
|
||||||
fieldEditSave: function() { this.cursor.rowIndex(this.cursor.rowIndex() + 1); },
|
fieldEditSave: function() { this.cursor.rowIndex(this.cursor.rowIndex() + 1); },
|
||||||
// Re-define editField after fieldEditSave to make it take precedence for the Enter key.
|
// 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() {
|
deleteRecords: function() {
|
||||||
const saved = this.cursor.getCursorPos();
|
const saved = this.cursor.getCursorPos();
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
* `dom.on('contextmenu', ev => ev.preventDefault())`
|
* `dom.on('contextmenu', ev => ev.preventDefault())`
|
||||||
*/
|
*/
|
||||||
import { Disposable, dom, DomArg, DomContents, Holder } from "grainjs";
|
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';
|
import { IOpenController, Menu } from 'popweasel';
|
||||||
|
|
||||||
export type IContextMenuContentFunc = (ctx: ContextMenuController) => DomContents;
|
export type IContextMenuContentFunc = (ctx: ContextMenuController) => DomContents;
|
||||||
@ -50,6 +50,8 @@ class ContextMenuController extends Disposable implements IOpenController {
|
|||||||
dom.domDispose(content);
|
dom.domDispose(content);
|
||||||
content.remove();
|
content.remove();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
registerMenuOpen(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
public close() {
|
public close() {
|
||||||
|
@ -24,6 +24,9 @@ export function gristLink(href: string|Observable<string>, ...args: IDomArgs<HTM
|
|||||||
dom.attr("href", href),
|
dom.attr("href", href),
|
||||||
dom.attr("target", "_blank"),
|
dom.attr("target", "_blank"),
|
||||||
dom.on("click", ev => onClickHyperLink(ev, typeof href === 'string' ? href : href.get())),
|
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
|
// As per Google and Mozilla recommendations to prevent opened links
|
||||||
// from running on the same process as Grist:
|
// from running on the same process as Grist:
|
||||||
// https://developers.google.com/web/tools/lighthouse/audits/noopener
|
// https://developers.google.com/web/tools/lighthouse/audits/noopener
|
||||||
|
@ -17,11 +17,28 @@ export interface IOptionFull<T> {
|
|||||||
icon?: IconName;
|
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.
|
// 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 type IOption<T> = (T & string) | IOptionFull<T>;
|
||||||
|
|
||||||
export function menu(createFunc: weasel.MenuCreateFunc, options?: weasel.IMenuOptions): DomElementMethod {
|
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.
|
// TODO Weasel doesn't allow other options for submenus, but probably should.
|
||||||
|
Loading…
Reference in New Issue
Block a user