Skip to content

Commit 2dff1f4

Browse files
authored
feat(ui5-illustrated-message): added new component (#3480)
The PR introduces new web component, called ui5-illustrated-message: - The component provides illustration and texts that add context to it. - The Component supports 4 main breakpoints: scene, dialog, spot and base and each of them provides different illustration. Note: In base breakpoint only title and subtitle of the component are displayed. Fixes: #3376 Closes: #3376
1 parent f4f5864 commit 2dff1f4

File tree

88 files changed

+2257
-0
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

88 files changed

+2257
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import getSharedResource from "../getSharedResource.js";
2+
3+
const registry = getSharedResource("SVGIllustration.registry", new Map());
4+
const ILLUSTRATION_NOT_FOUND = "ILLUSTRATION_NOT_FOUND";
5+
6+
const registerIllustration = (name, { dialogSvg, sceneSvg, spotSvg, title, subtitle } = {}) => { // eslint-disable-line
7+
registry.set(name, {
8+
dialogSvg,
9+
sceneSvg,
10+
spotSvg,
11+
title,
12+
subtitle,
13+
});
14+
};
15+
16+
const getIllustrationDataSync = nameProp => {
17+
return registry.get(nameProp) || ILLUSTRATION_NOT_FOUND;
18+
};
19+
20+
export {
21+
getIllustrationDataSync,
22+
registerIllustration,
23+
};

packages/fiori/bundle.common.js

+14
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,23 @@
11
// FIORI features
22
import "./dist/features/CoPilotAnimation.js";
33

4+
// FIORI illustrations
5+
import "./dist/illustrations/UnableToUpload.js";
6+
import "./dist/illustrations/UnableToLoad.js";
7+
import "./dist/illustrations/NoTasks.js";
8+
import "./dist/illustrations/NoSearchResults.js";
9+
import "./dist/illustrations/NoSavedItems.js";
10+
import "./dist/illustrations/NoNotifications.js";
11+
import "./dist/illustrations/NoMail.js";
12+
import "./dist/illustrations/NoEntries.js";
13+
import "./dist/illustrations/NoData.js";
14+
import "./dist/illustrations/NoActivities.js";
15+
import "./dist/illustrations/BeforeSearch";
16+
417
// FIORI components
518
import Bar from "./dist/Bar.js";
619
import FlexibleColumnLayout from "./dist/FlexibleColumnLayout.js";
20+
import IllustratedMessage from "./dist/IllustratedMessage.js";
721
import ProductSwitch from "./dist/ProductSwitch.js";
822
import ProductSwitchItem from "./dist/ProductSwitchItem.js";
923
import SideNavigation from "./dist/SideNavigation.js";
+45
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
<div class="ui5-illustrated-message-root">
2+
<div class="ui5-illustrated-message-illustration">
3+
{{{effectiveIllustration}}}
4+
</div>
5+
<ui5-title level="H2" class="ui5-illustrated-message-title" wrapping-type="Normal">{{effectiveTitleText}}</ui5-title>
6+
<div class="ui5-illustrated-message-subtitle">{{effectiveSubitleText}}</div>
7+
{{#if hasActions}}
8+
<div class="ui5-illustrated-message-actions">
9+
<slot></slot>
10+
</div>
11+
{{/if}}
12+
</div>
13+
14+
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" class="ui5-illustrated-message-util">
15+
<defs>
16+
<pattern id="sapIllus_PatternShadow" data-name="sapIllus_PatternShadow" width="3" height="5.5"
17+
patternUnits="userSpaceOnUse" viewBox="0 0 3 5.5">
18+
<rect class="sapIllus_NoColor" style="fill:var(--sapIllus_NoColor)" width="3" height="5.5" />
19+
<circle class="sapIllus_BrandColorPrimary" style="fill:var(--sapIllus_BrandColorPrimary)" cx="3" cy="5.5001"
20+
r="0.5" />
21+
<circle class="sapIllus_BrandColorPrimary" style="fill:var(--sapIllus_BrandColorPrimary)" cy="5.5001"
22+
r="0.5" />
23+
<circle class="sapIllus_BrandColorPrimary" style="fill:var(--sapIllus_BrandColorPrimary)" cx="1.5"
24+
cy="2.7501" r="0.5" />
25+
<circle class="sapIllus_BrandColorPrimary" style="fill:var(--sapIllus_BrandColorPrimary)" cx="3" cy="0.0001"
26+
r="0.5" />
27+
<circle class="sapIllus_BrandColorPrimary" style="fill:var(--sapIllus_BrandColorPrimary)" cy="0.0001"
28+
r="0.5" />
29+
</pattern>
30+
<pattern id="sapIllus_PatternHighlight" data-name="sapIllus_PatternHighlight" width="3" height="5.5"
31+
patternTransform="translate(35.9059 309.6208)" patternUnits="userSpaceOnUse" viewBox="0 0 3 5.5">
32+
<rect class="sapIllus_NoColor" style="fill:var(--sapIllus_NoColor)" width="3" height="5.5" />
33+
<circle class="sapIllus_ObjectFillColor" style="fill:var(--sapIllus_ObjectFillColor)" cx="3.0001"
34+
cy="5.5001" r="0.5" />
35+
<circle class="sapIllus_ObjectFillColor" style="fill:var(--sapIllus_ObjectFillColor)" cx="0.0001"
36+
cy="5.5001" r="0.5" />
37+
<circle class="sapIllus_ObjectFillColor" style="fill:var(--sapIllus_ObjectFillColor)" cx="1.5001"
38+
cy="2.7501" r="0.5" />
39+
<circle class="sapIllus_ObjectFillColor" style="fill:var(--sapIllus_ObjectFillColor)" cx="3.0001"
40+
cy="0.0001" r="0.5" />
41+
<circle class="sapIllus_ObjectFillColor" style="fill:var(--sapIllus_ObjectFillColor)" cx="0.0001"
42+
cy="0.0001" r="0.5" />
43+
</pattern>
44+
</defs>
45+
</svg>
+246
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,246 @@
1+
import UI5Element from "@ui5/webcomponents-base/dist/UI5Element.js";
2+
import ResizeHandler from "@ui5/webcomponents-base/dist/delegate/ResizeHandler.js";
3+
import { getIllustrationDataSync } from "@ui5/webcomponents-base/dist/asset-registries/Illustrations.js";
4+
5+
import { getI18nBundle, fetchI18nBundle } from "@ui5/webcomponents-base/dist/i18nBundle.js";
6+
import litRender from "@ui5/webcomponents-base/dist/renderer/LitRenderer.js";
7+
import IllustratedMessageTemplate from "./generated/templates/IllustratedMessageTemplate.lit.js";
8+
import IllustrationMessageType from "./types/IllustrationMessageType.js";
9+
10+
// Styles
11+
import IllustratedMessageCss from "./generated/themes/IllustratedMessage.css.js";
12+
13+
const ILLUSTRATION_NOT_FOUND = "ILLUSTRATION_NOT_FOUND";
14+
15+
/**
16+
* @public
17+
*/
18+
const metadata = {
19+
tag: "ui5-illustrated-message",
20+
managedSlots: true,
21+
properties: /** @lends sap.ui.webcomponents.fiori.IllustratedMessage.prototype */ {
22+
/**
23+
* Defines the title of the component.
24+
* <b>Note:</b> Using this property, the default title text of illustration will be overwritten.
25+
* @type {string}
26+
* @defaultvalue ""
27+
* @since 1.0.0-rc.15
28+
* @public
29+
*/
30+
titleText: {
31+
type: String,
32+
},
33+
/**
34+
* Defines the subtitle of the component.
35+
* <b>Note:</b> Using this property, the default subtitle text of illustration will be overwritten.
36+
* @type {string}
37+
* @defaultvalue ""
38+
* @since 1.0.0-rc.15
39+
* @public
40+
*/
41+
subtitleText: {
42+
type: String,
43+
},
44+
/**
45+
* Determinates what is the current media of the component based on its width.
46+
* @private
47+
*/
48+
media: {
49+
type: String,
50+
},
51+
/**
52+
* Determinates whether illustration is invalid.
53+
* @private
54+
*/
55+
invalid: {
56+
type: Boolean,
57+
},
58+
/**
59+
* Defines the illustration name that will be displayed in the component.
60+
* <br><br>
61+
* Available illustrations are:
62+
* <ul>
63+
* <li><code>BeforeSearch</code></li>
64+
* <li><code>NoActivities</code></li>
65+
* <li><code>NoData</code></li>
66+
* <li><code>NoEntries</code></li>
67+
* <li><code>NoMail</code></li>
68+
* <li><code>NoNotifications</code></li>
69+
* <li><code>NoSavedItems</code></li>
70+
* <li><code>NoSearchResults</code></li>
71+
* <li><code>NoTasks</code></li>
72+
* <li><code>UnableToLoad</code></li>
73+
* <li><code>UnableToUpload</code></li>
74+
* </ul>
75+
* <br><br>
76+
* <b>Note:</b> By default BeforeSearch illustration is loaded. When using illustration type
77+
* it have to be loaded separately (<code>import @ui5/webcomponents-fiori/dist/illustrations/BeforeSearch.js";</code>).
78+
* @type {IllustrationMessageType}
79+
* @defaultvalue "BeforeSearch"
80+
* @since 1.0.0-rc.15
81+
* @public
82+
*/
83+
name: {
84+
type: IllustrationMessageType,
85+
defaultValue: IllustrationMessageType.BeforeSearch,
86+
},
87+
},
88+
slots: /** @lends sap.ui.webcomponents.fiori.IllustratedMessage.prototype */ {
89+
/**
90+
* Defines the component actions.
91+
* @type {sap.ui.webcomponents.main.Button[]}
92+
* @slot actions
93+
* @public
94+
*/
95+
"default": {
96+
propertyName: "actions",
97+
type: HTMLElement,
98+
},
99+
},
100+
events: /** @lends sap.ui.webcomponents.fiori.IllustratedMessage.prototype */ {
101+
//
102+
},
103+
};
104+
105+
/**
106+
* @class
107+
*
108+
* <h3 class="comment-api-title">Overview</h3>
109+
* An <code>ui5-illustrated-message</code> recommended combination of a solution-oriented message, an engaging
110+
* illustration, and conversational tone to better communicate an empty or a success state than just show
111+
* a message alone.
112+
*
113+
* Each illistration has default internationalised title and subtitle texts. Also they can be managed with
114+
* <code>titleText</code> and <code>subtitleText</code> properties.
115+
*
116+
* <b>Note:</b> By default BeforeSearch illustration is loaded.
117+
*
118+
* <h3>Usage</h3>
119+
* <code>ui5-illustrated-message</code> is meant to be used inside container component, for example a <code>ui5-card</code>,
120+
* a <code>ui5-dialog</code> or a <code>ui5-page</code>
121+
*
122+
* For the <code>ui5-illustrated-message</code>
123+
* <h3>ES6 Module Import</h3>
124+
*
125+
* <code>import @ui5/webcomponents-fiori/dist/IllustratedMessage.js";</code>
126+
*
127+
* @constructor
128+
* @author SAP SE
129+
* @alias sap.ui.webcomponents.fiori.IllustratedMessage
130+
* @extends UI5Element
131+
* @tagname ui5-illustrated-message
132+
* @public
133+
* @since 1.0.0-rc.15
134+
*/
135+
class IllustratedMessage extends UI5Element {
136+
constructor() {
137+
super();
138+
139+
this.i18nBundle = getI18nBundle("@ui5/webcomponents-fiori");
140+
this._handleResize = this.handleResize.bind(this);
141+
}
142+
143+
static get metadata() {
144+
return metadata;
145+
}
146+
147+
static get render() {
148+
return litRender;
149+
}
150+
151+
static get styles() {
152+
return IllustratedMessageCss;
153+
}
154+
155+
static get template() {
156+
return IllustratedMessageTemplate;
157+
}
158+
159+
static async onDefine() {
160+
await fetchI18nBundle("@ui5/webcomponents-fiori");
161+
}
162+
163+
static get BREAKPOINTS() {
164+
return {
165+
DIALOG: 679,
166+
SPOT: 319,
167+
BASE: 259,
168+
};
169+
}
170+
171+
static get MEDIA() {
172+
return {
173+
BASE: "base",
174+
SPOT: "spot",
175+
DIALOG: "dialog",
176+
SCENE: "scene",
177+
};
178+
}
179+
180+
onBeforeRendering() {
181+
const illustrationData = getIllustrationDataSync(this.name);
182+
183+
if (illustrationData === ILLUSTRATION_NOT_FOUND) {
184+
this.invalid = true;
185+
/* eslint-disable-next-line */
186+
return console.warn(`Required illustration is not registered. You can either import the illustration as a module in order to use it e.g. "@ui5/webcomponents-fiori/dist/illustrations/${this.name}.js".`);
187+
}
188+
189+
this.invalid = false;
190+
this.spotSvg = illustrationData.spotSvg;
191+
this.dialogSvg = illustrationData.dialogSvg;
192+
this.sceneSvg = illustrationData.sceneSvg;
193+
194+
this.illustrationTitle = this.i18nBundle.getText(illustrationData.title);
195+
this.illustrationSubtitle = this.i18nBundle.getText(illustrationData.subtitle);
196+
}
197+
198+
onEnterDOM() {
199+
ResizeHandler.register(this, this._handleResize);
200+
}
201+
202+
onExitDOM() {
203+
ResizeHandler.deregister(this, this._handleResize);
204+
}
205+
206+
handleResize() {
207+
if (this.offsetWidth <= IllustratedMessage.BREAKPOINTS.BASE) {
208+
this.media = IllustratedMessage.MEDIA.BASE;
209+
} else if (this.offsetWidth <= IllustratedMessage.BREAKPOINTS.SPOT) {
210+
this.media = IllustratedMessage.MEDIA.SPOT;
211+
} else if (this.offsetWidth <= IllustratedMessage.BREAKPOINTS.DIALOG) {
212+
this.media = IllustratedMessage.MEDIA.DIALOG;
213+
} else {
214+
this.media = IllustratedMessage.MEDIA.SCENE;
215+
}
216+
}
217+
218+
get effectiveIllustration() {
219+
switch (this.media) {
220+
case IllustratedMessage.MEDIA.SPOT:
221+
return this.spotSvg;
222+
case IllustratedMessage.MEDIA.DIALOG:
223+
return this.dialogSvg;
224+
case IllustratedMessage.MEDIA.SCENE:
225+
return this.sceneSvg;
226+
default:
227+
return "";
228+
}
229+
}
230+
231+
get effectiveTitleText() {
232+
return this.titleText ? this.titleText : this.illustrationTitle;
233+
}
234+
235+
get effectiveSubitleText() {
236+
return this.subtitleText ? this.subtitleText : this.illustrationSubtitle;
237+
}
238+
239+
get hasActions() {
240+
return !!this.actions.length && this.media !== IllustratedMessage.MEDIA.BASE;
241+
}
242+
}
243+
244+
IllustratedMessage.define();
245+
246+
export default IllustratedMessage;

0 commit comments

Comments
 (0)