Skip to content

Commit 8bab408

Browse files
author
Huy TA QUOC
committed
fixup! linagora/esn#6: Added 2 new components, esn-app-grid & esn-popover
1 parent 8910eb9 commit 8bab408

19 files changed

+378
-278
lines changed

src/components.d.ts

+47-80
Original file line numberDiff line numberDiff line change
@@ -4,92 +4,59 @@
44
* This is an autogenerated file created by the Stencil compiler.
55
* It contains typing information for all components that exist in this project.
66
*/
7-
import { HTMLStencilElement, JSXBase } from '@stencil/core/internal';
7+
import { HTMLStencilElement, JSXBase } from "@stencil/core/internal";
88
export namespace Components {
9-
interface EsnAppGrid {
10-
/**
11-
* A serialized JSON object that contains information about the available apps.
12-
*/
13-
serializedItems: string;
14-
}
15-
interface EsnPopover {
16-
/**
17-
* A method to toggle the popover on/off.
18-
*/
19-
toggleShowState: () => Promise<void>;
20-
}
21-
interface MyComponent {
22-
/**
23-
* The first name
24-
*/
25-
first: string;
26-
/**
27-
* The last name
28-
*/
29-
last: string;
30-
/**
31-
* The middle name
32-
*/
33-
middle: string;
34-
}
9+
interface EsnAppGrid {
10+
/**
11+
* A serialized JSON object that contains information about the available apps.
12+
*/
13+
"serializedApplications": string;
14+
}
15+
interface EsnPopover {
16+
/**
17+
* A method to toggle the popover on/off.
18+
*/
19+
"toggleShowState": () => Promise<void>;
20+
}
3521
}
3622
declare global {
37-
interface HTMLEsnAppGridElement extends Components.EsnAppGrid, HTMLStencilElement {}
38-
var HTMLEsnAppGridElement: {
39-
prototype: HTMLEsnAppGridElement;
40-
new (): HTMLEsnAppGridElement;
41-
};
42-
interface HTMLEsnPopoverElement extends Components.EsnPopover, HTMLStencilElement {}
43-
var HTMLEsnPopoverElement: {
44-
prototype: HTMLEsnPopoverElement;
45-
new (): HTMLEsnPopoverElement;
46-
};
47-
interface HTMLMyComponentElement extends Components.MyComponent, HTMLStencilElement {}
48-
var HTMLMyComponentElement: {
49-
prototype: HTMLMyComponentElement;
50-
new (): HTMLMyComponentElement;
51-
};
52-
interface HTMLElementTagNameMap {
53-
'esn-app-grid': HTMLEsnAppGridElement;
54-
'esn-popover': HTMLEsnPopoverElement;
55-
'my-component': HTMLMyComponentElement;
56-
}
23+
interface HTMLEsnAppGridElement extends Components.EsnAppGrid, HTMLStencilElement {
24+
}
25+
var HTMLEsnAppGridElement: {
26+
prototype: HTMLEsnAppGridElement;
27+
new (): HTMLEsnAppGridElement;
28+
};
29+
interface HTMLEsnPopoverElement extends Components.EsnPopover, HTMLStencilElement {
30+
}
31+
var HTMLEsnPopoverElement: {
32+
prototype: HTMLEsnPopoverElement;
33+
new (): HTMLEsnPopoverElement;
34+
};
35+
interface HTMLElementTagNameMap {
36+
"esn-app-grid": HTMLEsnAppGridElement;
37+
"esn-popover": HTMLEsnPopoverElement;
38+
}
5739
}
5840
declare namespace LocalJSX {
59-
interface EsnAppGrid {
60-
/**
61-
* A serialized JSON object that contains information about the available apps.
62-
*/
63-
serializedItems?: string;
64-
}
65-
interface EsnPopover {}
66-
interface MyComponent {
67-
/**
68-
* The first name
69-
*/
70-
first?: string;
71-
/**
72-
* The last name
73-
*/
74-
last?: string;
75-
/**
76-
* The middle name
77-
*/
78-
middle?: string;
79-
}
80-
interface IntrinsicElements {
81-
'esn-app-grid': EsnAppGrid;
82-
'esn-popover': EsnPopover;
83-
'my-component': MyComponent;
84-
}
41+
interface EsnAppGrid {
42+
/**
43+
* A serialized JSON object that contains information about the available apps.
44+
*/
45+
"serializedApplications"?: string;
46+
}
47+
interface EsnPopover {
48+
}
49+
interface IntrinsicElements {
50+
"esn-app-grid": EsnAppGrid;
51+
"esn-popover": EsnPopover;
52+
}
8553
}
8654
export { LocalJSX as JSX };
87-
declare module '@stencil/core' {
88-
export namespace JSX {
89-
interface IntrinsicElements {
90-
'esn-app-grid': LocalJSX.EsnAppGrid & JSXBase.HTMLAttributes<HTMLEsnAppGridElement>;
91-
'esn-popover': LocalJSX.EsnPopover & JSXBase.HTMLAttributes<HTMLEsnPopoverElement>;
92-
'my-component': LocalJSX.MyComponent & JSXBase.HTMLAttributes<HTMLMyComponentElement>;
55+
declare module "@stencil/core" {
56+
export namespace JSX {
57+
interface IntrinsicElements {
58+
"esn-app-grid": LocalJSX.EsnAppGrid & JSXBase.HTMLAttributes<HTMLEsnAppGridElement>;
59+
"esn-popover": LocalJSX.EsnPopover & JSXBase.HTMLAttributes<HTMLEsnPopoverElement>;
60+
}
9361
}
94-
}
9562
}

src/components/esn-app-grid/esn-app-grid.tsx

+54-31
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,22 @@
11
import { Component, Prop, ComponentInterface, State, Host, h } from '@stencil/core';
2-
import fetchSvgIcon from '../../utils/fetch-icon';
3-
import { InternalItem } from './esn-app-grid.types';
4-
import { AppGridTogglerIcon } from './app-grid-icon/AppGridIcon';
2+
import { Application } from './esn-app-grid.types';
3+
import { AppGridTogglerIcon } from './icons/AppGridIcon';
4+
import { CalendarAppIcon } from './icons/CalendarAppIcon';
5+
import { InboxAppIcon } from './icons/InboxAppIcon';
6+
import { AdminAppIcon } from './icons/AdminAppIcon';
7+
import { ContactsAppIcon } from './icons/ContactsAppIcon';
8+
import { LinShareAppIcon } from './icons/LinShareAppIcon';
9+
import { UnknownAppIcon } from './icons/UnknownAppIcon';
10+
11+
const iconMapping = {
12+
Admin: AdminAppIcon,
13+
Calendar: CalendarAppIcon,
14+
Contacts: ContactsAppIcon,
15+
Inbox: InboxAppIcon,
16+
LinShare: LinShareAppIcon
17+
};
18+
19+
const isIconAvailable = (appName: string) => Object.keys(iconMapping).includes(appName);
520

621
@Component({
722
tag: 'esn-app-grid',
@@ -11,40 +26,40 @@ import { AppGridTogglerIcon } from './app-grid-icon/AppGridIcon';
1126
export class EsnAppGrid implements ComponentInterface {
1227
private popover: HTMLElement;
1328

14-
@State() internalItems: InternalItem[] = [];
29+
@State() applications: Application[] = [];
1530

1631
/**
1732
* A serialized JSON object that contains information about the available apps.
1833
*/
19-
@Prop() serializedItems: string;
34+
@Prop() serializedApplications: string;
2035

2136
componentWillLoad() {
2237
try {
23-
if (!this.serializedItems) {
24-
throw new Error("The 'serializedItems' attribute/property must be set to a serialized JSON");
38+
if (!this.serializedApplications) {
39+
throw new Error("The 'serializedApplications' attribute/property must be set to a serialized JSON");
2540
}
2641

27-
const items = JSON.parse(this.serializedItems);
42+
const applications = JSON.parse(this.serializedApplications);
43+
44+
if (Array.isArray(applications) && applications.length) {
45+
this.applications = applications.map(({ name, url }) => {
46+
if (!isIconAvailable(name)) {
47+
console.warn(
48+
`The application with name ${name} does not have an icon available. A fallback icon will be used instead.`
49+
);
50+
}
2851

29-
if (Array.isArray(items) && items.length) {
30-
this.internalItems = items.map(({ name, url }) => ({
31-
name,
32-
url
33-
}));
52+
return {
53+
name,
54+
url
55+
};
56+
});
3457
} else {
35-
throw new Error("The parsed 'serializedItems' has an invalid JSON structure");
58+
throw new Error("The parsed 'serializedApplications' has an invalid JSON structure");
3659
}
37-
38-
Promise.all(items.map(({ iconUrl }) => fetchSvgIcon(iconUrl))).then(iconContents => {
39-
this.internalItems = iconContents.map((iconContent, index) => ({
40-
name: this.internalItems[index].name,
41-
url: this.internalItems[index].url,
42-
iconContent
43-
}));
44-
});
4560
} catch (err) {
4661
console.error('Something went wrong', err);
47-
this.internalItems = [];
62+
this.applications = [];
4863
}
4964
}
5065

@@ -63,14 +78,22 @@ export class EsnAppGrid implements ComponentInterface {
6378
</button>
6479
<esn-popover ref={el => (this.popover = el as HTMLElement)}>
6580
<div class="esn-app-grid__popover-content">
66-
{this.internalItems.map(item => (
67-
<div class="esn-app-grid__app-item">
68-
<a href={item.url}>
69-
<div class="esn-app-grid__app-icon" innerHTML={item.iconContent} />
70-
<span class="label">{item.name}</span>
71-
</a>
72-
</div>
73-
))}
81+
{this.applications.map(application => {
82+
const IconComponent = isIconAvailable(application.name)
83+
? iconMapping[application.name]
84+
: UnknownAppIcon;
85+
86+
return (
87+
<div class="esn-app-grid__app-item">
88+
<a href={application.url}>
89+
<div class="esn-app-grid__app-icon">
90+
<IconComponent />
91+
</div>
92+
<span class="label">{application.name}</span>
93+
</a>
94+
</div>
95+
);
96+
})}
7497
</div>
7598
</esn-popover>
7699
</div>
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,6 @@
1-
export interface Item {
2-
name: string;
3-
iconUrl: string;
4-
url: string;
5-
}
6-
7-
export interface InternalItem {
1+
export interface Application {
82
name: string;
3+
iconUrl?: string;
94
iconContent?: string;
105
url: string;
116
}

src/components/esn-app-grid/icons/AdminAppIcon.tsx

+36
Large diffs are not rendered by default.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import { FunctionalComponent, h } from '@stencil/core';
2+
3+
interface CalendarAppIconProps {}
4+
5+
export const CalendarAppIcon: FunctionalComponent<CalendarAppIconProps> = () => (
6+
<svg
7+
width="100%"
8+
height="100%"
9+
viewBox="0 0 1067 1063"
10+
style={{ fillRule: 'evenodd', clipRule: 'evenodd', strokeLinejoin: 'round', strokeMiterlimit: '1.41421' }}
11+
>
12+
<rect id="Calendar" x="3.832" y="0" width="1062.5" height="1062.5" style={{ fill: 'none' }} />
13+
<g>
14+
<path
15+
id="calendar"
16+
class="app-menu-icon-primary-color"
17+
d="M753.832,781.25l-437.5,0l0,-343.75l437.5,0m-93.75,-218.75l0,62.5l-250,0l0,-62.5l-62.5,0l0,62.5l-31.25,0c-34.562,0 -62.187,27.968 -62.187,62.5l-0.312,437.5c0,34.5 27.937,62.5 62.499,62.5l437.5,0c34.468,0 62.5,-28 62.5,-62.5l0,-437.5c0,-34.532 -28.032,-62.5 -62.5,-62.5l-31.25,0l0,-62.5m-31.25,343.75l-156.25,0l0,156.25l156.25,0l0,-156.25Z"
18+
style={{ fill: '#2196f3', fillRule: 'nonzero' }}
19+
/>
20+
<rect
21+
class="app-menu-icon-accent-color"
22+
x="535.082"
23+
y="561.322"
24+
width="161.438"
25+
height="161.438"
26+
style={{ fill: '#ffc107' }}
27+
/>
28+
</g>
29+
</svg>
30+
);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import { FunctionalComponent, h } from '@stencil/core';
2+
3+
interface ContactsAppIconProps {}
4+
5+
export const ContactsAppIcon: FunctionalComponent<ContactsAppIconProps> = () => (
6+
<svg
7+
width="100%"
8+
height="100%"
9+
viewBox="0 0 1067 1063"
10+
style={{ fillRule: 'evenodd', clipRule: 'evenodd', strokeLinejoin: 'round', strokeMiterlimit: '1.41421' }}
11+
>
12+
<rect id="Contacts" x="3.095" y="0" width="1062.5" height="1062.5" style={{ fill: 'none' }} />
13+
<g>
14+
<rect
15+
id="account.-card.-lines"
16+
class="app-menu-icon-accent-color"
17+
x="570.804"
18+
y="321.116"
19+
width="284.722"
20+
height="222.222"
21+
style={{ fill: '#ffc107' }}
22+
/>
23+
<path
24+
id="account.-card.-details"
25+
class="app-menu-icon-primary-color"
26+
d="M239.206,265.625l590.278,0c30.907,0 59.028,28.121 59.028,59.028l0,413.194c0,30.907 -28.121,59.028 -59.028,59.028l-590.278,0c-30.907,0 -59.027,-28.121 -59.027,-59.028l0,-413.194c0,-30.907 28.12,-59.028 59.027,-59.028Zm354.167,88.542l0,29.514l236.111,0l0,-29.514l-236.111,0Zm0,59.027l0,29.514l236.111,0l0,-29.514l-236.111,0Zm0,59.028l0,29.514l206.597,0l0,-29.514l-206.597,0Zm-177.083,115.287c-59.028,0 -177.084,32.283 -177.084,91.31l0,29.514l354.167,0l0,-29.514c0,-59.027 -118.056,-91.31 -177.083,-91.31Zm0,-233.342c-48.9,0 -88.542,39.641 -88.542,88.541c0,48.902 39.641,88.542 88.542,88.542c48.9,0 88.541,-39.64 88.541,-88.542c0,-48.9 -39.641,-88.541 -88.541,-88.541Z"
27+
style={{ fill: '#2196f3', fillRule: 'nonzero' }}
28+
/>
29+
</g>
30+
</svg>
31+
);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import { FunctionalComponent, h } from '@stencil/core';
2+
3+
interface InboxAppIconProps {}
4+
5+
export const InboxAppIcon: FunctionalComponent<InboxAppIconProps> = () => (
6+
<svg
7+
width="100%"
8+
height="100%"
9+
viewBox="0 0 255 255"
10+
version="1.1"
11+
style={{ fillRule: 'evenodd', clipRule: 'evenodd', strokeLinejoin: 'round', strokeMiterlimit: '1.41421' }}
12+
>
13+
<rect id="Unified-Inbox" x="0" y="0" width="255" height="255" style={{ fill: 'none' }} />
14+
<clipPath id="_clip1">
15+
<rect id="Unified-Inbox1" x="0" y="0" width="255" height="255" />
16+
</clipPath>
17+
<g clip-path="url(#_clip1)">
18+
<g>
19+
<path
20+
id="email.-open"
21+
class="app-menu-icon-primary-color"
22+
d="M67.5,110.098l60,37.492l60,-37.492l0,-0.008l-60,-37.492l-60,37.492l0,0.008Zm135,-0.008l0,75c0,8.284 -6.715,15 -15,15l-120,0c-8.284,0 -15,-6.716 -15,-15l0,-75c0,-5.457 2.914,-10.233 7.271,-12.858l67.729,-42.322l67.73,42.322c4.356,2.625 7.27,7.401 7.27,12.858Z"
23+
style={{ fill: '#2196f3', fillRule: 'nonzero' }}
24+
/>
25+
<path
26+
id="email.paper"
27+
class="app-menu-icon-accent-color"
28+
d="M172.721,71.25l0,48.529l-45.221,27.787l-45.956,-28.522l0,-47.794l91.177,0Z"
29+
style={{ fill: '#ffc107' }}
30+
/>
31+
</g>
32+
<rect x="96.321" y="84.457" width="62.359" height="7.039" style={{ fill: '#ffffff' }} />
33+
<rect x="96.321" y="98.502" width="62.359" height="7.24" style={{ fill: '#ffffff' }} />
34+
<rect x="96.321" y="112.749" width="49.951" height="7.24" style={{ fill: '#ffffff' }} />
35+
</g>
36+
</svg>
37+
);

0 commit comments

Comments
 (0)