Skip to content

Commit 6fb7e10

Browse files
committed
feat: add thumbnail component that can request a media key and show it or fallback to an icon
1 parent 5622376 commit 6fb7e10

File tree

5 files changed

+152
-5
lines changed

5 files changed

+152
-5
lines changed
Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
import { UmbImagingCropMode } from '../types.js';
2+
import { UmbImagingRepository } from '../imaging.repository.js';
3+
import { css, customElement, html, nothing, property, state, when } from '@umbraco-cms/backoffice/external/lit';
4+
import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element';
5+
import { UmbTextStyles } from '@umbraco-cms/backoffice/style';
6+
7+
const ELEMENT_NAME = 'umb-imaging-thumbnail';
8+
9+
@customElement(ELEMENT_NAME)
10+
export class UmbImagingThumbnailElement extends UmbLitElement {
11+
/**
12+
* The unique identifier for the media item.
13+
* @remark This is also known as the media key and is used to fetch the resource.
14+
*/
15+
@property()
16+
unique = '';
17+
18+
/**
19+
* The width of the thumbnail.
20+
*/
21+
@property({ type: Number })
22+
width = 400;
23+
24+
/**
25+
* The height of the thumbnail.
26+
*/
27+
@property({ type: Number })
28+
height = 400;
29+
30+
/**
31+
* The mode of the thumbnail.
32+
* @remark The mode determines how the image is cropped.
33+
* @enum {UmbImagingCropMode}
34+
*/
35+
@property()
36+
mode: UmbImagingCropMode = UmbImagingCropMode.MIN;
37+
38+
/**
39+
* The alt text for the thumbnail.
40+
*/
41+
@property()
42+
alt = '';
43+
44+
/**
45+
* The fallback icon for the thumbnail.
46+
*/
47+
@property()
48+
icon = 'icon-picture';
49+
50+
/**
51+
* The `loading` state of the thumbnail.
52+
* @enum {'lazy' | 'eager'}
53+
* @default 'lazy'
54+
*/
55+
@property()
56+
loading: 'lazy' | 'eager' = 'lazy';
57+
58+
@state()
59+
private _isLoading = true;
60+
61+
@state()
62+
private _thumbnailUrl = '';
63+
64+
#imagingRepository = new UmbImagingRepository(this);
65+
66+
protected override async firstUpdated() {
67+
await this.#generateThumbnailUrl();
68+
this._isLoading = false;
69+
}
70+
71+
override render() {
72+
return html` ${this.#renderThumbnail()} ${when(this._isLoading, () => this.#renderLoading())} `;
73+
}
74+
75+
#renderLoading() {
76+
return html`<div class="container"><uui-loader></uui-loader></div>`;
77+
}
78+
79+
#renderThumbnail() {
80+
if (this._isLoading) return nothing;
81+
82+
return when(
83+
this._thumbnailUrl,
84+
() =>
85+
html`<img
86+
id="figure"
87+
src="${this._thumbnailUrl}"
88+
alt="${this.alt}"
89+
loading="${this.loading}"
90+
draggable="false" />`,
91+
() => html`<umb-icon id="icon" name="${this.icon}"></umb-icon>`,
92+
);
93+
}
94+
95+
async #generateThumbnailUrl() {
96+
const { data } = await this.#imagingRepository.requestThumbnailUrls(
97+
[this.unique],
98+
this.height,
99+
this.width,
100+
this.mode,
101+
);
102+
this._thumbnailUrl = data[0]?.url ?? '';
103+
}
104+
105+
static override styles = [
106+
UmbTextStyles,
107+
css`
108+
:host {
109+
display: block;
110+
position: relative;
111+
overflow: hidden;
112+
}
113+
114+
.container {
115+
display: flex;
116+
justify-content: center;
117+
align-items: center;
118+
}
119+
120+
#figure {
121+
display: block;
122+
width: 100%;
123+
height: 100%;
124+
object-fit: cover;
125+
126+
background-image: url('data:image/svg+xml;charset=utf-8,<svg xmlns="http://www.w3.org/2000/svg" width="100" height="100" fill-opacity=".1"><path d="M50 0h50v50H50zM0 50h50v50H0z"/></svg>');
127+
background-size: 10px 10px;
128+
background-repeat: repeat;
129+
}
130+
131+
#icon {
132+
width: 100%;
133+
height: 100%;
134+
font-size: var(--uui-size-8);
135+
}
136+
`,
137+
];
138+
}
139+
140+
declare global {
141+
interface HTMLElementTagNameMap {
142+
[ELEMENT_NAME]: UmbImagingThumbnailElement;
143+
}
144+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export * from './imaging-thumbnail.element.js';

src/packages/media/imaging/imaging.repository.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
1-
import type { UmbImagingModel } from './types.js';
1+
import { UmbImagingCropMode, type UmbImagingModel } from './types.js';
22
import { UmbImagingServerDataSource } from './imaging.server.data.js';
33
import { UMB_IMAGING_STORE_CONTEXT } from './imaging.store.token.js';
4-
import { ImageCropModeModel } from '@umbraco-cms/backoffice/external/backend-api';
54
import { UmbRepositoryBase } from '@umbraco-cms/backoffice/repository';
65
import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api';
76
import type { UmbApi } from '@umbraco-cms/backoffice/extension-api';
@@ -68,7 +67,7 @@ export class UmbImagingRepository extends UmbRepositoryBase implements UmbApi {
6867
* @param {ImageCropModeModel} mode - The crop mode
6968
* @memberof UmbImagingRepository
7069
*/
71-
async requestThumbnailUrls(uniques: Array<string>, height: number, width: number, mode = ImageCropModeModel.MIN) {
70+
async requestThumbnailUrls(uniques: Array<string>, height: number, width: number, mode = UmbImagingCropMode.MIN) {
7271
const imagingModel: UmbImagingModel = { height, width, mode };
7372
return this.requestResizedItems(uniques, imagingModel);
7473
}

src/packages/media/imaging/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
1+
export * from './components/index.js';
12
export { UmbImagingRepository } from './imaging.repository.js';
23
export { UMB_IMAGING_REPOSITORY_ALIAS } from './constants.js';

src/packages/media/imaging/types.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
1-
import type { ImageCropModeModel } from '@umbraco-cms/backoffice/external/backend-api';
1+
import { ImageCropModeModel as UmbImagingCropMode } from '@umbraco-cms/backoffice/external/backend-api';
2+
3+
export { UmbImagingCropMode };
24

35
export interface UmbImagingModel {
46
height?: number;
57
width?: number;
6-
mode?: ImageCropModeModel;
8+
mode?: UmbImagingCropMode;
79
}

0 commit comments

Comments
 (0)