@openui5/sap.m
Version:
OpenUI5 UI Library sap.m
571 lines (516 loc) • 17.3 kB
JavaScript
/*!
* OpenUI5
* (c) Copyright 2026 SAP SE or an SAP affiliate company.
* Licensed under the Apache License, Version 2.0 - see LICENSE.txt.
*/
sap.ui.define([
"sap/ui/core/Core",
"sap/ui/core/Element",
"sap/ui/core/HTML",
"sap/m/Button",
"sap/m/Image",
"sap/m/PDFViewer",
"sap/m/Dialog",
"sap/m/IllustratedMessage",
"sap/m/IllustratedMessageType",
"sap/m/Carousel",
"sap/base/Log",
"sap/ui/core/Lib",
"sap/m/VBox",
"sap/m/Bar",
"sap/m/Title",
"sap/ui/core/Control"
], function(Core, Element, HTML, Button, Image, PDFViewer, Dialog, IllustratedMessage, IllustratedMessageType, Carousel, Log, Library, VBox, Bar, Title, Control) {
"use strict";
// get resource translation bundle;
const oLibraryResourceBundle = Library.getResourceBundleFor("sap.m");
/**
* Media types that can be previewed.
* @enum {string}
*/
const PreviewableMediaType = {
Png: "image/png",
Bmp: "image/bmp",
Jpeg: "image/jpeg",
Gif: "image/gif",
Txt: "text/plain",
Pdf: "application/pdf",
ChromePdf: "application/x-google-chrome-pdf",
Mpeg: "video/mpeg",
Mp4: "video/mp4",
Quicktime: "video/quicktime",
MsVideo: "video/x-msvideo",
Vds: "model/vnd.sap.vds"
};
/**
* Constructor for a new FilePreviewDialog.
*
* @class
* <h3>Overview</h3>
*
* Dialog with a carousel to preview files uploaded using the UploadSetwithTable control.
* This Element should only be used within the {@link sap.m.plugins.UploadSetwithTable UploadSetwithTable} Plugin as an association.
*
* <h3>Supported File Types for Preview</h3>
*
* Following are the supported file types that can be previewed:
*
* <ul><li>Image (PNG, JPEG, BMP, GIF)</li>
* <li>PDF </li>
* <li>Text (Txt)</li>
* <li>Video (MP4, MPEG, Quicktime, MsVideo)</li>
* <li>SAP 3D Visual models (VDS)</li></ul>
*
* @author SAP SE
* @param {string} [sId] Id for the new control, it is generated automatically if no id is provided.
* @param {object} [mSettings] Initial settings for the new control.
* @constructor
* @public
* @since 1.120
* @version 1.146.0
* @extends sap.ui.core.Element
* @name sap.m.upload.FilePreviewDialog
*/
const FilePreviewDialog = Element.extend("sap.m.upload.FilePreviewDialog", {
metadata: {
library: "sap.m",
properties: {
/**
* Show or hide carousel's arrows.
*/
showCarouselArrows: {type: "boolean", defaultValue: true},
/**
* Size limit of the file in megabytes that is allowed to be previewed.
* <br>If not set, files of any size can be previewed.
*/
maxFileSizeforPreview: {type: "float", defaultValue: null},
/**
* Callback function to insert custom content into the preview dialog using a control to display the preview of unsupported file types.
* <br>Use this property as callback function to insert a control with the content into the preview dialog.
* <br>Callback function returns a promise that resolves with a control that is displayed in the preview dialog. Reject the promise to display the default illustrated message.
* <br>Callback function is invoked with {@link sap.m.upload.UploadItem item} for each unsupported file type.
* <br>
* <br>Example: There is a file with an xml extension and you want to display the content inside a codeeditor control for the file type.
*
* <pre><code>
* <UploadSetwithTable>
* <upload:FilePreviewDialog customPageContentHandler="{onCustomContentHandler}"></upload:FilePreviewDialog>
* </UploadSetwithTable>
* </code></pre>
*
* <pre><code>
* onCustomContentHandler: function(oItem) {
*
* return new Promise(function(resolve, reject) {
*
* switch (oItem.getMediaType().toLowerCase()) {
*
* case "application/xml":
*
* var oCodeEditor = new CodeEditor({
* value: "XML content",
* width: "100%",
* height: "100%"
* });
*
* resolve(oCodeEditor);
* break;
*
* default:
* reject(); // reject the promise to display the default illustrated message.
* break;
* }
* });
* }
* </code></pre>
*
* @since 1.136
**/
customPageContentHandler: {type: "function", defaultValue: null}
},
defaultAggregation: "additionalFooterButtons",
aggregations: {
/**
* Custom buttons, to be displayed in the preview dialog footer.
* <br>Control by default adds two buttons (download and close).
*/
additionalFooterButtons: {type: "sap.m.Button", multiple: true}
}
},
_previewItem: null,
_items: [],
_aCachedPageIndexs: [],
init: function () {
this._oRichTextEditor = null;
this._oDialog = null;
this._oViewer = null;
this._oContentResource = null;
this._oCarouselItems = null;
},
exit: function () {
this._oRichTextEditor = null;
this._oDialog = null;
this._oViewer = null;
this._oContentResource = null;
this._oCarouselItems = null;
this._aCachedPageIndexs = [];
},
/**
* Opens the {@link sap.m.upload.FilePreviewDialog}.
* @private
*/
_open: async function () {
const aItems = this._items;
if (aItems?.length && this._previewItem) {
this._oCarousel = await this._createCarousel();
if (!this._oDialog) {
this._oDialog = this._createDialog();
} else {
var oTitle = this._oDialog?.getCustomHeader()?.getContentLeft()[0];
// Sets the title of the dialog to the currently previewed item filename.
oTitle?.setText(this._previewItem?.getFileName() || "");
// Removes all the existing content and set the new content on the dialog.
this._oDialog.removeAllContent();
this._oDialog.insertContent(this._oCarousel);
}
this.fireEvent("beforePreviewDialogOpen", {oDialog: this._oDialog});
this._oDialog.open();
}
},
/**
* Dynamically require Vds Viewer Control
* @return {Promise} Promise that resolves on sucessful load of Viewer control
* @private
*/
_loadVkDependency: function() {
return new Promise(function (resolve, reject) {
Core.loadLibrary("sap.ui.vk", { async: true })
.then(() => {
sap.ui.require(["sap/ui/vk/Viewer", "sap/ui/vk/ContentResource"], (viewer, contentResource) => {
resolve({ viewer, contentResource});
}, (error) => {
reject(error);
});
})
.catch(() => {
reject("sap.ui.vk.Viewer Control not available.");
});
});
},
/**
* Dynamically require RichTextEditor Control
* @return {Promise} Promise that resolves on sucessful load of RichTextEditor control
* @private
*/
_loadRichTextEditorDependency: function() {
return new Promise((resolve, reject) => {
Core.loadLibrary("sap.ui.richtexteditor", { async: true })
.then(() => {
sap.ui.require(["sap/ui/richtexteditor/RichTextEditor"], (richTextEditor) => {
resolve(richTextEditor);
}, (error) => {
reject(error);
});
})
.catch(() => {
reject("RichTextEditor Control not available.");
});
});
},
/**
* Creates an illustrated message for when no preview is available
* @param {string} sFileName The name of the file to be previewed
* @return {sap.m.IllustratedMessage} An illustrated message instance
* @private
*/
_createIllustratedMessage: function (sFileName) {
const oIllustratedMessage = new IllustratedMessage({
illustrationType: IllustratedMessageType.NoData,
title: sFileName,
description: oLibraryResourceBundle.getText("FILE_PREVIEW_DIALOG_NO_PREVIEW_AVAILABLE_MSG"),
enableVerticalResponsiveness: true
});
return oIllustratedMessage;
},
/**
* Creates a viewer for .vds files
* @param {sap.m.upload.UploadItem} oItem The UploadSetwithTableItem or UploadItem to be previewed
* @return {sap.ui.vk.Viewer} A vds viewer instance or undefined if dependency unavailable
* @private
*/
_createVdsViewer: async function (oItem) {
if (!this.oViewer || !this._oContentResource) {
try {
const oVkDependency = await this._loadVkDependency();
this._oViewer = oVkDependency.viewer;
this._oContentResource = oVkDependency.contentResource;
} catch (error) {
Log.error(error);
return null;
}
}
const oVdsViewer = new this._oViewer({
contentResources: [
new this._oContentResource({
source: oItem.getUrl(),
sourceType: "vds"
})
],
width: "100%"
});
return oVdsViewer;
},
/**
* Creates a rich text viewer
* @param {sap.m.upload.UploadItem} oItem The UploadSetwithTableItem or UploadItem to be previewed
* @return {sap.ui.richtexteditor.RichTextEditor} A rich text editor instance or undefined if dependency unavailable
* @private
*/
_createRichTextEditor: async function (oItem) {
if (!this._oRichTextEditor) {
try {
const oRichTextEditor = await this._loadRichTextEditorDependency();
this._oRichTextEditor = oRichTextEditor;
} catch (error) {
Log.error(error);
return null;
}
}
const oRte = new this._oRichTextEditor({
height: "100%",
width: "100%",
editable: false,
busy: true
});
oRte.attachReady(function () {
oRte.setBusy(false);
});
const oRequest = new XMLHttpRequest();
oRequest.open("GET", oItem.getUrl(), false);
oRequest.send(null);
const sText = oRequest.responseText;
oRte.setValue(sText);
return oRte;
},
/**
* Creates a {@link sap.m.Carousel} of uploaded files.
* @return {sap.m.Carousel} The {@link sap.m.Carousel} control.
* @private
*/
_createCarousel: async function () {
const oPreviewItem = this._previewItem;
let aItems = this._oCarouselItems = !this.getShowCarouselArrows() ? [this._previewItem] : this._items;
let sActivePageId = "";
let oActivePage = null;
aItems = aItems?.filter((oItem) => oItem?.isA("sap.m.upload.UploadSetwithTableItem") || oItem?.isA("sap.m.upload.UploadItem"));
const aPagePromises = aItems.map(async (oItem) => {
let oPage = null;
if (oItem?.getId() === oPreviewItem.getId() && oItem.getPreviewable() && this.isFileSizeWithinMaxLimit(oItem)) {
const oPageContent = await this.getPageContent(oItem);
oPage = this._getContainerControl(oPageContent);
} else {
const oPlaceHolderControl = this._getPlaceHolderControl(oItem);
oPage = this._getContainerControl(oPlaceHolderControl);
}
oPage = !this.isFileSizeWithinMaxLimit(oItem) ? this._getMaxSizePageIllustration(oItem) : oPage;
sActivePageId = oItem?.getId() === oPreviewItem?.getId() ? oPage?.getId() : sActivePageId;
oActivePage = oItem?.getId() === oPreviewItem?.getId() ? oPage : oActivePage;
if (oItem?.getId() === oPreviewItem?.getId()) {
this._aCachedPageIndexs.push(this.get);
}
return oPage;
});
const aPages = await Promise.all(aPagePromises);
const oCarousel = new Carousel({
showPageIndicator: this.getShowCarouselArrows() ? true : false,
pages: [
aPages
],
activePage: sActivePageId,
pageChanged: async (oEvent) => {
const iIndex = aPages.findIndex(function(oPage) {
return oPage.sId === oEvent.getParameter("newActivePageId");
});
const oTargetPage = oCarousel.getPages()[iIndex];
// if the page is not cached, load the content and cache it.
if (!this._aCachedPageIndexs.includes(iIndex)) {
const oControl = await this.getPageContent(aItems[iIndex], aPages[iIndex]);
oTargetPage.removeAllItems();
oTargetPage.addItem(oControl);
this._aCachedPageIndexs.push(iIndex);
}
// oCarousel.setActivePage(oTargetPage);
const sNewDialogTitle = aItems[iIndex].getFileName();
var oTitle = this._oDialog?.getCustomHeader()?.getContentLeft()[0];
oTitle?.setText(sNewDialogTitle);
}
});
if (oActivePage && sActivePageId) {
const sActivePageIndex = oCarousel?.indexOfPage(oActivePage);
if (sActivePageIndex > -1) {
this._aCachedPageIndexs?.push(sActivePageIndex);
}
}
// prevent all swipe related events so carousel movement is disabled.
if (!this.getShowCarouselArrows()) {
oCarousel.ontouchstart = oCarousel.ontouchmove = oCarousel.ontouchend = (oEvent) => {
oEvent.preventDefault();
};
}
oCarousel.addStyleClass("sapMFilePreviewDialogCarousel");
return oCarousel;
},
getPageContent: async function(oItem, oNewPage) {
const sMediaType = oItem.getMediaType();
let oPage = this._createIllustratedMessage(oItem.getFileName());
switch (sMediaType?.toLowerCase()) {
case PreviewableMediaType.Png:
case PreviewableMediaType.Bmp:
case PreviewableMediaType.Jpeg:
case PreviewableMediaType.Gif: {
const oPage = new Image({
src: oItem.getUrl()
}).addStyleClass("image-scale");
return oPage;
}
case PreviewableMediaType.Txt: {
const oRte = await this._createRichTextEditor(oItem);
if (oRte) {
oPage = oRte;
}
return oPage;
}
case PreviewableMediaType.Pdf:
case PreviewableMediaType.ChromePdf: {
oPage = new PDFViewer({
source: oItem.getUrl(),
showDownloadButton: false,
isTrustedSource: oItem?.getIsTrustedSource()
});
oPage.setBusy(true);
return oPage;
}
case PreviewableMediaType.Mpeg:
case PreviewableMediaType.Mp4:
case PreviewableMediaType.Quicktime:
case PreviewableMediaType.MsVideo: {
const oPage = new HTML({
content: `<video controls width='100%' height='100%' src=${oItem.getUrl()}>`
});
return oPage;
}
case PreviewableMediaType.Tiff:
case PreviewableMediaType.Vds: {
const oVdsViewer = await this._createVdsViewer(oItem);
if (oVdsViewer) {
oPage = oVdsViewer;
return oPage;
}
break;
}
default:
if (this.getCustomPageContentHandler() && typeof this.getCustomPageContentHandler() === "function") {
const oPromise = this.getCustomPageContentHandler()(oItem);
if (oPromise && oPromise instanceof Promise) {
try {
const oControl = await oPromise;
if (oControl instanceof Element || oControl instanceof Control) {
oPage = oControl;
}
} catch (error) {
return oPage;
}
}
}
return oPage;
}
return oPage;
},
_getPlaceHolderControl: function(oItem) {
return this._createIllustratedMessage(oItem.getFileName());
},
_getContainerControl: function(oItem) {
const oContainer = new VBox({
items: [
oItem
],
fitContainer: true,
alignItems: "Center",
justifyContent: "Center",
alignContent: "Center",
renderType: "Bare"
});
return oContainer;
},
/**
* Creates a {@link sap.m.Dialog} with {@link sap.m.Carousel} for previewing uploaded files.
* @return {sap.m.Dialog} The {@link sap.m.Dialog} control.
* @private
*/
_createDialog: function() {
const oActiveItem = this._getActiveUploadSetwithTableItem();
const oDialog = new Dialog({
customHeader: new Bar({
contentLeft: [new Title({ text: oActiveItem.getFileName()}).addStyleClass("sapMDialogTitle")]
}),
content: this._oCarousel,
horizontalScrolling: false,
verticalScrolling: false,
contentWidth: "100%",
contentHeight: "80vh",
buttons: [
this.getAdditionalFooterButtons(),
new Button({
text: oLibraryResourceBundle.getText("UPLOAD_SET_TABLE_FILE_PREVIEW_DIALOG_DOWNLOAD"),
press: () => {
this._getActiveUploadSetwithTableItem().download(true);
}
}),
new Button({
text: oLibraryResourceBundle.getText("UPLOAD_SET_TABLE_FILE_PREVIEW_DIALOG_CLOSE"),
press: () => {
this._oCarousel.destroyPages();
this._oDialog.close();
this._aCachedPageIndexs = [];
}
})
]
});
return oDialog;
},
/**
* Creates a {@link sap.m.Carousel} of uploaded files.
* @return {sap.m.upload.UploadItem} The currently active UploadSetwithTableItem.
* @private
*/
_getActiveUploadSetwithTableItem: function () {
const sActivePageId = this._oCarousel.getActivePage();
const aPages = this._oCarousel.getPages();
const iIndex = aPages.findIndex((oPage) => {
return oPage.sId === sActivePageId;
});
return this._oCarouselItems[iIndex];
},
isFileSizeWithinMaxLimit: function(oItem) {
let maxFileSize = this.getMaxFileSizeforPreview();
const iFileSize = oItem && oItem.getFileSize ? oItem.getFileSize() : 0; // if no file size
// if maxfilesize or filesize is not defined any file size can be previwed.
if (!maxFileSize || !iFileSize) {
return true;
}
maxFileSize = maxFileSize * (FilePreviewDialog.MEGABYTE);
return iFileSize <= maxFileSize;
},
_getMaxSizePageIllustration: function(oItem) {
const oIllustratedMessage = new IllustratedMessage({
illustrationType: IllustratedMessageType.NoData,
title: oItem?.getFileName(),
description: oLibraryResourceBundle.getText("FILE_PREVIEW_DIALOG_MAX_PREVIEW_SIZE_EXCEEDED", [this.getMaxFileSizeforPreview()]),
enableVerticalResponsiveness: true
});
return oIllustratedMessage;
}
});
FilePreviewDialog.MEGABYTE = 1048576;
return FilePreviewDialog;
});