Skip to content

Commit

Permalink
feat(ui5-illustrated-message): added new component (#3480)
Browse files Browse the repository at this point in the history
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
  • Loading branch information
nnaydenow authored Jul 23, 2021
1 parent f4f5864 commit 2dff1f4
Show file tree
Hide file tree
Showing 88 changed files with 2,257 additions and 0 deletions.
23 changes: 23 additions & 0 deletions packages/base/src/asset-registries/Illustrations.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import getSharedResource from "../getSharedResource.js";

const registry = getSharedResource("SVGIllustration.registry", new Map());
const ILLUSTRATION_NOT_FOUND = "ILLUSTRATION_NOT_FOUND";

const registerIllustration = (name, { dialogSvg, sceneSvg, spotSvg, title, subtitle } = {}) => { // eslint-disable-line
registry.set(name, {
dialogSvg,
sceneSvg,
spotSvg,
title,
subtitle,
});
};

const getIllustrationDataSync = nameProp => {
return registry.get(nameProp) || ILLUSTRATION_NOT_FOUND;
};

export {
getIllustrationDataSync,
registerIllustration,
};
14 changes: 14 additions & 0 deletions packages/fiori/bundle.common.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,23 @@
// FIORI features
import "./dist/features/CoPilotAnimation.js";

// FIORI illustrations
import "./dist/illustrations/UnableToUpload.js";
import "./dist/illustrations/UnableToLoad.js";
import "./dist/illustrations/NoTasks.js";
import "./dist/illustrations/NoSearchResults.js";
import "./dist/illustrations/NoSavedItems.js";
import "./dist/illustrations/NoNotifications.js";
import "./dist/illustrations/NoMail.js";
import "./dist/illustrations/NoEntries.js";
import "./dist/illustrations/NoData.js";
import "./dist/illustrations/NoActivities.js";
import "./dist/illustrations/BeforeSearch";

// FIORI components
import Bar from "./dist/Bar.js";
import FlexibleColumnLayout from "./dist/FlexibleColumnLayout.js";
import IllustratedMessage from "./dist/IllustratedMessage.js";
import ProductSwitch from "./dist/ProductSwitch.js";
import ProductSwitchItem from "./dist/ProductSwitchItem.js";
import SideNavigation from "./dist/SideNavigation.js";
Expand Down
45 changes: 45 additions & 0 deletions packages/fiori/src/IllustratedMessage.hbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
<div class="ui5-illustrated-message-root">
<div class="ui5-illustrated-message-illustration">
{{{effectiveIllustration}}}
</div>
<ui5-title level="H2" class="ui5-illustrated-message-title" wrapping-type="Normal">{{effectiveTitleText}}</ui5-title>
<div class="ui5-illustrated-message-subtitle">{{effectiveSubitleText}}</div>
{{#if hasActions}}
<div class="ui5-illustrated-message-actions">
<slot></slot>
</div>
{{/if}}
</div>

<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" class="ui5-illustrated-message-util">
<defs>
<pattern id="sapIllus_PatternShadow" data-name="sapIllus_PatternShadow" width="3" height="5.5"
patternUnits="userSpaceOnUse" viewBox="0 0 3 5.5">
<rect class="sapIllus_NoColor" style="fill:var(--sapIllus_NoColor)" width="3" height="5.5" />
<circle class="sapIllus_BrandColorPrimary" style="fill:var(--sapIllus_BrandColorPrimary)" cx="3" cy="5.5001"
r="0.5" />
<circle class="sapIllus_BrandColorPrimary" style="fill:var(--sapIllus_BrandColorPrimary)" cy="5.5001"
r="0.5" />
<circle class="sapIllus_BrandColorPrimary" style="fill:var(--sapIllus_BrandColorPrimary)" cx="1.5"
cy="2.7501" r="0.5" />
<circle class="sapIllus_BrandColorPrimary" style="fill:var(--sapIllus_BrandColorPrimary)" cx="3" cy="0.0001"
r="0.5" />
<circle class="sapIllus_BrandColorPrimary" style="fill:var(--sapIllus_BrandColorPrimary)" cy="0.0001"
r="0.5" />
</pattern>
<pattern id="sapIllus_PatternHighlight" data-name="sapIllus_PatternHighlight" width="3" height="5.5"
patternTransform="translate(35.9059 309.6208)" patternUnits="userSpaceOnUse" viewBox="0 0 3 5.5">
<rect class="sapIllus_NoColor" style="fill:var(--sapIllus_NoColor)" width="3" height="5.5" />
<circle class="sapIllus_ObjectFillColor" style="fill:var(--sapIllus_ObjectFillColor)" cx="3.0001"
cy="5.5001" r="0.5" />
<circle class="sapIllus_ObjectFillColor" style="fill:var(--sapIllus_ObjectFillColor)" cx="0.0001"
cy="5.5001" r="0.5" />
<circle class="sapIllus_ObjectFillColor" style="fill:var(--sapIllus_ObjectFillColor)" cx="1.5001"
cy="2.7501" r="0.5" />
<circle class="sapIllus_ObjectFillColor" style="fill:var(--sapIllus_ObjectFillColor)" cx="3.0001"
cy="0.0001" r="0.5" />
<circle class="sapIllus_ObjectFillColor" style="fill:var(--sapIllus_ObjectFillColor)" cx="0.0001"
cy="0.0001" r="0.5" />
</pattern>
</defs>
</svg>
246 changes: 246 additions & 0 deletions packages/fiori/src/IllustratedMessage.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,246 @@
import UI5Element from "@ui5/webcomponents-base/dist/UI5Element.js";
import ResizeHandler from "@ui5/webcomponents-base/dist/delegate/ResizeHandler.js";
import { getIllustrationDataSync } from "@ui5/webcomponents-base/dist/asset-registries/Illustrations.js";

import { getI18nBundle, fetchI18nBundle } from "@ui5/webcomponents-base/dist/i18nBundle.js";
import litRender from "@ui5/webcomponents-base/dist/renderer/LitRenderer.js";
import IllustratedMessageTemplate from "./generated/templates/IllustratedMessageTemplate.lit.js";
import IllustrationMessageType from "./types/IllustrationMessageType.js";

// Styles
import IllustratedMessageCss from "./generated/themes/IllustratedMessage.css.js";

const ILLUSTRATION_NOT_FOUND = "ILLUSTRATION_NOT_FOUND";

/**
* @public
*/
const metadata = {
tag: "ui5-illustrated-message",
managedSlots: true,
properties: /** @lends sap.ui.webcomponents.fiori.IllustratedMessage.prototype */ {
/**
* Defines the title of the component.
* <b>Note:</b> Using this property, the default title text of illustration will be overwritten.
* @type {string}
* @defaultvalue ""
* @since 1.0.0-rc.15
* @public
*/
titleText: {
type: String,
},
/**
* Defines the subtitle of the component.
* <b>Note:</b> Using this property, the default subtitle text of illustration will be overwritten.
* @type {string}
* @defaultvalue ""
* @since 1.0.0-rc.15
* @public
*/
subtitleText: {
type: String,
},
/**
* Determinates what is the current media of the component based on its width.
* @private
*/
media: {
type: String,
},
/**
* Determinates whether illustration is invalid.
* @private
*/
invalid: {
type: Boolean,
},
/**
* Defines the illustration name that will be displayed in the component.
* <br><br>
* Available illustrations are:
* <ul>
* <li><code>BeforeSearch</code></li>
* <li><code>NoActivities</code></li>
* <li><code>NoData</code></li>
* <li><code>NoEntries</code></li>
* <li><code>NoMail</code></li>
* <li><code>NoNotifications</code></li>
* <li><code>NoSavedItems</code></li>
* <li><code>NoSearchResults</code></li>
* <li><code>NoTasks</code></li>
* <li><code>UnableToLoad</code></li>
* <li><code>UnableToUpload</code></li>
* </ul>
* <br><br>
* <b>Note:</b> By default BeforeSearch illustration is loaded. When using illustration type
* it have to be loaded separately (<code>import @ui5/webcomponents-fiori/dist/illustrations/BeforeSearch.js";</code>).
* @type {IllustrationMessageType}
* @defaultvalue "BeforeSearch"
* @since 1.0.0-rc.15
* @public
*/
name: {
type: IllustrationMessageType,
defaultValue: IllustrationMessageType.BeforeSearch,
},
},
slots: /** @lends sap.ui.webcomponents.fiori.IllustratedMessage.prototype */ {
/**
* Defines the component actions.
* @type {sap.ui.webcomponents.main.Button[]}
* @slot actions
* @public
*/
"default": {
propertyName: "actions",
type: HTMLElement,
},
},
events: /** @lends sap.ui.webcomponents.fiori.IllustratedMessage.prototype */ {
//
},
};

/**
* @class
*
* <h3 class="comment-api-title">Overview</h3>
* An <code>ui5-illustrated-message</code> recommended combination of a solution-oriented message, an engaging
* illustration, and conversational tone to better communicate an empty or a success state than just show
* a message alone.
*
* Each illistration has default internationalised title and subtitle texts. Also they can be managed with
* <code>titleText</code> and <code>subtitleText</code> properties.
*
* <b>Note:</b> By default BeforeSearch illustration is loaded.
*
* <h3>Usage</h3>
* <code>ui5-illustrated-message</code> is meant to be used inside container component, for example a <code>ui5-card</code>,
* a <code>ui5-dialog</code> or a <code>ui5-page</code>
*
* For the <code>ui5-illustrated-message</code>
* <h3>ES6 Module Import</h3>
*
* <code>import @ui5/webcomponents-fiori/dist/IllustratedMessage.js";</code>
*
* @constructor
* @author SAP SE
* @alias sap.ui.webcomponents.fiori.IllustratedMessage
* @extends UI5Element
* @tagname ui5-illustrated-message
* @public
* @since 1.0.0-rc.15
*/
class IllustratedMessage extends UI5Element {
constructor() {
super();

this.i18nBundle = getI18nBundle("@ui5/webcomponents-fiori");
this._handleResize = this.handleResize.bind(this);
}

static get metadata() {
return metadata;
}

static get render() {
return litRender;
}

static get styles() {
return IllustratedMessageCss;
}

static get template() {
return IllustratedMessageTemplate;
}

static async onDefine() {
await fetchI18nBundle("@ui5/webcomponents-fiori");
}

static get BREAKPOINTS() {
return {
DIALOG: 679,
SPOT: 319,
BASE: 259,
};
}

static get MEDIA() {
return {
BASE: "base",
SPOT: "spot",
DIALOG: "dialog",
SCENE: "scene",
};
}

onBeforeRendering() {
const illustrationData = getIllustrationDataSync(this.name);

if (illustrationData === ILLUSTRATION_NOT_FOUND) {
this.invalid = true;
/* eslint-disable-next-line */
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".`);
}

this.invalid = false;
this.spotSvg = illustrationData.spotSvg;
this.dialogSvg = illustrationData.dialogSvg;
this.sceneSvg = illustrationData.sceneSvg;

this.illustrationTitle = this.i18nBundle.getText(illustrationData.title);
this.illustrationSubtitle = this.i18nBundle.getText(illustrationData.subtitle);
}

onEnterDOM() {
ResizeHandler.register(this, this._handleResize);
}

onExitDOM() {
ResizeHandler.deregister(this, this._handleResize);
}

handleResize() {
if (this.offsetWidth <= IllustratedMessage.BREAKPOINTS.BASE) {
this.media = IllustratedMessage.MEDIA.BASE;
} else if (this.offsetWidth <= IllustratedMessage.BREAKPOINTS.SPOT) {
this.media = IllustratedMessage.MEDIA.SPOT;
} else if (this.offsetWidth <= IllustratedMessage.BREAKPOINTS.DIALOG) {
this.media = IllustratedMessage.MEDIA.DIALOG;
} else {
this.media = IllustratedMessage.MEDIA.SCENE;
}
}

get effectiveIllustration() {
switch (this.media) {
case IllustratedMessage.MEDIA.SPOT:
return this.spotSvg;
case IllustratedMessage.MEDIA.DIALOG:
return this.dialogSvg;
case IllustratedMessage.MEDIA.SCENE:
return this.sceneSvg;
default:
return "";
}
}

get effectiveTitleText() {
return this.titleText ? this.titleText : this.illustrationTitle;
}

get effectiveSubitleText() {
return this.subtitleText ? this.subtitleText : this.illustrationSubtitle;
}

get hasActions() {
return !!this.actions.length && this.media !== IllustratedMessage.MEDIA.BASE;
}
}

IllustratedMessage.define();

export default IllustratedMessage;
Loading

0 comments on commit 2dff1f4

Please sign in to comment.