gristlabs_grist-core/app/client/ui/UserImage.ts
Jarosław Sadziński 2a978c4313 (core) Change default team logo
Summary:
Also fixes a bug that prevented drag and drop from working
in BillingLogoEditor, and tweaks the hover state of AppHeader
to only activate when the cursor is over the dropdown menu, and
not the logo.

Test Plan:
Manual.
1. Wide logo tests with url http://localhost:8080/o/docs?__themeOrg=fieldlink
2. Avatar on home screen for personal site
3. Avatar on doc screen for personal site
4. Avatar on doc screen with hidden left drawer on doc page
5. Same tests but with personal image
6. Same tests but with custom logo uploaded
7. All above tests but with dark theme
8. All above but on mobile.

Reviewers: georgegevoian

Reviewed By: georgegevoian

Differential Revision: https://phab.getgrist.com/D4378
2024-10-11 15:40:04 +02:00

157 lines
3.8 KiB
TypeScript

import {colors, theme} from 'app/client/ui2018/cssVars';
import {UserProfile} from 'app/common/LoginSessionAPI';
import {dom, DomElementArg, styled} from 'grainjs';
import {icon} from 'app/client/ui2018/icons';
export type Size = 'small' | 'medium' | 'large';
/**
* Returns a DOM element showing a circular icon with a user's picture, or the user's initials if
* picture is missing. Also varies the color of the circle when using initials.
*/
export function createUserImage(
user: Partial<UserProfile>|'exampleUser'|null, size: Size, ...args: DomElementArg[]
): HTMLElement {
return cssUserImage(
cssUserImage.cls('-' + size),
...(function*() {
if (user === 'exampleUser') {
yield [cssUserImage.cls('-example'), cssExampleUserIcon('EyeShow')];
} else if (!user || user.anonymous) {
yield cssUserImage.cls('-anon');
} else {
if (user.picture) {
yield cssUserPicture({src: user.picture}, dom.on('error', (ev, el) => dom.hideElem(el, true)));
}
yield dom.style('background-color', pickColor(user));
const initials = getInitials(user);
if (initials.length > 1) {
yield cssUserImage.cls('-reduced');
}
yield initials;
}
})(),
...args,
);
}
/**
* Extracts initials from a user, e.g. a FullUser. E.g. "Foo Bar" is turned into "FB", and
* "foo@example.com" into just "f".
*
* Exported for testing.
*/
export function getInitials(user: Partial<UserProfile>) {
const source = (user.name && user.name.trim()) || (user.email && user.email.trim()) || '';
return source.split(/\s+/, 2).map(p => p.slice(0, 1)).join('');
}
/**
* Hashes the username to return a color.
*/
function pickColor(user: Partial<UserProfile>): string {
let c = hashCode(user.name + ':' + user.email) % someColors.length;
if (c < 0) { c += someColors.length; }
return someColors[c];
}
/**
* Hash a string into an integer. From https://stackoverflow.com/a/7616484/328565.
*/
function hashCode(str: string): number {
let hash: number = 0;
for (let i = 0; i < str.length; i++) {
// tslint:disable-next-line:no-bitwise
hash = ((hash << 5) - hash + str.charCodeAt(i)) | 0;
}
return hash;
}
// These mostly come from https://clrs.cc/
const someColors = [
'#0B437D',
'#0074D9',
'#7FDBFF',
'#39CCCC',
'#16DD6D',
'#2ECC40',
'#16B378',
'#EFCC00',
'#FF851B',
'#FF4136',
'#85144b',
'#F012BE',
'#B10DC9',
];
export const cssUserImage = styled('div', `
position: relative;
text-align: center;
text-transform: uppercase;
user-select: none;
-moz-user-select: none;
color: white;
border-radius: 100px;
display: flex;
align-items: center;
justify-content: center;
--border-size: 0px;
width: calc(var(--icon-size, 24px) - var(--border-size));
height: calc(var(--icon-size, 24px) - var(--border-size));
line-height: 1em;
&-small {
--icon-size: 24px;
font-size: 13.5px;
--reduced-font-size: 12px;
}
&-medium {
--icon-size: 32px;
font-size: 18px;
--reduced-font-size: 16px;
}
&-border {
--border-size: 2px;
}
&-large {
--icon-size: 40px;
font-size: 22.5px;
--reduced-font-size: 20px;
}
&-anon {
border: 1px solid ${colors.slate};
color: ${colors.slate};
}
&-anon::before {
content: "?"
}
&-reduced {
font-size: var(--reduced-font-size);
}
&-square {
border-radius: 0px;
}
&-example {
background-color: ${colors.slate};
border: 1px solid ${colors.slate};
}
`);
const cssUserPicture = styled('img', `
position: absolute;
width: 100%;
height: 100%;
object-fit: cover;
background-color: ${theme.menuBg};
border-radius: inherit;
box-sizing: content-box; /* keep the border outside of the size of the image */
`);
const cssExampleUserIcon = styled(icon, `
background-color: white;
width: 45px;
height: 45px;
transform: scaleY(0.75);
`);