Skip to content

Commit

Permalink
fix(Avatar): removed unnecessary code
Browse files Browse the repository at this point in the history
refactor(AvatarGroup): pulled out of Avatar
fix(Avatar.Icon): added to allow customization of icon
docs(avatar): updated examples
  • Loading branch information
Craig Howell authored and Craig Howell committed Oct 3, 2022
1 parent d5c4ae6 commit cad3071
Show file tree
Hide file tree
Showing 11 changed files with 422 additions and 95 deletions.
112 changes: 112 additions & 0 deletions src/lib/components/avatar-group/Avatar.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
<script lang="ts" context="module">
export const AVATAR_GROUP_AVATAR_CONTEXT_ID = 'avatar-group-avatar-context-id';
</script>

<script lang="ts">
import { AVATAR_GROUP_CONTEXT_ID } from './AvatarGroup.svelte';
import { useContext } from '../../utils/useContext';
import { browser } from '$app/environment';
import { setContext, getContext } from 'svelte/internal';
import { twMerge } from 'tailwind-merge';
import Placeholder from './Placeholder.svelte';
export let src: string | undefined = undefined;
export let alt = 'avatar';
export let initials: string | undefined = undefined;
let img: HTMLImageElement;
function handleError(e: Event) {
// if (withPlaceholder) {
img.remove();
// }
}
useContext({
context_id: AVATAR_GROUP_CONTEXT_ID,
parent: 'AvatarGroup',
component: 'AvatarGroup.Avatar'
});
const {
shape,
size
}: { shape: 'circle' | 'rounded' | 'square'; size: 'xs' | 'sm' | 'md' | 'lg' | 'xl' } =
getContext(AVATAR_GROUP_CONTEXT_ID);
setContext(AVATAR_GROUP_AVATAR_CONTEXT_ID, {
avatar: true,
src,
alt,
shape,
size
});
let defaultClass = '';
let containerDefaultClass = '';
if (src) {
defaultClass =
'inline-block absolute ring-2 ring-light-surface dark:ring-dark-surface transition-all duration-150';
containerDefaultClass = 'inline-block relative align-middle';
} else if (initials) {
defaultClass =
'inline-flex items-center justify-center align-middle transition-all duration-150 bg-light-icon-background dark:bg-dark-icon-background text-light-content dark:text-dark-content ring-2 ring-light-surface dark:ring-dark-surface transition-all duration-150';
}
if (size === 'xs') {
defaultClass += ' h-6 w-6';
containerDefaultClass += ' h-6 w-6';
} else if (size === 'sm') {
defaultClass += ' h-8 w-8';
containerDefaultClass += ' h-8 w-8';
} else if (size === 'md') {
defaultClass += ' h-10 w-10';
containerDefaultClass += ' h-10 w-10';
} else if (size === 'lg') {
defaultClass += ' h-12 w-12';
containerDefaultClass += ' h-12 w-12';
} else if (size === 'xl') {
defaultClass += ' h-16 w-16';
containerDefaultClass += ' h-16 w-16';
}
if (shape === 'circle') {
defaultClass += ' rounded-full';
containerDefaultClass += ' rounded-full';
} else if (shape === 'rounded') {
defaultClass += ' rounded-md';
containerDefaultClass += ' rounded-md';
}
$: finalClass = twMerge(defaultClass, $$props.class);
$: finalContainerClass = twMerge(containerDefaultClass, $$props.class);
</script>

