Skip to content

Commit 698d566

Browse files
Populate name for content and media on URL picker if title is left empty (#19677)
* Populate name for content and media on URL picker if title is left empty. * Display URL for manually entered URLs. * Updates from code review. * Reverted `elementName` constant * Sorted imports * Small code tidy-ups * Added logic to render the `url` as the `name` fallback In this case, the `detail` is left empty, giving prominence to the `url` value. * Refactored the get name/url methods for code consistency. * Updated `#requestRemoveItem()` to use the item's resolved name with a fallback to "item". Also localized the "Remove" button label --------- Co-authored-by: leekelleher <[email protected]>
1 parent 5d968af commit 698d566

File tree

2 files changed

+82
-27
lines changed

2 files changed

+82
-27
lines changed

src/Umbraco.Infrastructure/PropertyEditors/ValueConverters/MultiUrlPickerValueConverter.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,11 @@ public override PropertyCacheLevel GetPropertyCacheLevel(IPublishedPropertyType
101101
continue;
102102
}
103103

104+
if (string.IsNullOrEmpty(dto.Name))
105+
{
106+
dto.Name = content.Name;
107+
}
108+
104109
url = content.Url(_publishedUrlProvider);
105110
}
106111

src/Umbraco.Web.UI.Client/src/packages/multi-url-picker/components/input-multi-url/input-multi-url.element.ts

Lines changed: 77 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -12,26 +12,29 @@ import {
1212
when,
1313
} from '@umbraco-cms/backoffice/external/lit';
1414
import { simpleHashCode } from '@umbraco-cms/backoffice/observable-api';
15+
import { umbConfirmModal } from '@umbraco-cms/backoffice/modal';
1516
import { UmbChangeEvent } from '@umbraco-cms/backoffice/event';
1617
import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element';
17-
import { umbConfirmModal } from '@umbraco-cms/backoffice/modal';
18+
import {
19+
UmbDocumentItemRepository,
20+
UmbDocumentUrlRepository,
21+
UmbDocumentUrlsDataResolver,
22+
} from '@umbraco-cms/backoffice/document';
23+
import { UmbMediaItemRepository, UmbMediaUrlRepository } from '@umbraco-cms/backoffice/media';
1824
import { UmbModalRouteRegistrationController } from '@umbraco-cms/backoffice/router';
1925
import { UmbSorterController } from '@umbraco-cms/backoffice/sorter';
2026
import { UUIFormControlMixin } from '@umbraco-cms/backoffice/external/uui';
2127
import type { UmbModalRouteBuilder } from '@umbraco-cms/backoffice/router';
2228
import type { UmbVariantId } from '@umbraco-cms/backoffice/variant';
2329
import type { UUIModalSidebarSize } from '@umbraco-cms/backoffice/external/uui';
24-
import { UmbDocumentUrlRepository, UmbDocumentUrlsDataResolver } from '@umbraco-cms/backoffice/document';
25-
import { UmbMediaUrlRepository } from '@umbraco-cms/backoffice/media';
2630

