UNPKG

@openui5/sap.m

Version:

OpenUI5 UI Library sap.m

571 lines (516 loc) 17.3 kB
/*! * 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> * &lt;UploadSetwithTable&gt; * &lt;upload:FilePreviewDialog customPageContentHandler="{onCustomContentHandler}"&gt;&lt;/upload:FilePreviewDialog&gt; * &lt;/UploadSetwithTable&gt; * </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; });