Add confirmation support to popover

This commit is contained in:
Tanya Fomina 2022-07-29 18:51:56 +03:00
commit 58a2f97112
4 changed files with 122 additions and 27 deletions

View file

@ -72,6 +72,9 @@ export default class DeleteTune implements BlockTune {
label: this.api.i18n.t('Delete'),
onClick: (item, e): void => this.handleClick(e),
name: 'delete',
confirmation: {
label: 'Click to delete',
},
};
}
@ -85,36 +88,36 @@ export default class DeleteTune implements BlockTune {
* if block is not waiting the confirmation, subscribe on block-settings-closing event to reset
* otherwise delete block
*/
if (!this.needConfirmation) {
this.setConfirmation(true);
const button = (event.target as HTMLElement)
.closest('.' + Popover.CSS.item)
.querySelector('.' + Popover.CSS.itemIcon);
// if (!this.needConfirmation) {
// this.setConfirmation(true);
// const button = (event.target as HTMLElement)
// .closest('.' + Popover.CSS.item)
// .querySelector('.' + Popover.CSS.itemIcon);
button.classList.add(this.CSS.buttonDelete);
button.classList.add(this.CSS.buttonConfirm);
// button.classList.add(this.CSS.buttonDelete);
// button.classList.add(this.CSS.buttonConfirm);
/**
* Subscribe on event.
* When toolbar block settings is closed but block deletion is not confirmed,
* then reset confirmation state
*/
this.api.events.on('block-settings-closed', this.resetConfirmation);
} else {
/**
* Unsubscribe from block-settings closing event
*/
this.api.events.off('block-settings-closed', this.resetConfirmation);
// /**
// * Subscribe on event.
// * When toolbar block settings is closed but block deletion is not confirmed,
// * then reset confirmation state
// */
// this.api.events.on('block-settings-closed', this.resetConfirmation);
// } else {
/**
* Unsubscribe from block-settings closing event
*/
// this.api.events.off('block-settings-closed', this.resetConfirmation);
this.api.blocks.delete();
this.api.toolbar.close();
this.api.tooltip.hide();
this.api.blocks.delete();
this.api.toolbar.close();
this.api.tooltip.hide();
/**
* Prevent firing ui~documentClicked that can drop currentBlock pointer
*/
event.stopPropagation();
}
/**
* Prevent firing ui~documentClicked that can drop currentBlock pointer
*/
event.stopPropagation();
// }
}
/**

View file

@ -48,6 +48,13 @@ export interface PopoverItem {
* True if popover should close once item is activated
*/
closeOnActivate?: boolean;
/**
* If action requires confirmation, first click on the item will turn it into object specified in this field.
* Second click will perform the action.
*/
confirmation?: boolean | Partial<PopoverItem>;
}
/**
@ -146,6 +153,7 @@ export default class Popover extends EventsDispatcher<PopoverEvent> {
itemLabel: string;
itemIcon: string;
itemSecondaryLabel: string;
itemConfirmation: string;
noFoundMessage: string;
noFoundMessageShown: string;
popoverOverlay: string;
@ -162,6 +170,7 @@ export default class Popover extends EventsDispatcher<PopoverEvent> {
itemFlippable: 'ce-popover__item--flippable',
itemFocused: 'ce-popover__item--focused',
itemActive: 'ce-popover__item--active',
itemConfirmation: 'ce-popover__item--confirmation',
itemLabel: 'ce-popover__item-label',
itemIcon: 'ce-popover__item-icon',
itemSecondaryLabel: 'ce-popover__item-secondary-label',
@ -184,6 +193,11 @@ export default class Popover extends EventsDispatcher<PopoverEvent> {
*/
private api: API;
/**
* Reference to popover item that was clicked but requires second click to confirm action
*/
private itemAwaitngConfirmation = null;
/**
* Creates the Popover
*
@ -286,6 +300,8 @@ export default class Popover extends EventsDispatcher<PopoverEvent> {
this.isShown = false;
this.nodes.wrapper.classList.remove(this.className + '--opened-top');
this.cleanUpConfirmationState();
}
/**
@ -460,6 +476,12 @@ export default class Popover extends EventsDispatcher<PopoverEvent> {
const itemIndex = Array.from(allItems).indexOf(itemEl);
const clickedItem = this.items[itemIndex];
const ignoreClick = this.handleConfirmation(itemEl, clickedItem);
if (ignoreClick) {
return;
}
clickedItem.onClick(clickedItem, event);
if (clickedItem.closeOnActivate) {
@ -467,6 +489,57 @@ export default class Popover extends EventsDispatcher<PopoverEvent> {
}
}
/**
* Handles case when item needs confirmation before calling onClick callback.
* Returns true if click should be ignored.
*
* @param itemEl - clicked HTML element
* @param clickedItem - corresponding popover item
*/
private handleConfirmation(itemEl: HTMLElement, clickedItem: PopoverItem): boolean {
if (this.itemAwaitngConfirmation !== clickedItem && clickedItem.confirmation) {
/**
* Item requires confirmation.
* If configured, item's label, icon and other params should be replaced with values defined for confirmation state.
* Click is ignored.
*/
this.itemAwaitngConfirmation = clickedItem;
const itemData = {
...clickedItem,
...clickedItem.confirmation as PopoverItem,
confirmation: false,
};
const confirmationStateItemEl = this.createItem(itemData);
confirmationStateItemEl.classList.add(Popover.CSS.itemConfirmation);
itemEl.parentElement.replaceChild(confirmationStateItemEl, itemEl);
return true;
}
/**
* If item requiring confirmation is clicked for the second time or any other item is clicked,
* get rid of confirmation state on item
*/
this.cleanUpConfirmationState();
}
/**
* If popover contains an item in confirmation state, bring it to default state
*/
private cleanUpConfirmationState(): void {
if (!this.itemAwaitngConfirmation) {
return;
}
const defaultStateItemEl = this.createItem(this.itemAwaitngConfirmation);
const confirmationStateItemEl = this.nodes.wrapper.querySelector(`.${Popover.CSS.itemConfirmation}`);
confirmationStateItemEl.parentElement.replaceChild(defaultStateItemEl, confirmationStateItemEl);
defaultStateItemEl.classList.remove(Popover.CSS.itemConfirmation);
this.itemAwaitngConfirmation = null;
}
/**
* Creates Flipper instance to be able to leaf tools
*/

View file

@ -76,6 +76,25 @@
@apply --button-active;
}
&--confirmation {
background: var(--color-confirm);
@media (--can-hover) {
&:hover {
background: #ce4343;
}
}
.ce-popover__item-icon {
color: var(--color-confirm);
}
.ce-popover__item-label {
color: white;
}
}
&-icon {
@apply --tool-icon;
}

View file

@ -106,7 +106,7 @@
--button-active: {
background: rgba(56, 138, 229, 0.1);
color: #388AE5;
color: var(--color-active-icon);
};
/**