@veracity/vui 2.14.8-beta.2 → 2.15.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. package/dist/cjs/avatar/avatar.d.ts.map +1 -1
  2. package/dist/cjs/avatar/avatar.js +40 -9
  3. package/dist/cjs/header/header.types.d.ts +9 -4
  4. package/dist/cjs/header/header.types.d.ts.map +1 -1
  5. package/dist/cjs/header/loggedInHeader.d.ts.map +1 -1
  6. package/dist/cjs/header/loggedInHeader.js +44 -41
  7. package/dist/cjs/header/loggedOutHeader.d.ts.map +1 -1
  8. package/dist/cjs/header/loggedOutHeader.js +17 -3
  9. package/dist/cjs/input/consts.js +2 -2
  10. package/dist/cjs/menu/menu.types.d.ts +5 -1
  11. package/dist/cjs/menu/menu.types.d.ts.map +1 -1
  12. package/dist/cjs/menu/menuList.d.ts.map +1 -1
  13. package/dist/cjs/menu/menuList.js +6 -1
  14. package/dist/cjs/utils/images.d.ts +3 -0
  15. package/dist/cjs/utils/images.d.ts.map +1 -0
  16. package/dist/cjs/utils/images.js +24 -0
  17. package/dist/esm/avatar/avatar.d.ts.map +1 -1
  18. package/dist/esm/avatar/avatar.js +17 -9
  19. package/dist/esm/header/header.types.d.ts +9 -4
  20. package/dist/esm/header/header.types.d.ts.map +1 -1
  21. package/dist/esm/header/loggedInHeader.d.ts.map +1 -1
  22. package/dist/esm/header/loggedInHeader.js +38 -35
  23. package/dist/esm/header/loggedOutHeader.d.ts.map +1 -1
  24. package/dist/esm/header/loggedOutHeader.js +18 -4
  25. package/dist/esm/input/consts.js +2 -2
  26. package/dist/esm/menu/menu.types.d.ts +5 -1
  27. package/dist/esm/menu/menu.types.d.ts.map +1 -1
  28. package/dist/esm/menu/menuList.d.ts.map +1 -1
  29. package/dist/esm/menu/menuList.js +6 -1
  30. package/dist/esm/utils/images.d.ts +3 -0
  31. package/dist/esm/utils/images.d.ts.map +1 -0
  32. package/dist/esm/utils/images.js +20 -0
  33. package/package.json +1 -1
  34. package/src/avatar/avatar.tsx +45 -18
  35. package/src/header/header.types.ts +31 -25
  36. package/src/header/loggedInHeader.tsx +115 -93
  37. package/src/header/loggedOutHeader.tsx +39 -4
  38. package/src/input/consts.ts +2 -2
  39. package/src/menu/menu.types.ts +5 -1
  40. package/src/menu/menuList.tsx +15 -1
  41. package/src/utils/images.ts +20 -0
@@ -1,5 +1,6 @@
1
1
  import React from 'react';
2
2
  import Box from '../box';
3
+ import Button from '../button';
3
4
  import { RenderOnScreen, useDown, useStyleConfig, vui } from '../core';
4
5
  import List from '../list';
5
6
  import { cs, isJsx } from '../utils';
@@ -9,7 +10,7 @@ import HeaderAccountUserInfo from './headerAccountUserInfo';
9
10
  import HeaderServicesMessage from './headerServicesMessage';
10
11
  /** Displays Header content appropriate for logged-in users, such as services, notifications or account information. */
