Update to new design for avatars: individual/group icons/colors
And two initials.pull/27/head^2
parent
cf16ced91c
commit
8f3e3b7aaf
@ -0,0 +1,22 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="28px" height="28px" viewBox="0 0 28 28" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<!-- Generator: Sketch 51.3 (57544) - http://www.bohemiancoding.com/sketch -->
|
||||
<title>Group/group-28</title>
|
||||
<desc>Created with Sketch.</desc>
|
||||
<defs>
|
||||
<path d="M18.7272727,13.2857143 C20.6890909,13.2857143 22.2609091,11.6585714 22.2609091,9.64285714 C22.2609091,7.62714286 20.6890909,6 18.7272727,6 C16.7654545,6 15.1818182,7.62714286 15.1818182,9.64285714 C15.1818182,11.6585714 16.7654545,13.2857143 18.7272727,13.2857143 Z M9.27272727,13.2857143 C11.2345455,13.2857143 12.8063636,11.6585714 12.8063636,9.64285714 C12.8063636,7.62714286 11.2345455,6 9.27272727,6 C7.31090909,6 5.72727273,7.62714286 5.72727273,9.64285714 C5.72727273,11.6585714 7.31090909,13.2857143 9.27272727,13.2857143 Z M9.27272727,15.7142857 C6.51909091,15.7142857 1,17.135 1,19.9642857 L1,23 L17.5454545,23 L17.5454545,19.9642857 C17.5454545,17.135 12.0263636,15.7142857 9.27272727,15.7142857 Z M18.7272727,15.7142857 C18.3845455,15.7142857 17.9945455,15.7385714 17.5809091,15.775 C18.9518182,16.795 19.9090909,18.1671429 19.9090909,19.9642857 L19.9090909,23 L27,23 L27,19.9642857 C27,17.135 21.4809091,15.7142857 18.7272727,15.7142857 Z" id="path-1"></path>
|
||||
<rect id="path-3" x="0" y="0" width="28" height="28"></rect>
|
||||
</defs>
|
||||
<g id="Group/group-28" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||
<mask id="mask-2" fill="white">
|
||||
<use xlink:href="#path-1"></use>
|
||||
</mask>
|
||||
<use id="Shape" fill="#000000" fill-rule="nonzero" xlink:href="#path-1"></use>
|
||||
<g id="Color/UI/Black" mask="url(#mask-2)">
|
||||
<mask id="mask-4" fill="white">
|
||||
<use xlink:href="#path-3"></use>
|
||||
</mask>
|
||||
<use id="fill" fill="#000000" fill-rule="evenodd" xlink:href="#path-3"></use>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 1.9 KiB |
@ -0,0 +1,22 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="28px" height="28px" viewBox="0 0 28 28" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<!-- Generator: Sketch 51.3 (57544) - http://www.bohemiancoding.com/sketch -->
|
||||
<title>Profile/profile-28</title>
|
||||
<desc>Created with Sketch.</desc>
|
||||
<defs>
|
||||
<path d="M14,14 C16.7625,14 19,11.7625 19,9 C19,6.2375 16.7625,4 14,4 C11.2375,4 9,6.2375 9,9 C9,11.7625 11.2375,14 14,14 Z M14,16.5 C10.6625,16.5 4,18.175 4,21.5 L4,24 L24,24 L24,21.5 C24,18.175 17.3375,16.5 14,16.5 Z" id="path-1"></path>
|
||||
<rect id="path-3" x="0" y="0" width="28" height="28"></rect>
|
||||
</defs>
|
||||
<g id="Profile/profile-28" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||
<mask id="mask-2" fill="white">
|
||||
<use xlink:href="#path-1"></use>
|
||||
</mask>
|
||||
<use id="Shape" fill="#000000" fill-rule="nonzero" xlink:href="#path-1"></use>
|
||||
<g id="Color/UI/Black" mask="url(#mask-2)">
|
||||
<mask id="mask-4" fill="white">
|
||||
<use xlink:href="#path-3"></use>
|
||||
</mask>
|
||||
<use id="fill" fill="#000000" fill-rule="evenodd" xlink:href="#path-3"></use>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 1.2 KiB |
@ -0,0 +1,299 @@
|
||||
### With avatar
|
||||
|
||||
```jsx
|
||||
<Avatar
|
||||
size={28}
|
||||
color="pink"
|
||||
name="John Smith"
|
||||
avatarPath={util.gifObjectUrl}
|
||||
conversationType="direct"
|
||||
i18n={util.i18n}
|
||||
/>
|
||||
<Avatar
|
||||
size={28}
|
||||
color="pink"
|
||||
name="Puppies"
|
||||
avatarPath={util.gifObjectUrl}
|
||||
conversationType="group"
|
||||
i18n={util.i18n}
|
||||
/>
|
||||
```
|
||||
|
||||
### With only name
|
||||
|
||||
```jsx
|
||||
<Avatar
|
||||
size={28}
|
||||
color="blue"
|
||||
name="John"
|
||||
conversationType="direct"
|
||||
i18n={util.i18n}
|
||||
/>
|
||||
<Avatar
|
||||
size={28}
|
||||
color="green"
|
||||
name="John Smith"
|
||||
conversationType="direct"
|
||||
i18n={util.i18n}
|
||||
/>
|
||||
<Avatar
|
||||
size={28}
|
||||
color="red"
|
||||
name="Puppies"
|
||||
conversationType="group"
|
||||
i18n={util.i18n}
|
||||
/>
|
||||
```
|
||||
|
||||
### Just phone number
|
||||
|
||||
```jsx
|
||||
<Avatar
|
||||
size={28}
|
||||
color="pink"
|
||||
phoneNumber="(555) 353-3433"
|
||||
conversationType="direct"
|
||||
i18n={util.i18n}
|
||||
/>
|
||||
```
|
||||
|
||||
### All colors
|
||||
|
||||
```jsx
|
||||
<Avatar
|
||||
size={28}
|
||||
color="red"
|
||||
name="Red"
|
||||
conversationType="direct"
|
||||
i18n={util.i18n}
|
||||
/>
|
||||
<Avatar
|
||||
size={28}
|
||||
color="deep_orange"
|
||||
name="Deep Orange"
|
||||
conversationType="direct"
|
||||
i18n={util.i18n}
|
||||
/>
|
||||
<Avatar
|
||||
size={28}
|
||||
color="brown"
|
||||
name="Broen"
|
||||
conversationType="direct"
|
||||
i18n={util.i18n}
|
||||
/>
|
||||
<Avatar
|
||||
size={28}
|
||||
color="pink"
|
||||
name="Pink"
|
||||
conversationType="direct"
|
||||
i18n={util.i18n}
|
||||
/>
|
||||
<Avatar
|
||||
size={28}
|
||||
color="purple"
|
||||
name="Purple"
|
||||
conversationType="direct"
|
||||
i18n={util.i18n}
|
||||
/>
|
||||
<Avatar
|
||||
size={28}
|
||||
color="indigo"
|
||||
name="Indigo"
|
||||
conversationType="direct"
|
||||
i18n={util.i18n}
|
||||
/>
|
||||
<Avatar
|
||||
size={28}
|
||||
color="blue"
|
||||
name="Blue"
|
||||
conversationType="direct"
|
||||
i18n={util.i18n}
|
||||
/>
|
||||
<Avatar
|
||||
size={28}
|
||||
color="teal"
|
||||
name="Teal"
|
||||
conversationType="direct"
|
||||
i18n={util.i18n}
|
||||
/>
|
||||
<Avatar
|
||||
size={28}
|
||||
color="green"
|
||||
name="Green"
|
||||
conversationType="direct"
|
||||
i18n={util.i18n}
|
||||
/>
|
||||
<Avatar
|
||||
size={28}
|
||||
color="light_green"
|
||||
name="Light Green"
|
||||
conversationType="direct"
|
||||
i18n={util.i18n}
|
||||
/>
|
||||
<Avatar
|
||||
size={28}
|
||||
color="blue_grey"
|
||||
name="Blue Grey"
|
||||
conversationType="direct"
|
||||
i18n={util.i18n}
|
||||
/>
|
||||
```
|
||||
|
||||
### 36px
|
||||
|
||||
```jsx
|
||||
<Avatar
|
||||
size={36}
|
||||
color="teal"
|
||||
name="John Smith"
|
||||
avatarPath={util.gifObjectUrl}
|
||||
conversationType="direct"
|
||||
i18n={util.i18n}
|
||||
/>
|
||||
<Avatar
|
||||
size={36}
|
||||
color="teal"
|
||||
name="John"
|
||||
conversationType="direct"
|
||||
i18n={util.i18n}
|
||||
/>
|
||||
<Avatar
|
||||
size={36}
|
||||
color="teal"
|
||||
name="John Smith"
|
||||
conversationType="direct"
|
||||
i18n={util.i18n}
|
||||
/>
|
||||
<Avatar
|
||||
size={36}
|
||||
color="teal"
|
||||
conversationType="direct"
|
||||
i18n={util.i18n}
|
||||
/>
|
||||
<Avatar
|
||||
size={36}
|
||||
color="teal"
|
||||
name="Pupplies"
|
||||
conversationType="group"
|
||||
i18n={util.i18n}
|
||||
/>
|
||||
```
|
||||
|
||||
### 48px
|
||||
|
||||
```jsx
|
||||
<Avatar
|
||||
size={48}
|
||||
color="teal"
|
||||
name="John Smith"
|
||||
avatarPath={util.gifObjectUrl}
|
||||
conversationType="direct"
|
||||
i18n={util.i18n}
|
||||
/>
|
||||
<Avatar
|
||||
size={48}
|
||||
color="teal"
|
||||
name="John"
|
||||
conversationType="direct"
|
||||
i18n={util.i18n}
|
||||
/>
|
||||
<Avatar
|
||||
size={48}
|
||||
color="teal"
|
||||
name="John Smith"
|
||||
conversationType="direct"
|
||||
i18n={util.i18n}
|
||||
/>
|
||||
<Avatar
|
||||
size={48}
|
||||
color="teal"
|
||||
conversationType="direct"
|
||||
i18n={util.i18n}
|
||||
/>
|
||||
<Avatar
|
||||
size={48}
|
||||
color="teal"
|
||||
name="Pupplies"
|
||||
conversationType="group"
|
||||
i18n={util.i18n}
|
||||
/>
|
||||
```
|
||||
|
||||
### 80px
|
||||
|
||||
```jsx
|
||||
<Avatar
|
||||
size={80}
|
||||
color="teal"
|
||||
name="John Smith"
|
||||
avatarPath={util.gifObjectUrl}
|
||||
conversationType="direct"
|
||||
i18n={util.i18n}
|
||||
/>
|
||||
<Avatar
|
||||
size={80}
|
||||
color="teal"
|
||||
name="John"
|
||||
conversationType="direct"
|
||||
i18n={util.i18n}
|
||||
/>
|
||||
<Avatar
|
||||
size={80}
|
||||
color="teal"
|
||||
name="John Smith"
|
||||
conversationType="direct"
|
||||
i18n={util.i18n}
|
||||
/>
|
||||
<Avatar
|
||||
size={80}
|
||||
color="teal"
|
||||
conversationType="direct"
|
||||
i18n={util.i18n}
|
||||
/>
|
||||
<Avatar
|
||||
size={80}
|
||||
color="teal"
|
||||
name="Pupplies"
|
||||
conversationType="group"
|
||||
i18n={util.i18n}
|
||||
/>
|
||||
```
|
||||
|
||||
### Broken color
|
||||
|
||||
```jsx
|
||||
<Avatar
|
||||
size={28}
|
||||
color="fake"
|
||||
name="F"
|
||||
conversationType="direct"
|
||||
i18n={util.i18n}
|
||||
/>
|
||||
```
|
||||
|
||||
### Broken image
|
||||
|
||||
```jsx
|
||||
<Avatar
|
||||
size={28}
|
||||
color="pink"
|
||||
name="John Smith"
|
||||
avatarPath="nonexistent"
|
||||
conversationType="direct"
|
||||
i18n={util.i18n}
|
||||
/>
|
||||
```
|
||||
|
||||
### Broken image for group
|
||||
|
||||
```jsx
|
||||
<Avatar
|
||||
size={28}
|
||||
avatarPath="nonexistent"
|
||||
color="pink"
|
||||
name="Puppies"
|
||||
avatarPath="nonexistent"
|
||||
conversationType="group"
|
||||
i18n={util.i18n}
|
||||
/>
|
||||
```
|
@ -0,0 +1,118 @@
|
||||
import React from 'react';
|
||||
import classNames from 'classnames';
|
||||
|
||||
import { getInitials } from '../util/getInitials';
|
||||
import { Localizer } from '../types/Util';
|
||||
|
||||
interface Props {
|
||||
avatarPath?: string;
|
||||
color?: string;
|
||||
conversationType: 'group' | 'direct';
|
||||
i18n: Localizer;
|
||||
name?: string;
|
||||
phoneNumber?: string;
|
||||
profileName?: string;
|
||||
size: number;
|
||||
}
|
||||
|
||||
interface State {
|
||||
imageBroken: boolean;
|
||||
}
|
||||
|
||||
export class Avatar extends React.Component<Props, State> {
|
||||
public handleImageErrorBound: () => void;
|
||||
|
||||
public constructor(props: Props) {
|
||||
super(props);
|
||||
|
||||
this.handleImageErrorBound = this.handleImageError.bind(this);
|
||||
|
||||
this.state = {
|
||||
imageBroken: false,
|
||||
};
|
||||
}
|
||||
|
||||
public handleImageError() {
|
||||
// tslint:disable-next-line no-console
|
||||
console.log('Avatar: Image failed to load; failing over to placeholder');
|
||||
this.setState({
|
||||
imageBroken: true,
|
||||
});
|
||||
}
|
||||
|
||||
public renderImage() {
|
||||
const { avatarPath, i18n, name, phoneNumber, profileName } = this.props;
|
||||
const { imageBroken } = this.state;
|
||||
const hasImage = avatarPath && !imageBroken;
|
||||
|
||||
if (!hasImage) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const title = `${name || phoneNumber}${
|
||||
!name && profileName ? ` ~${profileName}` : ''
|
||||
}`;
|
||||
|
||||
return (
|
||||
<img
|
||||
onError={this.handleImageErrorBound}
|
||||
alt={i18n('contactAvatarAlt', [title])}
|
||||
src={avatarPath}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
public renderNoImage() {
|
||||
const { conversationType, name, size } = this.props;
|
||||
|
||||
const initials = getInitials(name);
|
||||
const isGroup = conversationType === 'group';
|
||||
|
||||
if (!isGroup && initials) {
|
||||
return (
|
||||
<div
|
||||
className={classNames(
|
||||
'module-avatar__label',
|
||||
`module-avatar__label--${size}`
|
||||
)}
|
||||
>
|
||||
{initials}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
className={classNames(
|
||||
'module-avatar__icon',
|
||||
`module-avatar__icon--${conversationType}`,
|
||||
`module-avatar__icon--${size}`
|
||||
)}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
public render() {
|
||||
const { avatarPath, color, size } = this.props;
|
||||
const { imageBroken } = this.state;
|
||||
|
||||
const hasImage = avatarPath && !imageBroken;
|
||||
|
||||
if (size !== 28 && size !== 36 && size !== 48 && size !== 80) {
|
||||
throw new Error(`Size ${size} is not supported!`);
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
className={classNames(
|
||||
'module-avatar',
|
||||
`module-avatar--${size}`,
|
||||
hasImage ? 'module-avatar--with-image' : 'module-avatar--no-image',
|
||||
!hasImage ? `module-avatar--${color}` : null
|
||||
)}
|
||||
>
|
||||
{hasImage ? this.renderImage() : this.renderNoImage()}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
@ -0,0 +1,25 @@
|
||||
import React from 'react';
|
||||
import classNames from 'classnames';
|
||||
|
||||
interface Props {
|
||||
/**
|
||||
* Corresponds to the theme setting in the app, and the class added to the root element.
|
||||
*/
|
||||
theme: 'light-theme' | 'dark-theme';
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides the parent elements necessary to allow the main Signal Desktop stylesheet to
|
||||
* apply (with no changes) to messages in the Style Guide.
|
||||
*/
|
||||
export class LeftPaneContext extends React.Component<Props> {
|
||||
public render() {
|
||||
const { theme } = this.props;
|
||||
|
||||
return (
|
||||
<div className={classNames(theme || 'light-theme')}>
|
||||
<div className="gutter">{this.props.children}</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
@ -0,0 +1,21 @@
|
||||
const BAD_CHARACTERS = /[^A-Za-z\s]+/g;
|
||||
const WHITESPACE = /\s+/g;
|
||||
|
||||
function removeNonInitials(name: string) {
|
||||
return name.replace(BAD_CHARACTERS, '').replace(WHITESPACE, ' ');
|
||||
}
|
||||
|
||||
export function getInitials(name?: string): string | null {
|
||||
if (!name) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const cleaned = removeNonInitials(name);
|
||||
const parts = cleaned.split(' ');
|
||||
const initials = parts.map(part => part.trim()[0]);
|
||||
if (!initials.length) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return initials.slice(0, 2).join('');
|
||||
}
|
Loading…
Reference in New Issue