{#if src}
<span class={finalContainerClass} style={$$props.style}>
{#if $$slots.placeholder}
<slot name="placeholder" />
{:else}
<Placeholder />
{/if}

<img
bind:this={img}
class={finalClass}
style={$$props.style}
src={(browser && src) || ''}
{alt}
on:error={handleError}
/>

<slot name="indicator" />
</span>
{:else if initials}
<span class={finalClass} style={$$props.style}>
<span
class="font-bold leading-none text-current"
class:text-xs={size === 'xs'}
class:text-sm={size === 'sm'}
class:text-xl={size === 'lg'}
class:text-2xl={size === 'xl'}>{initials}</span
>
</span>
{/if}
42 changes: 23 additions & 19 deletions src/lib/components/avatar-group/AvatarGroup.svelte
Original file line number Diff line number Diff line change
@@ -1,27 +1,31 @@
<script lang="ts" context="module">
export const AVATAR_GROUP_CONTEXT_ID = 'avatar-group-context-id';
</script>

<script lang="ts">
import Avatar from './Avatar.svelte';
import { setContext } from 'svelte/internal';
import { twMerge } from 'tailwind-merge';
export let group: string[] = [];
export let shape: 'circle' | 'rounded' | 'square' = 'circle';
export let size: 'xs' | 'sm' | 'md' | 'lg' | 'xl' = 'md';
// TODO: pull out and rename AvatarGroup with AvatarGroup.Avatar
setContext(AVATAR_GROUP_CONTEXT_ID, {
avatarGroup: true,
shape,
size
});
let defaultClass = '';
if (size === 'xs') {
defaultClass = 'flex overflow-hidden p-0.5 -space-x-1';
} else if (size === 'sm' || size === 'md') {
defaultClass = 'flex overflow-hidden p-0.5 -space-x-2';
} else if (size === 'lg' || size === 'xl') {
defaultClass = 'flex overflow-hidden p-0.5 -space-x-3';
}
$: finalClass = twMerge(defaultClass, $$props.class);
</script>

<div
class="flex overflow-hidden"
class:h-[1.65rem]={size === 'xs'}
class:-space-x-1={size === 'xs'}
class:-space-x-2={size === 'sm' || size === 'md'}
class:-space-x-3={size === 'lg' || size === 'xl'}
>
<!-- TODO: <slot /> -->
<!-- {#each group as item}
<Avatar
src={item}
{size}
{shape}
class="ring-2 ring-light-surface dark:ring-dark-surface transition-all duration-150"
/>
{/each} -->
<div class={finalClass} style={$$props.style}>
<slot />
</div>
50 changes: 50 additions & 0 deletions src/lib/components/avatar-group/Icon.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
<script lang="ts">
import { twMerge } from 'tailwind-merge';
import { AVATAR_GROUP_CONTEXT_ID } from './AvatarGroup.svelte';
import { AVATAR_GROUP_AVATAR_CONTEXT_ID } from './Avatar.svelte';
import { AVATAR_GROUP_AVATAR_PLACEHOLDER_CONTEXT_ID } from './Placeholder.svelte';
import { getContext } from 'svelte/internal';
import { useContext } from '../../utils/useContext';
import type MaterialIcons from '../../types/material-icons';
export let icon: MaterialIcons = 'person';
useContext({
context_id: AVATAR_GROUP_CONTEXT_ID,
parent: 'AvatarGroup',
component: 'AvatarGroup.Avatar.Placeholder.Icon'
});
useContext({
context_id: AVATAR_GROUP_AVATAR_CONTEXT_ID,
parent: 'AvatarGroup.Avatar',
component: 'AvatarGroup.Avatar.Placeholder.Icon'
});
useContext({
context_id: AVATAR_GROUP_AVATAR_PLACEHOLDER_CONTEXT_ID,
parent: 'AvatarGroup.Avatar.Placeholder',
component: 'AvatarGroup.Avatar.Placeholder.Icon'
});
const { size }: { size: 'xs' | 'sm' | 'md' | 'lg' | 'xl' } = getContext(AVATAR_GROUP_CONTEXT_ID);
let defaultClass =
'material-icons absolute text-light-icon dark:text-dark-icon transition-all duration-150';
if (size === 'xs') {
defaultClass += ' text-2xl bottom-[-0.5rem]';
} else if (size === 'sm') {
defaultClass += ' text-4xl bottom-[-0.5rem]';
} else if (size === 'md') {
defaultClass += ' text-5xl bottom-[-0.5rem]';
} else if (size === 'lg') {
defaultClass += ' text-6xl bottom-[-0.75rem]';
} else if (size === 'xl') {
defaultClass += ' text-7xl bottom-[-0.75rem]';
}
$: finalClass = twMerge(defaultClass, $$props.class);
</script>

<span class={finalClass} style={$$props.style}>
{icon}
</span>
61 changes: 61 additions & 0 deletions src/lib/components/avatar-group/Indicator.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
<script lang="ts">
import { useContext } from '../../utils/useContext';
import { getContext } from 'svelte/internal';
import { AVATAR_GROUP_AVATAR_CONTEXT_ID } from './Avatar.svelte';
import { twMerge } from 'tailwind-merge';
export let placement: 'top-right' | 'bottom-right' | 'top-left' | 'bottom-left' = 'top-right';
useContext({
context_id: AVATAR_GROUP_AVATAR_CONTEXT_ID,
parent: 'Avatar',
component: 'Avatar.Indicator'
});
const {
shape,
size
}: { size: 'xs' | 'sm' | 'md' | 'lg' | 'xl'; shape: 'circle' | 'rounded' | 'square' } =
getContext(AVATAR_GROUP_AVATAR_CONTEXT_ID);
let defaultClass = '';
if (placement === 'top-right') {
defaultClass =
'absolute top-0 right-0 block rounded-full ring-2 ring-light-surface dark:ring-dark-surface bg-primary transition-all duration-150';
} else if (placement === 'top-left') {
defaultClass =
'absolute top-0 left-0 block rounded-full ring-2 ring-light-surface dark:ring-dark-surface bg-primary transition-all duration-150';
} else if (placement === 'bottom-left') {
defaultClass =
'absolute bottom-0 left-0 block rounded-full ring-2 ring-light-surface dark:ring-dark-surface bg-primary transition-all duration-150';
} else if (placement === 'bottom-right') {
defaultClass =
'absolute bottom-0 right-0 block rounded-full ring-2 ring-light-surface dark:ring-dark-surface bg-primary transition-all duration-150';
}
if (shape !== 'circle') {
defaultClass += ' transform';
if (placement === 'top-right') {
defaultClass += ' -translate-y-1/2 translate-x-1/2';
} else if (placement === 'bottom-right') {
defaultClass += ' translate-y-1/2 translate-x-1/2';
} else if (placement === 'top-left') {
defaultClass += ' -translate-y-1/2 -translate-x-1/2';
} else if (placement === 'bottom-left') {
defaultClass += ' translate-y-1/2 -translate-x-1/2';
}
}
if (size === 'xs') {
defaultClass += ' h-1.5 w-1.5';
} else if (size === 'sm') {
defaultClass += ' h-2 w-2';
} else if (size === 'md') {
defaultClass += ' h-2.5 w-2.5';
} else if (size === 'lg') {
defaultClass += ' h-3 w-3';
} else if (size === 'xl') {
defaultClass += ' h-3.5 w-3.5';
}
$: finalClass = twMerge(defaultClass, $$props.class);
</script>

<span class={finalClass} style={$$props.style} />
44 changes: 44 additions & 0 deletions src/lib/components/avatar-group/Placeholder.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
<script lang="ts" context="module">
export const AVATAR_GROUP_AVATAR_PLACEHOLDER_CONTEXT_ID =
'avatar-group-avatar-placeholder-context-id';
</script>

<script lang="ts">
import { AVATAR_GROUP_AVATAR_CONTEXT_ID } from './Avatar.svelte';
import { useContext } from '../../utils/useContext';
import { getContext, setContext } from 'svelte/internal';
import { twMerge } from 'tailwind-merge';
import Icon from './Icon.svelte';
useContext({
context_id: AVATAR_GROUP_AVATAR_CONTEXT_ID,
parent: 'Avatar',
component: 'Avatar.Placeholder'
});
const { shape }: { shape: 'circle' | 'rounded' | 'square' } = getContext(
AVATAR_GROUP_AVATAR_CONTEXT_ID
);
setContext(AVATAR_GROUP_AVATAR_PLACEHOLDER_CONTEXT_ID, {
placeholder: true
});
let defaultClass =
'absolute inset-0 h-full w-full flex items-center justify-center overflow-hidden transition-all duration-150 bg-light-icon-background dark:bg-dark-icon-background';
if (shape === 'circle') {
defaultClass += ' rounded-full';
} else if (shape === 'rounded') {
defaultClass += ' rounded-md';
}
$: finalClass = twMerge(defaultClass, $$props.class);
</script>

<div class={finalClass} style={$$props.style}>
{#if $$slots.icon || $$slots.default}
<slot name="icon" />
<slot />
{:else}
<Icon />
{/if}
</div>
30 changes: 22 additions & 8 deletions src/lib/components/avatar-group/index.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,29 @@
export { default as Avatar } from './Avatar.svelte';
// export { default as AvatarGroup } from './Group.svelte';

import OriginalAvatarGroup from './AvatarGroup.svelte';
import OriginalAvatar from './Avatar.svelte';
// import Group from './Group.svelte';
import Indicator from './Indicator.svelte';
import OriginalPlaceholder from './Placeholder.svelte';
import Icon from './Icon.svelte';

const AvatarGroup = OriginalAvatarGroup as AvatarGroupStatic;
AvatarGroup.Avatar = OriginalAvatar as AvatarStatic;
AvatarGroup.Avatar.Indicator = Indicator;
AvatarGroup.Avatar.Placeholder = OriginalPlaceholder as PlaceholderStatic;
AvatarGroup.Avatar.Placeholder.Icon = Icon;

const Avatar = OriginalAvatar as AvatarStatic;
// Avatar.Group = Group;
export default AvatarGroup;

export default Avatar;
export interface AvatarGroupStatic {
new (...args: ConstructorParameters<typeof OriginalAvatarGroup>): OriginalAvatarGroup;
Avatar: AvatarStatic;
}

export interface AvatarStatic {
new (...args: ConstructorParameters<typeof OriginalAvatar>): OriginalAvatar;
// Group: typeof Group;
Indicator: typeof Indicator;
Placeholder: PlaceholderStatic;
}

export interface PlaceholderStatic {
new (...args: ConstructorParameters<typeof OriginalPlaceholder>): OriginalPlaceholder;
Icon: typeof Icon;
}
Loading

0 comments on commit cad3071

Please sign in to comment.