11
12
  export const LoggedInHeader = vui((props, ref) => {
12
- const { account, supportLinks, applicationName, applicationUrl, children, className, isApplication = false, logo, mainLinks, services, showAltUserInfo = true, showMainLinks = isApplication ? false : true, showServices = true, notifications, ...rest } = props;
13
+ const { account, supportLinks, applicationName, applicationUrl, children, className, isApplication = false, isCleanLayout = false, logo, mainLinks, services, showAltUserInfo = true, showMainLinks = isApplication ? false : true, showServices = true, notifications, url, ...rest } = props;
13
14
  if (isApplication) {
14
15
  if (!applicationName) {
15
16
  console.error('Header: isApplication is true but applicationName is not set.');
@@ -21,7 +22,7 @@ export const LoggedInHeader = vui((props, ref) => {
21
22
  const servicesLinks = isJsx(services) ? [] : services?.links || [];
22
23
  const styles = useStyleConfig('Header', useHeaderContext());
23
24
  const localTextColor = rest?.variant === 'dark' ? 'white' : 'darkBlue.main';
24
- return (React.createElement(Header, { className: cs('vui-loggedInHeader', isApplication ? 'vui-loggedInAppHeader' : '', className), mb: isApplication && isSmallScreen ? '41px' : 'inherited', ref: ref, ...rest },
25
+ return (React.createElement(Header, { className: cs('vui-loggedInHeader', isApplication ? 'vui-loggedInAppHeader' : undefined, className), mb: isApplication && isSmallScreen ? '41px' : 'inherited', ref: ref, ...rest },
25
26
  React.createElement(Header.Content, null,
26
27
  (!isApplication || !isLessThanMD) && React.createElement(React.Fragment, null,
27
28
  " ",
@@ -32,40 +33,42 @@ export const LoggedInHeader = vui((props, ref) => {
32
33
  React.createElement(Header.ApplicationName, { color: localTextColor, title: applicationName, url: applicationUrl || '/' }))),
33
34
  !isApplication && React.createElement(RenderOnScreen, { minWidth: "lg" }, children),
34
35
  React.createElement(Box, { ml: "auto" }),
35
- isApplication && (React.createElement(React.Fragment, null,
36
- React.createElement(RenderOnScreen, { minWidth: "lg" },
37
- showMainLinks && mainLinks !== undefined && (React.createElement(React.Fragment, null,
38
- isJsx(mainLinks) ? mainLinks : React.createElement(Header.MainLinks, { links: mainLinks }),
39
- React.createElement(Header.Divider, { mx: 1 }))),
40
- !showMainLinks && mainLinks && (React.createElement(React.Fragment, null,
41
- React.createElement(Header.MobileToggle, { ml: 0 }),
42
- React.createElement(Header.MobileContent, null,
43
- React.createElement(React.Fragment, null,
44
- isJsx(mainLinks) ? (mainLinks) : (React.createElement(Box, { column: true, w: 1 },
45
- React.createElement(List, { py: 1 }, mainLinks?.map(({ icon, url, ...rest }, index) => (React.createElement(List.Item, { iconLeft: icon, key: index, linkProps: { href: url }, ...rest })))))),
46
- !showServices && servicesLinks && (React.createElement(Box, { column: true, w: 1 },
47
- React.createElement(List, { className: "vui-headerHiddenServicesLargeScreen", heading: "My Services", maxH: 295, right: 0, w: 320, ...styles.services.list, borderTop: 1, borderTopColor: "sandstone.main", pt: 1 }, servicesLinks.length > 0 ? (servicesLinks?.map(({ url, ...rest }, index) => (React.createElement(List.Item, { key: index, linkProps: { href: url }, ...rest })))) : (React.createElement(HeaderServicesMessage, null))))))))),
48
- showServices && services && (React.createElement(React.Fragment, null, isJsx(services) ? services : React.createElement(Header.Services, { isApplication: isApplication, ...services })))),
49
- React.createElement(Header.Support, { ...supportLinks }))),
50
- !isApplication && (React.createElement(RenderOnScreen, { minWidth: "lg" },
51
- isJsx(mainLinks) ? mainLinks : React.createElement(Header.MainLinks, { links: mainLinks }),
52
- mainLinks !== undefined && React.createElement(Header.Divider, { mx: 1 }),
53
- isJsx(services) ? services : React.createElement(Header.Services, { ...services }))),
54
- isJsx(notifications) ? notifications : React.createElement(Header.Notifications, { ...notifications }),
55
- !showMainLinks &&
56
- showAltUserInfo &&
57
- (isJsx(userInfo) ? (userInfo) : (React.createElement(React.Fragment, null,
58
- React.createElement(Header.Divider, { ml: 2 }),
59
- React.createElement(HeaderAccountUserInfo, { className: "vui-altUserInfo", color: localTextColor, isApplication: true, ...userInfo })))),
60
- isJsx(account) ? account : React.createElement(Header.Account, { ...account }),
61
- React.createElement(RenderOnScreen, { maxWidth: "lg" },
62
- isApplication && React.createElement(Header.Divider, { ml: 1 }),
63
- !isApplication && (account !== undefined || notifications !== undefined) && React.createElement(Header.Divider, { ml: 1 }),
64
- React.createElement(Header.MobileToggle, null),
65
- React.createElement(Header.MobileContent, null,
66
- !isApplication && children,
36
+ isCleanLayout ? (React.createElement(React.Fragment, null, !!url && (React.createElement(Box, null,
37
+ React.createElement(Button, { as: "a", className: cs('vui-headerSignOut'), href: url, size: "lg", variant: "secondaryDark" }, "Sign out"))))) : (React.createElement(React.Fragment, null,
38
+ isApplication && (React.createElement(React.Fragment, null,
39
+ React.createElement(RenderOnScreen, { minWidth: "lg" },
40
+ showMainLinks && mainLinks !== undefined && (React.createElement(React.Fragment, null,
41
+ isJsx(mainLinks) ? mainLinks : React.createElement(Header.MainLinks, { links: mainLinks }),
42
+ React.createElement(Header.Divider, { mx: 1 }))),
43
+ !showMainLinks && mainLinks && (React.createElement(React.Fragment, null,
44
+ React.createElement(Header.MobileToggle, { ml: 0 }),
45
+ React.createElement(Header.MobileContent, null,
46
+ React.createElement(React.Fragment, null,
47
+ isJsx(mainLinks) ? (mainLinks) : (React.createElement(Box, { column: true, w: 1 },
48
+ React.createElement(List, { py: 1 }, mainLinks?.map(({ icon, url, ...rest }, index) => (React.createElement(List.Item, { iconLeft: icon, key: index, linkProps: { href: url }, ...rest })))))),
49
+ !showServices && servicesLinks && (React.createElement(Box, { column: true, w: 1 },
50
+ React.createElement(List, { className: "vui-headerHiddenServicesLargeScreen", heading: "My Services", maxH: 295, right: 0, w: 320, ...styles.services.list, borderTop: 1, borderTopColor: "sandstone.main", pt: 1 }, servicesLinks.length > 0 ? (servicesLinks?.map(({ url, ...rest }, index) => (React.createElement(List.Item, { key: index, linkProps: { href: url }, ...rest })))) : (React.createElement(HeaderServicesMessage, null))))))))),
51
+ showServices && services && (React.createElement(React.Fragment, null, isJsx(services) ? services : React.createElement(Header.Services, { isApplication: isApplication, ...services })))),
52
+ React.createElement(Header.Support, { ...supportLinks }))),
53
+ !isApplication && (React.createElement(RenderOnScreen, { minWidth: "lg" },
67
54
  isJsx(mainLinks) ? mainLinks : React.createElement(Header.MainLinks, { links: mainLinks }),
68
- isJsx(services) ? services : React.createElement(Header.Services, { ...services, showTopBorder: mainLinks ? true : false }))))));
55
+ mainLinks !== undefined && React.createElement(Header.Divider, { mx: 1 }),
56
+ isJsx(services) ? services : React.createElement(Header.Services, { ...services }))),
57
+ isJsx(notifications) ? notifications : React.createElement(Header.Notifications, { ...notifications }),
58
+ !showMainLinks &&
59
+ showAltUserInfo &&
60
+ (isJsx(userInfo) ? (userInfo) : (React.createElement(React.Fragment, null,
61
+ React.createElement(Header.Divider, { ml: 2 }),
62
+ React.createElement(HeaderAccountUserInfo, { className: "vui-altUserInfo", color: localTextColor, isApplication: true, ...userInfo })))),
63
+ isJsx(account) ? account : React.createElement(Header.Account, { ...account }),
64
+ React.createElement(RenderOnScreen, { maxWidth: "lg" },
65
+ isApplication && React.createElement(Header.Divider, { ml: 1 }),
66
+ !isApplication && (account !== undefined || notifications !== undefined) && React.createElement(Header.Divider, { ml: 1 }),
67
+ React.createElement(Header.MobileToggle, null),
68
+ React.createElement(Header.MobileContent, null,
69
+ !isApplication && children,
70
+ isJsx(mainLinks) ? mainLinks : React.createElement(Header.MainLinks, { links: mainLinks }),
71
+ isJsx(services) ? (services) : (React.createElement(Header.Services, { ...services, showTopBorder: mainLinks ? true : false })))))))));
69
72
  });
70
73
  LoggedInHeader.displayName = 'LoggedInHeader';
71
74
  export default LoggedInHeader;
@@ -1 +1 @@
1
- {"version":3,"file":"loggedOutHeader.d.ts","sourceRoot":"","sources":["../../../src/header/loggedOutHeader.tsx"],"names":[],"mappings":"AAMA,OAAO,EAAE,oBAAoB,EAAE,MAAM,gBAAgB,CAAA;AAErD,iHAAiH;AACjH,eAAO,MAAM,eAAe,6DAwC1B,CAAA;AAIF,eAAe,eAAe,CAAA"}
1
+ {"version":3,"file":"loggedOutHeader.d.ts","sourceRoot":"","sources":["../../../src/header/loggedOutHeader.tsx"],"names":[],"mappings":"AAMA,OAAO,EAAE,oBAAoB,EAAE,MAAM,gBAAgB,CAAA;AAErD,iHAAiH;AACjH,eAAO,MAAM,eAAe,6DA2E1B,CAAA;AAIF,eAAe,eAAe,CAAA"}
@@ -1,14 +1,28 @@
1
1
  import React from 'react';
2
2
  import Box from '../box';
3
- import { RenderOnScreen, vui } from '../core';
3
+ import { RenderOnScreen, useDown, vui } from '../core';
4
4
  import { cs, isJsx } from '../utils';
5
5
  import Header from './header';
6
6
  /** Displays Header content appropriate for logged out users, such as sign in button or create account prompt. */
7
7
  export const LoggedOutHeader = vui((props, ref) => {
8
- const { children, className, createAccount, logo, mainLinks, signIn, ...rest } = props;
9
- return (React.createElement(Header, { className: cs('vui-loggedOutHeader', className), ref: ref, ...rest },
8
+ const { applicationName, applicationUrl, children, className, isApplication = false, createAccount, logo, mainLinks, signIn, ...rest } = props;
9
+ if (isApplication) {
10
+ if (!applicationName) {
11
+ console.error('Header: isApplication is true but applicationName is not set.');
12
+ }
13
+ }
14
+ const isSmallScreen = useDown('md');
15
+ const isLessThanMD = useDown('sm');
16
+ const localTextColor = rest?.variant === 'dark' ? 'white' : 'darkBlue.main';
17
+ return (React.createElement(Header, { className: cs('vui-loggedOutHeader', isApplication ? 'vui-loggedOutAppHeader' : undefined, className), mb: isApplication && isSmallScreen ? '41px' : 'inherited', ref: ref, ...rest },
10
18
  React.createElement(Header.Content, null,
11
- isJsx(logo) ? logo : React.createElement(Header.Logo, { ...logo }),
19
+ (!isApplication || !isLessThanMD) && React.createElement(React.Fragment, null,
20
+ " ",
21
+ isJsx(logo) ? logo : React.createElement(Header.Logo, { ...logo })),
22
+ isApplication && (React.createElement(React.Fragment, null,
23
+ React.createElement(RenderOnScreen, { minWidth: "sm" },
24
+ React.createElement(Header.Divider, { mr: 2 })),
25
+ React.createElement(Header.ApplicationName, { color: localTextColor, title: applicationName, url: applicationUrl || '/' }))),
12
26
  React.createElement(RenderOnScreen, { minWidth: "lg" }, children),
13
27
  React.createElement(Box, { ml: "auto" }),
14
28
  React.createElement(RenderOnScreen, { minWidth: "lg" },
@@ -48,6 +48,6 @@ export const displayValueOnlyTextSize = {
48
48
  export const clearIconSize = {
49
49
  sm: 'sm',
50
50
  md: 'sm',
51
- lg: 'md',
52
- xl: 'lg'
51
+ lg: 'sm',
52
+ xl: 'md'
53
53
  };
@@ -1,4 +1,4 @@
1
- import { RefObject } from 'react';
1
+ import { ReactNode, RefObject } from 'react';
2
2
  import { ListProps } from '../list';
3
3
  import { PopoverContentProps, PopoverProps } from '../popover';
4
4
  import { ThemingProps } from '../theme';
@@ -7,6 +7,10 @@ export type MenuListProps = ListProps & {
7
7
  contentProps?: PopoverContentProps;
8
8
  /** Ref passed to the inner PopoverContent. */
9
9
  contentRef?: RefObject<HTMLDivElement>;
10
+ /** Custom content displayed at the bottom of the panel */
11
+ slotBottom?: ReactNode;
12
+ /** Custom content displayed at the top of the panel */
13
+ slotTop?: ReactNode;
10
14
  };
11
15
  export type MenuProps = ThemingProps<'Menu'> & PopoverProps & {
12
16
  /** Content closes when clicking on a list item. */
@@ -1 +1 @@
1
- {"version":3,"file":"menu.types.d.ts","sourceRoot":"","sources":["../../../src/menu/menu.types.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAEjC,OAAO,EAAE,SAAS,EAAE,MAAM,SAAS,CAAA;AACnC,OAAO,EAAE,mBAAmB,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA;AAC9D,OAAO,EAAE,YAAY,EAAE,MAAM,UAAU,CAAA;AAEvC,MAAM,MAAM,aAAa,GAAG,SAAS,GAAG;IACtC,uDAAuD;IACvD,YAAY,CAAC,EAAE,mBAAmB,CAAA;IAClC,8CAA8C;IAC9C,UAAU,CAAC,EAAE,SAAS,CAAC,cAAc,CAAC,CAAA;CACvC,CAAA;AAED,MAAM,MAAM,SAAS,GAAG,YAAY,CAAC,MAAM,CAAC,GAC1C,YAAY,GAAG;IACb,mDAAmD;IACnD,aAAa,CAAC,EAAE,OAAO,CAAA;CACxB,CAAA"}
1
+ {"version":3,"file":"menu.types.d.ts","sourceRoot":"","sources":["../../../src/menu/menu.types.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAE5C,OAAO,EAAE,SAAS,EAAE,MAAM,SAAS,CAAA;AACnC,OAAO,EAAE,mBAAmB,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA;AAC9D,OAAO,EAAE,YAAY,EAAE,MAAM,UAAU,CAAA;AAEvC,MAAM,MAAM,aAAa,GAAG,SAAS,GAAG;IACtC,uDAAuD;IACvD,YAAY,CAAC,EAAE,mBAAmB,CAAA;IAClC,8CAA8C;IAC9C,UAAU,CAAC,EAAE,SAAS,CAAC,cAAc,CAAC,CAAA;IACtC,0DAA0D;IAC1D,UAAU,CAAC,EAAE,SAAS,CAAA;IACtB,uDAAuD;IACvD,OAAO,CAAC,EAAE,SAAS,CAAA;CACpB,CAAA;AAED,MAAM,MAAM,SAAS,GAAG,YAAY,CAAC,MAAM,CAAC,GAC1C,YAAY,GAAG;IACb,mDAAmD;IACnD,aAAa,CAAC,EAAE,OAAO,CAAA;CACxB,CAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"menuList.d.ts","sourceRoot":"","sources":["../../../src/menu/menuList.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAuB,YAAY,EAAE,MAAM,SAAS,CAAA;AAC3D,OAAO,IAAI,MAAM,SAAS,CAAA;AAI1B,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAA;AAE5C,kEAAkE;AAClE,eAAO,MAAM,QAAQ;aAUV,OAAO,KAAK,OAAO;aACnB,OAAO,KAAK,OAAO;UACtB,OAAO,KAAK,IAAI;UAChB,OAAO,KAAK,IAAI;UAChB,OAAO,SAAS;CACvB,CAAA;AAQD,eAAe,QAAQ,CAAA"}
1
+ {"version":3,"file":"menuList.d.ts","sourceRoot":"","sources":["../../../src/menu/menuList.tsx"],"names":[],"mappings":"AAGA,OAAO,EAAuB,YAAY,EAAE,MAAM,SAAS,CAAA;AAC3D,OAAO,IAAI,MAAM,SAAS,CAAA;AAI1B,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAA;AAE5C,kEAAkE;AAClE,eAAO,MAAM,QAAQ;aAuBV,OAAO,KAAK,OAAO;aACnB,OAAO,KAAK,OAAO;UACtB,OAAO,KAAK,IAAI;UAChB,OAAO,KAAK,IAAI;UAChB,OAAO,SAAS;CACvB,CAAA;AAQD,eAAe,QAAQ,CAAA"}
@@ -1,4 +1,5 @@
1
1
  import React from 'react';
2
+ import { Box } from '../box';
2
3
  import { useStyleConfig, vui } from '../core';
3
4
  import List from '../list';
4
5
  import { PopoverContent } from '../popover';
@@ -7,9 +8,13 @@ import { useMenuContext } from './context';
7
8
  /** Displays a list of menu items in a popover content element. */
8
9
  export const MenuList = vui((props, ref) => {
9
10
  const { className, contentProps, contentRef, ...rest } = props;
11
+ const { slotTop, slotBottom } = props;
10
12
  const styles = useStyleConfig('Menu', useMenuContext());
11
13
  return (React.createElement(PopoverContent, { borderRadius: 0, ref: contentRef, ...contentProps },
12
- React.createElement(List, { className: cs('vui-menuList', className), py: 1, ref: ref, w: "100%", ...styles.list, ...rest, maxH: 295 })));
14
+ React.createElement(Box, { flexDirection: "column" },
15
+ slotTop,
16
+ React.createElement(List, { className: cs('vui-menuList', className), py: 1, ref: ref, w: "100%", ...styles.list, ...rest, maxH: 295 }),
17
+ slotBottom)));
13
18
  });
14
19
  MenuList.Divider = List.Divider;
15
20
  MenuList.Heading = List.Heading;
@@ -0,0 +1,3 @@
1
+ /** Attempts to load the image and returns a boolean indicating success or not. */
2
+ export declare function loadImageUrl(url: string): Promise<boolean>;
3
+ //# sourceMappingURL=images.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"images.d.ts","sourceRoot":"","sources":["../../../src/utils/images.ts"],"names":[],"mappings":"AAEA,kFAAkF;AAClF,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,oBAgBvC"}
@@ -0,0 +1,20 @@
1
+ const loadImagePromises = new Map();
2
+ /** Attempts to load the image and returns a boolean indicating success or not. */
3
+ export function loadImageUrl(url) {
4
+ let promise = loadImagePromises.get(url);
5
+ if (promise)
6
+ return promise;
7
+ promise = new Promise(resolve => {
8
+ const img = new Image();
9
+ img.onload = () => {
10
+ // After this event occurs, you can use the url in the DOM. The browser will not make another request for the image; there will be no delay.
11
+ resolve(true);
12
+ };
13
+ img.onerror = () => {
14
+ resolve(false);
15
+ };
16
+ img.src = url;
17
+ });
18
+ loadImagePromises.set(url, promise);
19
+ return promise;
20
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@veracity/vui",
3
- "version": "2.14.8-beta.2",
3
+ "version": "2.15.2",
4
4
  "description": "Veracity UI is a React component library crafted for use within Veracity applications and pages. Based on Styled Components and @xstyled.",
5
5
  "module": "./dist/esm/index.js",
6
6
  "main": "./dist/cjs/index.js",
@@ -1,21 +1,13 @@
1
- import React from 'react'
1
+ import React, { useEffect, useState } from 'react'
2
2
 
3
3
  import { omitThemingProps, styled, useStyleConfig, vui } from '../core'
4
- import Icon from '../icon'
4
+ import Icon, { IconProp, IconProps } from '../icon'
5
5
  import Image from '../image'
6
6
  import { cs, filterUndefined } from '../utils'
7
+ import { loadImageUrl } from '../utils/images'
7
8
  import { AvatarProps } from './avatar.types'
8
9
  import { defaultGetInitials } from './helpers'
9
10
 
10
- function imageExists(image_url: string) {
11
- const http = new XMLHttpRequest()
12
-
13
- http.open('HEAD', image_url, false)
14
- http.send()
15
-
16
- return http.status !== 404
17
- }
18
-
19
11
  export const AvatarBase = styled.spanBox`
20
12
  align-items: center;
21
13
  display: inline-flex;
@@ -57,8 +49,6 @@ export const Avatar = vui<'span', AvatarProps>((props, ref) => {
57
49
  const borderRadius = isSquare ? (props.size === 'sm' ? '3px' : '6px') : '50%'
58
50
  const initials = getInitials(name)
59
51
 
60
- const internalIcon = src && !imageExists(src) ? 'falUser' : icon
61
-
62
52
  const interactiveProps =
63
53
  !disabled && isInteractive
64
54
  ? ({
@@ -92,16 +82,53 @@ export const Avatar = vui<'span', AvatarProps>((props, ref) => {
92
82
  >
93
83
  {children ? (
94
84
  children
95
- ) : src && imageExists(src) ? (
96
- <Image {...{ borderRadius, src }} />
97
- ) : internalIcon ? (
98
- <Icon name={internalIcon} size={iconSize} />
85
+ ) : src ? (
86
+ <LazyImageOrDefault borderRadius={borderRadius} icon={icon} iconSize={iconSize} initials={initials} src={src} />
87
+ ) : icon ? (
88
+ <Icon name={icon} size={iconSize} />
99
89
  ) : initials ? (
100
90
  initials
101
- ) : null}
91
+ ) : (
92
+ <Icon name="falUser" size={iconSize} />
93
+ )}
102
94
  </AvatarBase>
103
95
  )
104
96
  })
105
97
 
98
+ function LazyImageOrDefault({
99
+ src,
100
+ borderRadius,
101
+ icon,
102
+ iconSize,
103
+ initials
104
+ }: {
105
+ src: string
106
+ borderRadius: string
107
+ icon: IconProp | undefined
108
+ iconSize: IconProps['size']
109
+ initials: string
110
+ }) {
111
+ const [imgLoaded, setImgLoaded] = useState(false)
112
+ useEffect(() => {
113
+ let cancelled = false
114
+ loadImageUrl(src).then(res => {
115
+ if (!cancelled) setImgLoaded(res)
116
+ })
117
+ return () => {
118
+ cancelled = true
119
+ }
120
+ }, [src])
121
+
122
+ return imgLoaded ? (
123
+ <Image borderRadius={borderRadius} src={src} />
124
+ ) : icon ? (
125
+ <Icon name={icon} size={iconSize} />
126
+ ) : initials ? (
127
+ <>initials</>
128
+ ) : (
129
+ <Icon name="falUser" size={iconSize} />
130
+ )
131
+ }
132
+
106
133
  Avatar.displayName = 'Avatar'
107
134
  export default Avatar
@@ -86,39 +86,45 @@ export type HeaderServicesProps = MenuProps &
86
86
 
87
87
  export type HeaderSupportProps = MenuProps & HeaderServicesData & {}
88
88
 
89
- export type HeaderSignInData = {
89
+ export type HeaderButtonData = {
90
90
  /** Provides href to the button link. */
91
91
  url?: string
92
92
  }
93
93
 
94
- export type HeaderSignInProps = ButtonProps & HeaderSignInData
94
+ export type HeaderSignInProps = ButtonProps & HeaderButtonData
95
95
 
96
- export type LoggedInHeaderProps = HeaderProps & {
97
- /** Socket for account popover icon on the right side. */
98
- account?: HeaderAccountData | JSX.Element
99
- supportLinks?: HeaderServicesData
96
+ export type LoggedInHeaderProps = HeaderProps &
97
+ HeaderButtonData & {
98
+ /** Socket for account popover icon on the right side. */
99
+ account?: HeaderAccountData | JSX.Element
100
+ supportLinks?: HeaderServicesData
101
+ /** Socket for the app name on the left side. */
102
+ applicationName?: string
103
+ applicationUrl?: string
104
+ isApplication?: boolean
105
+ isCleanLayout?: boolean
106
+ isSticky?: boolean
107
+ /** Socket for logo on the left side. */
108
+ logo?: LogoData | JSX.Element
109
+ /** Socket for main links in the middle-right. */
110
+ mainLinks?: HeaderLinkData[] | JSX.Element
111
+ /** Socket for notifications icon on the right side. */
112
+ notifications?: HeaderNotificationsData | JSX.Element
113
+ /** Socket for services menu icon on the right side. */
114
+ services?: HeaderServicesData | JSX.Element
115
+ /** Socket for showing the 'alternative' user Info with role (alternative: 3 lines, role. default: 2 lines, no role); introduced for APP; by default it is true. */
116
+ showAltUserInfo?: boolean
117
+ /** Socket for showing the services trigger up front; introduced for APP; by default it is displayed. */
118
+ showServices?: boolean
119
+ /** Socket for showing the main Links up front in case of APP. by default they are folded into the hamburger menu. */
120
+ showMainLinks?: boolean
121
+ }
122
+
123
+ export type LoggedOutHeaderProps = HeaderProps & {
100
124
  /** Socket for the app name on the left side. */
101
125
  applicationName?: string
102
126
  applicationUrl?: string
103
127
  isApplication?: boolean
104
- isSticky?: boolean
105
- /** Socket for logo on the left side. */
106
- logo?: LogoData | JSX.Element
107
- /** Socket for main links in the middle-right. */
108
- mainLinks?: HeaderLinkData[] | JSX.Element
109
- /** Socket for notifications icon on the right side. */
110
- notifications?: HeaderNotificationsData | JSX.Element
111
- /** Socket for services menu icon on the right side. */
112
- services?: HeaderServicesData | JSX.Element
113
- /** Socket for showing the 'alternative' user Info with role (alternative: 3 lines, role. default: 2 lines, no role); introduced for APP; by default it is true. */
114
- showAltUserInfo?: boolean
115
- /** Socket for showing the services trigger up front; introduced for APP; by default it is displayed. */
116
- showServices?: boolean
117
- /** Socket for showing the main Links up front in case of APP. by default they are folded into the hamburger menu. */
118
- showMainLinks?: boolean
119
- }
120
-
121
- export type LoggedOutHeaderProps = HeaderProps & {
122
128
  /** Socket for create account prompt on the right side. */
123
129
  createAccount?: HeaderCreateAccountData | JSX.Element
124
130
  isSticky?: boolean
@@ -127,5 +133,5 @@ export type LoggedOutHeaderProps = HeaderProps & {
127
133
  /** Socket for links in the middle-right section. */
128
134
  mainLinks?: HeaderLinkData[] | JSX.Element
129
135
  /** Socket for sign in button on the right side. */
130
- signIn?: HeaderSignInData | JSX.Element
136
+ signIn?: HeaderButtonData | JSX.Element
131
137
  }