From 570cb68e1e162873d9967d8deb19aae8eb3f76fd Mon Sep 17 00:00:00 2001 From: Andrii Shvaika Date: Tue, 7 Apr 2026 15:29:56 +0300 Subject: [PATCH] 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. --- .../device-install-dialog.component.scss | 25 +++++++++++++++++++ .../device-install-dialog.component.ts | 21 ++++++++++++++-- 2 files changed, 44 insertions(+), 2 deletions(-) diff --git a/ui-ngx/src/app/modules/home/components/iot-hub/device-install-dialog/device-install-dialog.component.scss b/ui-ngx/src/app/modules/home/components/iot-hub/device-install-dialog/device-install-dialog.component.scss index a0a5793e7d..9d6fd5fa9a 100644 --- a/ui-ngx/src/app/modules/home/components/iot-hub/device-install-dialog/device-install-dialog.component.scss +++ b/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%; + } + } } } diff --git a/ui-ngx/src/app/modules/home/components/iot-hub/device-install-dialog/device-install-dialog.component.ts b/ui-ngx/src/app/modules/home/components/iot-hub/device-install-dialog/device-install-dialog.component.ts index 4348b7a3bb..63fbe90cb2 100644 --- a/ui-ngx/src/app/modules/home/components/iot-hub/device-install-dialog/device-install-dialog.component.ts +++ b/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 { 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 { + img.addEventListener('click', () => { + img.classList.toggle('tb-gallery-img-expanded'); + }); + }); } resolveImagePath(path: string): string { @@ -300,6 +306,17 @@ export class TbDeviceInstallDialogComponent extends DialogComponent⬇ Download docker-compose.yml'; } + // 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 => ``) + .join(''); + return ``; + } const dotIdx = key.indexOf('.'); if (dotIdx > 0) { const alias = key.substring(0, dotIdx);