Tweak avatar badge sizing/placement
This commit is contained in:
parent
829604dbcf
commit
d8b230e348
2 changed files with 79 additions and 32 deletions
|
@ -45,7 +45,7 @@ export enum AvatarSize {
|
||||||
ONE_HUNDRED_TWELVE = 112,
|
ONE_HUNDRED_TWELVE = 112,
|
||||||
}
|
}
|
||||||
|
|
||||||
type BadgePlacementType = { size: number; bottom: number; right: number };
|
type BadgePlacementType = { bottom: number; right: number };
|
||||||
|
|
||||||
export type Props = {
|
export type Props = {
|
||||||
avatarPath?: string;
|
avatarPath?: string;
|
||||||
|
@ -78,14 +78,16 @@ export type Props = {
|
||||||
} & Pick<React.HTMLProps<HTMLDivElement>, 'className'>;
|
} & Pick<React.HTMLProps<HTMLDivElement>, 'className'>;
|
||||||
|
|
||||||
const BADGE_PLACEMENT_BY_SIZE = new Map<number, BadgePlacementType>([
|
const BADGE_PLACEMENT_BY_SIZE = new Map<number, BadgePlacementType>([
|
||||||
[28, { size: 16, bottom: -4, right: -2 }],
|
[28, { bottom: -4, right: -2 }],
|
||||||
[32, { size: 16, bottom: -4, right: -2 }],
|
[32, { bottom: -4, right: -2 }],
|
||||||
[36, { size: 16, bottom: -3, right: 0 }],
|
[36, { bottom: -3, right: 0 }],
|
||||||
[40, { size: 24, bottom: -6, right: -4 }],
|
[40, { bottom: -6, right: -4 }],
|
||||||
[48, { size: 24, bottom: -6, right: -4 }],
|
[48, { bottom: -6, right: -4 }],
|
||||||
[56, { size: 24, bottom: -6, right: 0 }],
|
[56, { bottom: -6, right: 0 }],
|
||||||
[80, { size: 36, bottom: -8, right: 0 }],
|
[64, { bottom: -6, right: 0 }],
|
||||||
[88, { size: 36, bottom: -4, right: 3 }],
|
[80, { bottom: -8, right: 0 }],
|
||||||
|
[88, { bottom: -4, right: 3 }],
|
||||||
|
[112, { bottom: -4, right: 3 }],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const getDefaultBlur = (
|
const getDefaultBlur = (
|
||||||
|
@ -241,10 +243,12 @@ export const Avatar: FunctionComponent<Props> = ({
|
||||||
}
|
}
|
||||||
|
|
||||||
let badgeNode: ReactNode;
|
let badgeNode: ReactNode;
|
||||||
|
const badgeSize = _getBadgeSize(size);
|
||||||
if (
|
if (
|
||||||
badge &&
|
badge &&
|
||||||
theme &&
|
theme &&
|
||||||
!noteToSelf &&
|
!noteToSelf &&
|
||||||
|
badgeSize &&
|
||||||
isBadgeVisible(badge) &&
|
isBadgeVisible(badge) &&
|
||||||
shouldShowBadges()
|
shouldShowBadges()
|
||||||
) {
|
) {
|
||||||
|
@ -253,15 +257,14 @@ export const Avatar: FunctionComponent<Props> = ({
|
||||||
theme === ThemeType.light ? BadgeImageTheme.Light : BadgeImageTheme.Dark;
|
theme === ThemeType.light ? BadgeImageTheme.Light : BadgeImageTheme.Dark;
|
||||||
const badgeImagePath = getBadgeImageFileLocalPath(
|
const badgeImagePath = getBadgeImageFileLocalPath(
|
||||||
badge,
|
badge,
|
||||||
badgePlacement.size,
|
badgeSize,
|
||||||
badgeTheme
|
badgeTheme
|
||||||
);
|
);
|
||||||
if (badgeImagePath) {
|
if (badgeImagePath) {
|
||||||
const positionStyles: CSSProperties = {
|
const positionStyles: CSSProperties = {
|
||||||
width: badgePlacement.size,
|
width: badgeSize,
|
||||||
height: badgePlacement.size,
|
height: badgeSize,
|
||||||
bottom: badgePlacement.bottom,
|
...badgePlacement,
|
||||||
right: badgePlacement.right,
|
|
||||||
};
|
};
|
||||||
if (onClickBadge) {
|
if (onClickBadge) {
|
||||||
badgeNode = (
|
badgeNode = (
|
||||||
|
@ -312,15 +315,26 @@ export const Avatar: FunctionComponent<Props> = ({
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// This is only exported for testing.
|
||||||
|
export function _getBadgeSize(avatarSize: number): undefined | number {
|
||||||
|
if (avatarSize < 24) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
if (avatarSize <= 36) {
|
||||||
|
return 16;
|
||||||
|
}
|
||||||
|
if (avatarSize <= 64) {
|
||||||
|
return 24;
|
||||||
|
}
|
||||||
|
if (avatarSize <= 112) {
|
||||||
|
return 36;
|
||||||
|
}
|
||||||
|
return Math.round(avatarSize * 0.4);
|
||||||
|
}
|
||||||
|
|
||||||
// This is only exported for testing.
|
// This is only exported for testing.
|
||||||
export function _getBadgePlacement(
|
export function _getBadgePlacement(
|
||||||
avatarSize: number
|
avatarSize: number
|
||||||
): Readonly<BadgePlacementType> {
|
): Readonly<BadgePlacementType> {
|
||||||
return (
|
return BADGE_PLACEMENT_BY_SIZE.get(avatarSize) || { bottom: 0, right: 0 };
|
||||||
BADGE_PLACEMENT_BY_SIZE.get(avatarSize) || {
|
|
||||||
size: Math.ceil(avatarSize * 0.425),
|
|
||||||
bottom: 0,
|
|
||||||
right: 0,
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,9 +3,40 @@
|
||||||
|
|
||||||
import { assert } from 'chai';
|
import { assert } from 'chai';
|
||||||
|
|
||||||
import { _getBadgePlacement } from '../../components/Avatar';
|
import { _getBadgeSize, _getBadgePlacement } from '../../components/Avatar';
|
||||||
|
|
||||||
describe('<Avatar>', () => {
|
describe('<Avatar>', () => {
|
||||||
|
describe('_getBadgeSize', () => {
|
||||||
|
it('returns undefined for sizes under 24px', () => {
|
||||||
|
assert.isUndefined(_getBadgeSize(1));
|
||||||
|
assert.isUndefined(_getBadgeSize(23));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns 16px for sizes between 24px–36px', () => {
|
||||||
|
assert.strictEqual(_getBadgeSize(24), 16);
|
||||||
|
assert.strictEqual(_getBadgeSize(30), 16);
|
||||||
|
assert.strictEqual(_getBadgeSize(36), 16);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns 24px for sizes between 37px–64px', () => {
|
||||||
|
assert.strictEqual(_getBadgeSize(37), 24);
|
||||||
|
assert.strictEqual(_getBadgeSize(50), 24);
|
||||||
|
assert.strictEqual(_getBadgeSize(64), 24);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns 36px for sizes between 65px–112px', () => {
|
||||||
|
assert.strictEqual(_getBadgeSize(65), 36);
|
||||||
|
assert.strictEqual(_getBadgeSize(100), 36);
|
||||||
|
assert.strictEqual(_getBadgeSize(112), 36);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns ~40% of the size for sizes above 112px (a fallback)', () => {
|
||||||
|
assert.strictEqual(_getBadgeSize(113), 45);
|
||||||
|
assert.strictEqual(_getBadgeSize(200), 80);
|
||||||
|
assert.strictEqual(_getBadgeSize(999), 400);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe('_getBadgePlacement', () => {
|
describe('_getBadgePlacement', () => {
|
||||||
const check = (
|
const check = (
|
||||||
testCases: Map<number, ReturnType<typeof _getBadgePlacement>>
|
testCases: Map<number, ReturnType<typeof _getBadgePlacement>>
|
||||||
|
@ -22,21 +53,23 @@ describe('<Avatar>', () => {
|
||||||
|
|
||||||
it('returns values as specified by designs', () => {
|
it('returns values as specified by designs', () => {
|
||||||
const testCases = new Map([
|
const testCases = new Map([
|
||||||
[28, { size: 16, bottom: -4, right: -2 }],
|
[28, { bottom: -4, right: -2 }],
|
||||||
[36, { size: 16, bottom: -3, right: 0 }],
|
[36, { bottom: -3, right: 0 }],
|
||||||
[40, { size: 24, bottom: -6, right: -4 }],
|
[40, { bottom: -6, right: -4 }],
|
||||||
[48, { size: 24, bottom: -6, right: -4 }],
|
[48, { bottom: -6, right: -4 }],
|
||||||
[56, { size: 24, bottom: -6, right: 0 }],
|
[56, { bottom: -6, right: 0 }],
|
||||||
[80, { size: 36, bottom: -8, right: 0 }],
|
[64, { bottom: -6, right: 0 }],
|
||||||
[88, { size: 36, bottom: -4, right: 3 }],
|
[80, { bottom: -8, right: 0 }],
|
||||||
|
[88, { bottom: -4, right: 3 }],
|
||||||
|
[112, { bottom: -4, right: 3 }],
|
||||||
]);
|
]);
|
||||||
check(testCases);
|
check(testCases);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('returns fallback values for sizes not specified by designs', () => {
|
it('returns 0, 0 values for sizes not specified by designs', () => {
|
||||||
const testCases = new Map([
|
const testCases = new Map([
|
||||||
[16, { size: 7, bottom: 0, right: 0 }],
|
[16, { bottom: 0, right: 0 }],
|
||||||
[200, { size: 85, bottom: 0, right: 0 }],
|
[200, { bottom: 0, right: 0 }],
|
||||||
]);
|
]);
|
||||||
check(testCases);
|
check(testCases);
|
||||||
});
|
});
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue