Browse Source

feat(iot-hub): add image gallery placeholder for markdown instructions

Add ${images.gallery(path1,path2,path3)} template syntax that renders
multiple ZIP images as a side-by-side gallery in instruction steps.
Images are resolved from zipImages (base64 data URIs). Click to expand
individual images. Styled as a flex row with thumbnails.
pull/15508/head
Andrii Shvaika 3 months ago
parent
commit
570cb68e1e
  1. 25
      ui-ngx/src/app/modules/home/components/iot-hub/device-install-dialog/device-install-dialog.component.scss
  2. 21
      ui-ngx/src/app/modules/home/components/iot-hub/device-install-dialog/device-install-dialog.component.ts

25
ui-ngx/src/app/modules/home/components/iot-hub/device-install-dialog/device-install-dialog.component.scss

@ -153,6 +153,31 @@
background: rgba(48, 86, 128, 0.08);
}
}
.tb-gallery {
display: flex;
flex-wrap: wrap;
gap: 8px;
margin: 12px 0;
}
.tb-gallery-img {
max-height: 160px;
border-radius: 4px;
border: 1px solid rgba(0, 0, 0, 0.12);
cursor: pointer;
transition: max-height 0.2s ease;
object-fit: contain;
&:hover {
border-color: rgba(0, 0, 0, 0.24);
}
&.tb-gallery-img-expanded {
max-height: none;
max-width: 100%;
}
}
}
}

21
ui-ngx/src/app/modules/home/components/iot-hub/device-install-dialog/device-install-dialog.component.ts

@ -251,12 +251,12 @@ export class TbDeviceInstallDialogComponent extends DialogComponent<TbDeviceInst
}
onMarkdownReady(container: HTMLElement): void {
// Download button handlers
const buttons = container.querySelectorAll('[data-action="download-gateway-docker-compose"]');
buttons.forEach(btn => {
btn.addEventListener('click', (e) => {
e.preventDefault();
if (this.gatewayDockerComposeContent) {
// Custom template from ZIP — download as Blob
const blob = new Blob([this.gatewayDockerComposeContent], { type: 'application/x-yaml' });
const url = URL.createObjectURL(blob);
const link = document.createElement('a');
@ -265,7 +265,6 @@ export class TbDeviceInstallDialogComponent extends DialogComponent<TbDeviceInst
link.click();
URL.revokeObjectURL(url);
} else {
// Standard template — authenticated API call
const gateway = this.entityOutputs.get('gateway');
if (gateway?.id) {
this.deviceService.downloadGatewayDockerComposeFile(gateway.id).subscribe();
@ -273,6 +272,13 @@ export class TbDeviceInstallDialogComponent extends DialogComponent<TbDeviceInst
}
});
});
// Gallery image click-to-expand
const galleryImages = container.querySelectorAll('.tb-gallery-img');
galleryImages.forEach(img => {
img.addEventListener('click', () => {
img.classList.toggle('tb-gallery-img-expanded');
});
});
}
resolveImagePath(path: string): string {
@ -300,6 +306,17 @@ export class TbDeviceInstallDialogComponent extends DialogComponent<TbDeviceInst
if (key === 'gateway.downloadButton') {
return '<a href="#" data-action="download-gateway-docker-compose" class="tb-download-btn">⬇ Download docker-compose.yml</a>';
}
// Image gallery: ${images.gallery(path1,path2,path3)}
const galleryMatch = key.match(/^images\.gallery\((.+)\)$/);
if (galleryMatch) {
const paths = galleryMatch[1].split(',').map(p => p.trim());
const images = paths
.map(p => this.zipImages.get(p))
.filter(src => !!src)
.map(src => `<img src="${src}" class="tb-gallery-img" />`)
.join('');
return `<div class="tb-gallery">${images}</div>`;
}
const dotIdx = key.indexOf('.');
if (dotIdx > 0) {
const alias = key.substring(0, dotIdx);

Loading…
Cancel
Save