(core) Updating flow and UI for shortcut warnings

Summary:
- Popup looks different (better shadow, order and alignment)
- Warnings need to be dismissed by checking "Don't show again" button, pressing
  Esc/Enter or clicking away just hides the popup, but it will be opened once again.
- Dismissing one warning popup (about zoom keys), dismisses them all

Test Plan: Updated

Reviewers: georgegevoian

Reviewed By: georgegevoian

Differential Revision: https://phab.getgrist.com/D3683
This commit is contained in:
Jarosław Sadziński 2022-10-25 21:04:23 +02:00
parent 64710b60f3
commit fb16c3de56
4 changed files with 49 additions and 22 deletions

View File

@ -63,28 +63,46 @@ export class DeprecatedCommands extends Disposable {
} }
private _handleCommand(c: Command) { private _handleCommand(c: Command) {
const seenWarnings = this._gristDoc.docPageModel.appModel.deprecatedWarnings;
if (!this._hasSeenWarning(c.name)) { if (!this._hasSeenWarning(c.name)) {
markAsSeen(seenWarnings, c.name); this._showWarning(c);
this._showWarning(c.desc);
return false; // Stop processing. return false; // Stop processing.
} else { } else {
return true; // Continue processing. return true; // Continue processing.
} }
} }
private _showWarning(desc: string) { private _showWarning(c: Command) {
// Try to figure out where to show the message. If we have active view, we can try // Try to figure out where to show the message. If we have active view, we can try
// to find the selected cell and show the message there. Otherwise, we show it in the // to find the selected cell and show the message there. Otherwise, we show it in the
// bottom right corner as a warning. // bottom right corner as a warning.
const selectedCell = this._gristDoc.currentView.get()?.viewPane.querySelector(".selected_cursor"); const selectedCell = this._gristDoc.currentView.get()?.viewPane.querySelector(".selected_cursor");
const seenWarnings = this._gristDoc.docPageModel.appModel.deprecatedWarnings;
function onClose(checked: boolean) {
if (checked) {
// For deprecated zoom commands we have the same messages, so mark them as seen.
const zoomCommands: DeprecationWarning[] = [
'deprecatedDeleteRecords',
'deprecatedInsertRecordAfter',
'deprecatedInsertRowBefore',
];
if (zoomCommands.includes(c.name as any)) {
zoomCommands.forEach((name) => markAsSeen(seenWarnings, name));
} else {
markAsSeen(seenWarnings, c.name);
}
}
}
if (!selectedCell) { if (!selectedCell) {
reportMessage(() => dom('div', this._createMessage(desc)), { reportMessage(() => dom('div', this._createMessage(c.desc)), {
level: 'info', level: 'info',
key: 'deprecated-command', key: 'deprecated-command',
}); });
} else { } else {
showDeprecatedWarning(selectedCell, this._createMessage(desc)); showDeprecatedWarning(
selectedCell,
this._createMessage(c.desc),
onClose
);
} }
} }
@ -103,7 +121,7 @@ export class DeprecatedCommands extends Disposable {
if (part[0] === '{') { if (part[0] === '{') {
const otherCommand = commands.allCommands[part.slice(1, -1)]; const otherCommand = commands.allCommands[part.slice(1, -1)];
if (otherCommand) { if (otherCommand) {
elements.push(otherCommand.getKeysDom()); elements.push(otherCommand.getKeysDom(() => dom('span', 'or')));
} }
} else { } else {
elements.push(cssTallerText(part)); elements.push(cssTallerText(part));

View File

@ -159,9 +159,10 @@ Command.prototype.getDesc = function() {
/** /**
* Returns DOM for the keyboard shortcuts, wrapped in cute boxes that look like keyboard keys. * Returns DOM for the keyboard shortcuts, wrapped in cute boxes that look like keyboard keys.
*/ */
Command.prototype.getKeysDom = function() { Command.prototype.getKeysDom = function(separator) {
return dom('span.shortcut_keys', return dom('span.shortcut_keys',
this.humanKeys.map(key => dom('span.shortcut_key_image', key)) separator ? this.humanKeys.map((key, i) => [i ? separator() : null, dom('span.shortcut_key_image', key)])
: this.humanKeys.map(key => dom('span.shortcut_key_image', key))
); );
}; };

View File

@ -35,16 +35,16 @@ export function buildConfirmDelete(
dom('div', `Are you sure you want to delete ${single ? 'this' : 'these'} record${single ? '' : 's'}?`, dom('div', `Are you sure you want to delete ${single ? 'this' : 'these'} record${single ? '' : 's'}?`,
dom.style('margin-bottom', '10px'), dom.style('margin-bottom', '10px'),
), ),
dom('div',
labeledSquareCheckbox(remember, "Don't ask again.", testId('confirm-remember')),
dom.style('margin-bottom', '10px'),
),
cssButtons( cssButtons(
dom.style('margin-bottom', '12px'),
primaryButton('Delete', testId('confirm-save'), dom.on('click', () => { primaryButton('Delete', testId('confirm-save'), dom.on('click', () => {
onSave(remember.get()); onSave(remember.get());
ctl.close(); ctl.close();
})), })),
basicButton('Cancel', testId('confirm-cancel'), dom.on('click', () => ctl.close())) basicButton('Cancel', testId('confirm-cancel'), dom.on('click', () => ctl.close()))
),
dom('div',
labeledSquareCheckbox(remember, "Don't ask again.", testId('confirm-remember')),
) )
), {} ), {}
); );
@ -60,21 +60,29 @@ export function buildConfirmDelete(
export function showDeprecatedWarning( export function showDeprecatedWarning(
refElement: Element, refElement: Element,
content: DomContents content: DomContents,
onClose: (checked: boolean) => void,
) { ) {
const remember = observable(false);
const tooltip = modalTooltip(refElement, (ctl) => const tooltip = modalTooltip(refElement, (ctl) =>
cssWideContainer( cssWideContainer(
testId('popup-warning-deprecated'), testId('popup-warning-deprecated'),
elem => { FocusLayer.create(ctl, {defaultFocusElem: elem, pauseMousetrap: true}); }, elem => { FocusLayer.create(ctl, {defaultFocusElem: elem, pauseMousetrap: true}); },
dom.onKeyDown({ dom.onKeyDown({
Escape: () => ctl.close(), Escape: () => { ctl.close(); onClose(remember.get()); },
Enter: () => ctl.close(), Enter: () => { ctl.close(); onClose(remember.get()); },
}), }),
content, content,
cssButtons( cssButtons(
dom.style('margin-top', '12px'), dom.style('margin-top', '12px'),
dom.style('justify-content', 'right'), dom.style('justify-content', 'space-between'),
basicButton('Close', testId('confirm-cancel'), dom.on('click', () => ctl.close())) dom.style('align-items', 'center'),
dom('div',
labeledSquareCheckbox(remember, "Don't show again.", testId('confirm-remember')),
),
basicButton('Dismiss', testId('confirm-save'),
dom.on('click', () => { ctl.close(); onClose(remember.get()); })
)
), ),
) )
); );
@ -133,7 +141,7 @@ const cssButtons = styled('div', `
`); `);
const cssContainer = styled(cssTheme, ` const cssContainer = styled(cssTheme, `
max-width: 210px; max-width: 270px;
`); `);
const cssWideContainer = styled(cssTheme, ` const cssWideContainer = styled(cssTheme, `

View File

@ -9,6 +9,7 @@ import {waitGrainObs} from 'app/common/gutil';
import {IOpenController, IPopupDomCreator, IPopupOptions, PopupControl, popupOpen} from 'popweasel'; import {IOpenController, IPopupDomCreator, IPopupOptions, PopupControl, popupOpen} from 'popweasel';
import {Computed, Disposable, dom, DomContents, DomElementArg, input, keyframes, import {Computed, Disposable, dom, DomContents, DomElementArg, input, keyframes,
MultiHolder, Observable, styled} from 'grainjs'; MultiHolder, Observable, styled} from 'grainjs';
import {cssMenuElem} from 'app/client/ui2018/menus';
// IModalControl is passed into the function creating the body of the modal. // IModalControl is passed into the function creating the body of the modal.
export interface IModalControl { export interface IModalControl {
@ -492,11 +493,10 @@ export function modalTooltip(
/* CSS styled components */ /* CSS styled components */
const cssModalTooltip = styled('div', ` const cssModalTooltip = styled(cssMenuElem, `
padding: 16px; padding: 16px 24px;
background: ${theme.modalBg}; background: ${theme.modalBg};
border-radius: 3px; border-radius: 3px;
box-shadow: 0 2px 18px 0 ${theme.modalInnerShadow}, 0 0 1px 0 ${theme.modalOuterShadow};
outline: none; outline: none;
& > div { & > div {
outline: none; outline: none;