2731
/**
2832
* @element umb-input-multi-url
2933
* @fires change - when the value of the input changes
3034
* @fires blur - when the input loses focus
3135
* @fires focus - when the input gains focus
3236
*/
33-
const elementName = 'umb-input-multi-url';
34-
@customElement(elementName)
37+
@customElement('umb-input-multi-url')
3538
export class UmbInputMultiUrlElement extends UUIFormControlMixin(UmbLitElement, '') {
3639
#sorter = new UmbSorterController<UmbLinkPickerLink>(this, {
3740
getUniqueOfElement: (element) => {
@@ -131,14 +134,21 @@ export class UmbInputMultiUrlElement extends UUIFormControlMixin(UmbLitElement,
131134
this.#urls = [...data]; // Unfreeze data coming from State, so we can manipulate it.
132135
super.value = this.#urls.map((x) => x.url).join(',');
133136
this.#sorter.setModel(this.#urls);
134-
this.#populateLinksUrl();
137+
this.#populateLinksNameAndUrl();
135138
}
136139
get urls(): Array<UmbLinkPickerLink> {
137140
return this.#urls;
138141
}
139142

140143
#urls: Array<UmbLinkPickerLink> = [];
141144

145+
#documentItemRepository = new UmbDocumentItemRepository(this);
146+
#documentUrlRepository = new UmbDocumentUrlRepository(this);
147+
#documentUrlsDataResolver = new UmbDocumentUrlsDataResolver(this);
148+
149+
#mediaItemRepository = new UmbMediaItemRepository(this);
150+
#mediaUrlRepository = new UmbMediaUrlRepository(this);
151+
142152
/**
143153
* Sets the input to readonly mode, meaning value cannot be changed but still able to read and select its content.
144154
* @type {boolean}
@@ -163,6 +173,9 @@ export class UmbInputMultiUrlElement extends UUIFormControlMixin(UmbLitElement,
163173
@state()
164174
private _modalRoute?: UmbModalRouteBuilder;
165175

176+
@state()
177+
_resolvedLinkNames: Array<{ unique: string; name: string }> = [];
178+
166179
@state()
167180
_resolvedLinkUrls: Array<{ unique: string; url: string }> = [];
168181

@@ -235,26 +248,42 @@ export class UmbInputMultiUrlElement extends UUIFormControlMixin(UmbLitElement,
235248
});
236249
}
237250

238-
#populateLinksUrl() {
239-
// Documents and media have URLs saved in the local link format. Display the actual URL to align with what
240-
// the user sees when they selected it initially.
251+
#populateLinksNameAndUrl() {
252+
this._resolvedLinkNames = [];
253+
this._resolvedLinkUrls = [];
254+
255+
// Documents and media have URLs saved in the local link format.
256+
// Display the actual URL to align with what the user sees when they selected it initially.
241257
this.#urls.forEach(async (link) => {
242258
if (!link.unique) return;
243259

260+
let name: string | undefined = undefined;
244261
let url: string | undefined = undefined;
262+
245263
switch (link.type) {
246264
case 'document': {
265+
if (!link.name || link.name.length === 0) {
266+
name = await this.#getNameForDocument(link.unique);
267+
}
247268
url = await this.#getUrlForDocument(link.unique);
248269
break;
249270
}
250271
case 'media': {
272+
if (!link.name || link.name.length === 0) {
273+
name = await this.#getNameForMedia(link.unique);
274+
}
251275
url = await this.#getUrlForMedia(link.unique);
252276
break;
253277
}
254278
default:
255279
break;
256280
}
257281

282+
if (name) {
283+
const resolvedName = { unique: link.unique, name };
284+
this._resolvedLinkNames = [...this._resolvedLinkNames, resolvedName];
285+
}
286+
258287
if (url) {
259288
const resolvedUrl = { unique: link.unique, url };
260289
this._resolvedLinkUrls = [...this._resolvedLinkUrls, resolvedUrl];
@@ -263,30 +292,38 @@ export class UmbInputMultiUrlElement extends UUIFormControlMixin(UmbLitElement,
263292
}
264293

265294
async #getUrlForDocument(unique: string) {
266-
const documentUrlRepository = new UmbDocumentUrlRepository(this);
267-
const { data: documentUrlData } = await documentUrlRepository.requestItems([unique]);
268-
const urlsItem = documentUrlData?.[0];
269-
const dataResolver = new UmbDocumentUrlsDataResolver(this);
270-
dataResolver.setData(urlsItem?.urls);
271-
const resolvedUrls = await dataResolver.getUrls();
295+
const { data: data } = await this.#documentUrlRepository.requestItems([unique]);
296+
297+
this.#documentUrlsDataResolver.setData(data?.[0]?.urls);
298+
299+
const resolvedUrls = await this.#documentUrlsDataResolver.getUrls();
272300
return resolvedUrls?.[0]?.url ?? '';
273301
}
274302

275303
async #getUrlForMedia(unique: string) {
276-
const mediaUrlRepository = new UmbMediaUrlRepository(this);
277-
const { data: mediaUrlData } = await mediaUrlRepository.requestItems([unique]);
278-
return mediaUrlData?.[0].url ?? '';
304+
const { data } = await this.#mediaUrlRepository.requestItems([unique]);
305+
return data?.[0].url ?? '';
306+
}
307+
308+
async #getNameForDocument(unique: string) {
309+
const { data } = await this.#documentItemRepository.requestItems([unique]);
310+
return data?.[0]?.name ?? '';
279311
}
280312

281-
async #requestRemoveItem(index: number) {
313+
async #getNameForMedia(unique: string) {
314+
const { data } = await this.#mediaItemRepository.requestItems([unique]);
315+
return data?.[0]?.name ?? '';
316+
}
317+
318+
async #requestRemoveItem(index: number, name?: string) {
282319
const item = this.#urls[index];
283320
if (!item) throw new Error('Could not find item at index: ' + index);
284321

285322
await umbConfirmModal(this, {
286323
color: 'danger',
287-
headline: `Remove ${item.name}?`,
288-
content: 'Are you sure you want to remove this item',
289-
confirmLabel: 'Remove',
324+
headline: `Remove ${name || item.name || 'item'}?`,
325+
content: 'Are you sure you want to remove this item?',
326+
confirmLabel: '#general_remove',
290327
});
291328

292329
this.#removeItem(index);
@@ -320,6 +357,17 @@ export class UmbInputMultiUrlElement extends UUIFormControlMixin(UmbLitElement,
320357
this.dispatchEvent(new UmbChangeEvent());
321358
}
322359

360+
#getResolvedItemName(link: UmbLinkPickerLink): string {
361+
return (link.name || this._resolvedLinkNames.find((name) => name.unique === link.unique)?.name) ?? '';
362+
}
363+
364+
#getResolvedItemUrl(link: UmbLinkPickerLink): string {
365+
return (
366+
(this._resolvedLinkUrls.find((url) => url.unique === link.unique)?.url ?? link.url ?? '') +
367+
(link.queryString || '')
368+
);
369+
}
370+
323371
override render() {
324372
return html`${this.#renderItems()} ${this.#renderAddButton()}`;
325373
}
@@ -356,13 +404,15 @@ export class UmbInputMultiUrlElement extends UUIFormControlMixin(UmbLitElement,
356404
#renderItem(link: UmbLinkPickerLink, index: number) {
357405
const unique = this.#getUnique(link);
358406
const href = this.readonly ? undefined : (this._modalRoute?.({ index }) ?? undefined);
359-
const resolvedUrl = this._resolvedLinkUrls.find((url) => url.unique === link.unique)?.url ?? '';
407+
const name = this.#getResolvedItemName(link);
408+
const url = this.#getResolvedItemUrl(link);
409+
360410
return html`
361411
<uui-ref-node
362412
id=${unique}
363413
href=${ifDefined(href)}
364-
name=${link.name || ''}
365-
detail=${resolvedUrl + (link.queryString || '')}
414+
name=${name || url}
415+
detail=${ifDefined(name ? url : undefined)}
366416
?readonly=${this.readonly}>
367417
<umb-icon slot="icon" name=${link.icon || 'icon-link'}></umb-icon>
368418
${when(
@@ -371,7 +421,7 @@ export class UmbInputMultiUrlElement extends UUIFormControlMixin(UmbLitElement,
371421
<uui-action-bar slot="actions">
372422
<uui-button
373423
label=${this.localize.term('general_remove')}
374-
@click=${() => this.#requestRemoveItem(index)}></uui-button>
424+
@click=${() => this.#requestRemoveItem(index, name)}></uui-button>
375425
</uui-action-bar>
376426
`,
377427
)}
@@ -390,6 +440,6 @@ export class UmbInputMultiUrlElement extends UUIFormControlMixin(UmbLitElement,
390440

391441
declare global {
392442
interface HTMLElementTagNameMap {
393-
[elementName]: UmbInputMultiUrlElement;
443+
'umb-input-multi-url': UmbInputMultiUrlElement;
394444
}
395445
}

0 commit comments

Comments
 (0)