Skip to content

Commit 54038e4

Browse files
authored
feat(ui5-upload-collection): implement new webcomponent (#1316)
Background ui5-upload-collection is new component located in fiori package, which benefits and brings some of ui5-list features, but it shows different items of type ui5-upload-collection-item. It is in the responsibility of the app developer to put ui5-file-uploader in the header, create and add new ui5-upload-collection-item(s) to the collection. Main features: - header slot - items slot (default) - items have special visualisation, suitable for files representation - items have "file" property, to allow app developers to set and get file object - drag and drop overlay
1 parent 82861f8 commit 54038e4

28 files changed

+2221
-7
lines changed
+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
/**
2+
* "" -> ""
3+
* "noExtension" -> ""
4+
* "file.txt" -> ".txt"
5+
* "file.with.many.dots.doc" -> ".doc"
6+
* ".gitignore" -> ""
7+
*
8+
* @param fileName - the file name
9+
* @returns {string}
10+
*/
11+
const getFileExtension = fileName => {
12+
const dotPos = fileName.lastIndexOf(".");
13+
14+
if (dotPos < 1) {
15+
return "";
16+
}
17+
18+
return fileName.slice(dotPos);
19+
};
20+
21+
export default getFileExtension;

packages/fiori/bundle.esm.js

+2
Original file line numberDiff line numberDiff line change
@@ -11,3 +11,5 @@ import ProductSwitch from "./dist/ProductSwitch.js";
1111
import ProductSwitchItem from "./dist/ProductSwitchItem.js";
1212
import ShellBar from "./dist/ShellBar.js";
1313
import ShellBarItem from "./dist/ShellBarItem.js";
14+
import UploadCollection from "./dist/UploadCollection.js";
15+
import UploadCollectionItem from "./dist/UploadCollectionItem.js";
+35
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
2+
<div class="ui5-uc-root">
3+
<div class="ui5-uc-header">
4+
<slot name="header"></slot>
5+
</div>
6+
<div class="ui5-uc-content">
7+
<ui5-list
8+
mode="{{mode}}"
9+
@ui5-selectionChange="{{_onSelectionChange}}"
10+
@ui5-itemDelete="{{_onItemDelete}}"
11+
>
12+
<slot></slot>
13+
</ui5-list>
14+
{{#if _showNoData}}
15+
<div class="uc-no-files">
16+
<div class="icon-container">
17+
<ui5-icon name="document"></ui5-icon>
18+
</div>
19+
<ui5-title level="H2">{{_noDataText}}</ui5-title>
20+
<ui5-label class="subtitle">{{_noDataDescription}}</ui5-label>
21+
</div>
22+
{{else if _showDndOverlay}}
23+
<div
24+
class="{{classes.dndOverlay}}"
25+
@dragenter="{{_ondragenter}}"
26+
@dragleave="{{_ondragleave}}"
27+
@dragover="{{_ondragover}}"
28+
@drop="{{_ondrop}}"
29+
>
30+
<ui5-icon name="upload-to-cloud"></ui5-icon>
31+
<span class="dnd-overlay-text">{{_dndOverlayText}}</span>
32+
</div>
33+
{{/if}}
34+
</div>
35+
</div>
+319
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,319 @@
1+
import UI5Element from "@ui5/webcomponents-base/dist/UI5Element.js";
2+
import litRender from "@ui5/webcomponents-base/dist/renderer/LitRenderer.js";
3+
import { getI18nBundle, fetchI18nBundle } from "@ui5/webcomponents-base/dist/i18nBundle.js";
4+
import Icon from "@ui5/webcomponents/dist/Icon.js";
5+
import Label from "@ui5/webcomponents/dist/Label.js";
6+
import List from "@ui5/webcomponents/dist/List.js";
7+
import ListMode from "@ui5/webcomponents/dist/types/ListMode.js";
8+
import Title from "@ui5/webcomponents/dist/Title.js";
9+
import "@ui5/webcomponents-icons/dist/icons/upload-to-cloud.js";
10+
import "@ui5/webcomponents-icons/dist/icons/document.js";
11+
import {
12+
UPLOADCOLLECTION_NO_DATA_TEXT,
13+
UPLOADCOLLECTION_NO_DATA_DESCRIPTION,
14+
UPLOADCOLLECTION_DRAG_FILE_INDICATOR,
15+
UPLOADCOLLECTION_DROP_FILE_INDICATOR,
16+
} from "./generated/i18n/i18n-defaults.js";
17+
import {
18+
attachBodyDnDHandler,
19+
detachBodyDnDHandler,
20+
draggingFiles,
21+
} from "./upload-utils/UploadCollectionBodyDnD.js";
22+
import UploadCollectionDnDOverlayMode from "./types/UploadCollectionDnDMode.js";
23+
24+
// Template
25+
import UploadCollectionTemplate from "./generated/templates/UploadCollectionTemplate.lit.js";
26+
27+
// Styles
28+
import UploadCollectionCss from "./generated/themes/UploadCollection.css.js";
29+
30+
/**
31+
* @public
32+
*/
33+
const metadata = {
34+
tag: "ui5-upload-collection",
35+
properties: /** @lends sap.ui.webcomponents.fiori.UploadCollection.prototype */ {
36+
/**
37+
* Defines the mode of the <code>ui5-upload-collection</code>.
38+
* <br><br>
39+
* <b>Note:</b> Available options are <code>None</code>, <code>SingleSelect</code>,
40+
* <code>MultiSelect</code>, and <code>Delete</code>.
41+
*
42+
* @type {ListMode}
43+
* @defaultvalue "None"
44+
* @public
45+
*/
46+
mode: {
47+
type: ListMode,
48+
defaultValue: ListMode.None,
49+
},
50+
51+
/**
52+
* Allows you to set your own text for the 'No data' description.
53+
*
54+
* @type {string}
55+
* @defaultvalue ""
56+
* @public
57+
*/
58+
noDataDescription: {
59+
type: String,
60+
},
61+
62+
/**
63+
* Allows you to set your own text for the 'No data' text.
64+
*
65+
* @type {string}
66+
* @defaultvalue ""
67+
* @public
68+
*/
69+
noDataText: {
70+
type: String,
71+
},
72+
73+
/**
74+
* By default there will be drag and drop overlay shown over the <code>ui5-upload-collection</code> when files
75+
* are dragged. If you don't intend to use drag and drop, set this property to <code>true</code>
76+
* <br><br>
77+
* <b>Note:</b> It is up to the application developer to add handler for <code>drop</code> event and handle it.
78+
* <code>ui5-upload-collection</code> only shows an overlay.
79+
*
80+
* @type {boolean}
81+
* @defaultvalue false
82+
* @public
83+
*/
84+
noDnd: {
85+
type: Boolean,
86+
},
87+
88+
/**
89+
* Indicates what overlay to show when files are being dragged.
90+
*
91+
* @type {UploadCollectionDnDOverlayMode}
92+
* @defaultvalue "None"
93+
* @private
94+
*/
95+
_dndOverlayMode: {
96+
type: String,
97+
defaultValue: UploadCollectionDnDOverlayMode.None,
98+
},
99+
},
100+
managedSlots: true,
101+
slots: /** @lends sap.ui.webcomponents.fiori.UploadCollection.prototype */ {
102+
/**
103+
* Defines the items of the <code>ui5-upload-collection</code>.
104+
* <br><b>Note:</b> Use <code>ui5-upload-collection-item</code> for the intended design.
105+
*
106+
* @type {HTMLElement[]}
107+
* @slot
108+
* @public
109+
*/
110+
"default": {
111+
propertyName: "items",
112+
type: HTMLElement,
113+
},
114+
115+
/**
116+
* Defines the <code>ui5-upload-collection</code> header.
117+
*
118+
* @type {HTMLElement[]}
119+
* @slot
120+
* @public
121+
*/
122+
header: {
123+
type: HTMLElement,
124+
},
125+
},
126+
events: /** @lends sap.ui.webcomponents.fiori.UploadCollection.prototype */ {
127+
/**
128+
* Fired when the Delete button of any item is pressed.
129+
* <br><br>
130+
* <b>Note:</b> A Delete button is displayed on each item,
131+
* when the <code>ui5-upload-collection</code> <code>mode</code> property is set to <code>Delete</code>.
132+
* @event
133+
* @param {HTMLElement} item The <code>ui5-upload-collection-item</code> which was renamed.
134+
* @public
135+
*/
136+
fileDeleted: {
137+
detail: {
138+
item: { type: HTMLElement },
139+
},
140+
},
141+
142+
/**
143+
* Fired when selection is changed by user interaction
144+
* in <code>SingleSelect</code> and <code>MultiSelect</code> modes.
145+
*
146+
* @event
147+
* @param {Array} selectedItems An array of the selected items.
148+
* @public
149+
*/
150+
selectionChange: {
151+
detail: {
152+
selectedItems: { type: Array },
153+
},
154+
},
155+
},
156+
};
157+
158+
/**
159+
* @class
160+
*
161+
* <h3 class="comment-api-title">Overview</h3>
162+
* This component allows you to represent files before uploading them to a server, with the help of <code>ui5-upload-collection-item</code>.
163+
* It also allows you to show already uploaded files.
164+
*
165+
* <h3>ES6 Module Import</h3>
166+
* <code>import @ui5/webcomponents-fiori/dist/UploadCollection.js";</code>
167+
*
168+
* @constructor
169+
* @author SAP SE
170+
* @alias sap.ui.webcomponents.fiori.UploadCollection
171+
* @extends UI5Element
172+
* @tagname ui5-upload-collection
173+
* @appenddocs UploadCollectionItem
174+
* @public
175+
* @since 1.0.0-rc.7
176+
*/
177+
class UploadCollection extends UI5Element {
178+
static get metadata() {
179+
return metadata;
180+
}
181+
182+
static get render() {
183+
return litRender;
184+
}
185+
186+
static get styles() {
187+
return UploadCollectionCss;
188+
}
189+
190+
static get template() {
191+
return UploadCollectionTemplate;
192+
}
193+
194+
static async onDefine() {
195+
await Promise.all([
196+
Icon.define(),
197+
Label.define(),
198+
List.define(),
199+
Title.define(),
200+
fetchI18nBundle("@ui5/webcomponents-fiori"),
201+
]);
202+
}
203+
204+
constructor() {
205+
super();
206+
this.i18nBundle = getI18nBundle("@ui5/webcomponents-fiori");
207+
this._bodyDnDHandler = event => {
208+
if (this._dndOverlayMode !== UploadCollectionDnDOverlayMode.Drop) {
209+
this._dndOverlayMode = event.mode;
210+
}
211+
};
212+
}
213+
214+
onEnterDOM() {
215+
if (this.noDnd) {
216+
return;
217+
}
218+
219+
attachBodyDnDHandler(this._bodyDnDHandler);
220+
}
221+
222+
onExitDOM() {
223+
if (this.noDnd) {
224+
return;
225+
}
226+
227+
detachBodyDnDHandler(this._bodyDnDHandler);
228+
}
229+
230+
_ondragenter(event) {
231+
if (this.noDnd) {
232+
return;
233+
}
234+
235+
if (!draggingFiles(event)) {
236+
return;
237+
}
238+
239+
this._dndOverlayMode = UploadCollectionDnDOverlayMode.Drop;
240+
}
241+
242+
_ondrop(event) {
243+
if (this.noDnd) {
244+
return;
245+
}
246+
247+
this._dndOverlayMode = UploadCollectionDnDOverlayMode.None;
248+
}
249+
250+
_ondragover(event) {
251+
if (this.noDnd) {
252+
return;
253+
}
254+
255+
event.preventDefault();
256+
}
257+
258+
_ondragleave(event) {
259+
if (this.noDnd) {
260+
return;
261+
}
262+
263+
this._dndOverlayMode = UploadCollectionDnDOverlayMode.Drag;
264+
}
265+
266+
_onItemDelete(event) {
267+
this.fireEvent("fileDeleted", { item: event.detail.item });
268+
}
269+
270+
_onSelectionChange(event) {
271+
this.fireEvent("selectionChange", { selectedItems: event.detail.selectedItems });
272+
}
273+
274+
get classes() {
275+
return {
276+
dndOverlay: {
277+
"uc-dnd-overlay": true,
278+
"uc-drag-overlay": this._dndOverlayMode === UploadCollectionDnDOverlayMode.Drag,
279+
"uc-drop-overlay": this._dndOverlayMode === UploadCollectionDnDOverlayMode.Drop,
280+
},
281+
};
282+
}
283+
284+
get _root() {
285+
return this.shadowRoot.querySelector(".ui5-uc-root");
286+
}
287+
288+
get _dndOverlay() {
289+
return this._root.querySelector(".uc-dnd-overlay");
290+
}
291+
292+
get _showDndOverlay() {
293+
return this._dndOverlayMode !== UploadCollectionDnDOverlayMode.None;
294+
}
295+
296+
get _showNoData() {
297+
return this.items.length === 0 && !this._showDndOverlay;
298+
}
299+
300+
get _noDataText() {
301+
return this.noDataText || this.i18nBundle.getText(UPLOADCOLLECTION_NO_DATA_TEXT);
302+
}
303+
304+
get _noDataDescription() {
305+
return this.noDataDescription || this.i18nBundle.getText(UPLOADCOLLECTION_NO_DATA_DESCRIPTION);
306+
}
307+
308+
get _dndOverlayText() {
309+
if (this._dndOverlayMode === UploadCollectionDnDOverlayMode.Drag) {
310+
return this.i18nBundle.getText(UPLOADCOLLECTION_DRAG_FILE_INDICATOR);
311+
}
312+
313+
return this.i18nBundle.getText(UPLOADCOLLECTION_DROP_FILE_INDICATOR);
314+
}
315+
}
316+
317+
UploadCollection.define();
318+
319+
export default UploadCollection;

0 commit comments

Comments
 (0)