Adds Clear Clipboard button & logic (#20757)

* Adds new dictionary/localization item for the clipboard dialog clear all prompt

* Removes the wrapping uui-box and moved inside the component itself

* Adds Clear Clipboard button and logic

* Adds uui-box from outer components consuimg this into this component
* Adds a header to uui-box
* Adds a conditional uui-button when we have items in clipboard
* Adds confirm dialog/prompt to ask if user wants to clear all items

* Adds in general_clipboard item to use in the UUI-box header

* Removes extra space & moves the requestItems outside the for loop

* Be a better citizen

Make sure the promise for the modal is caught and we return out early if user explictiy cancels modal or presses ESC

* Cleanup my noisy comments for a re-review

---------

Co-authored-by: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com>
This commit is contained in:
Warren Buckley
2025-11-11 11:50:23 +00:00
committed by GitHub
parent 0858d02172
commit 9ad4a7eeba
4 changed files with 64 additions and 22 deletions

View File

@@ -1017,6 +1017,7 @@ export default {
if (new Date(date).getTime() < new Date(now).getTime()) return `${duration} ago`;
return `in ${duration}`;
},
clipboard: 'Clipboard',
},
colors: {
black: 'Black',
@@ -2508,6 +2509,7 @@ export default {
labelForCopyToClipboard: 'Copy to clipboard',
confirmDeleteHeadline: 'Delete from clipboard',
confirmDeleteDescription: 'Are you sure you want to delete <strong>{0}</strong> from the clipboard?',
confirmClearDescription: 'Are you sure you want to clear the clipboard?',
copySuccessHeadline: 'Copied to clipboard',
},
propertyActions: {

View File

@@ -194,11 +194,9 @@ export class UmbBlockCatalogueModalElement extends UmbModalBaseElement<
#renderClipboard() {
return html`
<uui-box>
<umb-clipboard-entry-picker
.config=${{ multiple: true, asyncFilter: this.data?.clipboardFilter }}
@selection-change=${this.#onClipboardPickerSelectionChange}></umb-clipboard-entry-picker>
</uui-box>
<umb-clipboard-entry-picker
.config=${{ multiple: true, asyncFilter: this.data?.clipboardFilter }}
@selection-change=${this.#onClipboardPickerSelectionChange}></umb-clipboard-entry-picker>
`;
}

View File

@@ -27,12 +27,10 @@ export class UmbClipboardEntryPickerModalElement extends UmbModalBaseElement<
override render() {
return html`<umb-body-layout headline="Clipboard">
<uui-box>
<umb-clipboard-entry-picker
.selection=${this.value?.selection}
.config=${this.data}
@selection-change=${this.#onSelectionChange}></umb-clipboard-entry-picker>
</uui-box>
<umb-clipboard-entry-picker
.selection=${this.value?.selection}
.config=${this.data}
@selection-change=${this.#onSelectionChange}></umb-clipboard-entry-picker>
<div slot="actions">
<uui-button label="Close" @click=${this.#close}></uui-button>
<uui-button label="Submit" look="primary" color="positive" @click=${this.#submit}></uui-button>

View File

@@ -1,6 +1,7 @@
import { UmbClipboardCollectionRepository } from '../../collection/index.js';
import type { UmbClipboardEntryDetailModel } from '../types.js';
import { css, customElement, html, property, repeat, state, when } from '@umbraco-cms/backoffice/external/lit';
import UmbClipboardEntryDetailRepository from '../detail/clipboard-entry-detail.repository.js';
import { css, customElement, html, nothing, property, repeat, state, when } from '@umbraco-cms/backoffice/external/lit';
import { UmbEntityContext } from '@umbraco-cms/backoffice/entity';
import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element';
import {
@@ -10,6 +11,7 @@ import {
import { UmbSelectionManager } from '@umbraco-cms/backoffice/utils';
import { UMB_ACTION_EVENT_CONTEXT } from '@umbraco-cms/backoffice/action';
import type { UmbEntityUnique } from '@umbraco-cms/backoffice/entity';
import { umbConfirmModal } from '@umbraco-cms/backoffice/modal';
// TODO: make this into an extension point (Picker) with two kinds of pickers: tree-item-picker and collection-item-picker;
@customElement('umb-clipboard-entry-picker')
@@ -28,6 +30,8 @@ export class UmbClipboardEntryPickerElement extends UmbLitElement {
#entityContext = new UmbEntityContext(this);
#actionEventContext?: typeof UMB_ACTION_EVENT_CONTEXT.TYPE;
#clipboardDetailRepository = new UmbClipboardEntryDetailRepository(this);
constructor() {
super();
this.#entityContext.setEntityType('clipboard-entry');
@@ -117,17 +121,57 @@ export class UmbClipboardEntryPickerElement extends UmbLitElement {
}
};
async #clearClipboard() {
try {
await umbConfirmModal(this, {
headline: '#clipboard_labelForClearClipboard',
content: '#clipboard_confirmClearDescription',
color: 'danger',
confirmLabel: '#general_clear',
cancelLabel: '#general_cancel',
});
} catch {
return;
}
for (const item of this._items) {
const { error } = await this.#clipboardDetailRepository.delete(item.unique);
if (error) {
console.error(`Unable to delete clipboard item with unique ${item.unique}`, error);
}
}
this.#requestItems();
}
override render() {
return when(
this._items.length > 0,
() =>
repeat(
this._items,
(item) => item.unique,
(item) => this.#renderItem(item),
),
() => html`<p>There are no items in the clipboard.</p>`,
);
return html`
<uui-box headline=${this.localize.term('general_clipboard')}>
<span slot="header-actions">
${when(
this._items.length > 0,
() => html`
<uui-button color="default" look="default" @click="${this.#clearClipboard}">
<uui-icon name="delete"></uui-icon>
<umb-localize key="clipboard_labelForClearClipboard">Clear Clipboard</umb-localize>
</uui-button>
`,
() => nothing,
)}
</span>
${when(
this._items.length > 0,
() =>
repeat(
this._items,
(item) => item.unique,
(item) => this.#renderItem(item),
),
() => html`<p>There are no items in the clipboard.</p>`,
)}
</uui-box>
`;
}
#renderItem(item: UmbClipboardEntryDetailModel) {