Skip to content

Commit

Permalink
Merge pull request #11 from zolplay-cn/react/avatar
Browse files Browse the repository at this point in the history
feat(react): add avatars
  • Loading branch information
CaliCastle authored Oct 22, 2022
2 parents 341a8a2 + 7d51bb0 commit e74f7b1
Show file tree
Hide file tree
Showing 8 changed files with 261 additions and 3 deletions.
6 changes: 6 additions & 0 deletions packages/react/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# @zolplay/react

## 0.2.0

### Minor Changes

- Added Avatar and StyledAvatar components

## 0.1.1

### Patch Changes
Expand Down
11 changes: 9 additions & 2 deletions packages/react/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@zolplay/react",
"version": "0.1.1",
"version": "0.2.0",
"main": "dist/index.js",
"types": "dist/index.d.ts",
"module": "dist/index.mjs",
Expand All @@ -16,15 +16,22 @@
"lint": "eslint . --ext .ts,.tsx"
},
"dependencies": {
"@radix-ui/react-avatar": "^1.0.1",
"@stitches/react": "^1.2.8",
"@zolplay/config": "workspace:*",
"@zolplay/utils": "workspace:*",
"nanoid": "^4.0.0",
"react": "^18.0.0",
"react-dom": "^18.0.0"
"react-dom": "^18.0.0",
"string-hash": "^1.1.3",
"tinycolor2": "^1.4.2"
},
"devDependencies": {
"@testing-library/jest-dom": "^5.16.5",
"@testing-library/react": "^13.4.0",
"@types/react": "^18.0.21",
"@types/string-hash": "^1.1.1",
"@types/tinycolor2": "^1.4.3",
"@vitejs/plugin-react": "^2.1.0",
"@zolplay/tsconfig": "workspace:*",
"jsdom": "^20.0.1",
Expand Down
18 changes: 18 additions & 0 deletions packages/react/src/Avatar/StyledAvatar.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import * as React from 'react'
import { describe, expect, test } from 'vitest'

import { StyledAvatar } from './StyledAvatar'

import { render, screen } from '@testing-library/react'

describe('StyledAvatar component', () => {
test('should render correctly', () => {
render(
<StyledAvatar.Root>
<StyledAvatar.Image src="https://i.pravatar.cc/300" alt="Avatar" />
<StyledAvatar.Fallback randomColor>AB</StyledAvatar.Fallback>
</StyledAvatar.Root>
)
expect(screen.getByText('AB')).toBeTruthy()
})
})
105 changes: 105 additions & 0 deletions packages/react/src/Avatar/StyledAvatar.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
import { nanoid } from 'nanoid'
import { useEffect, useState } from 'react'
import stringHash from 'string-hash'
import tinycolor from 'tinycolor2'

import type { Component } from '@/types'
import type { AvatarFallbackProps } from '@radix-ui/react-avatar'
import * as AvatarPrimitive from '@radix-ui/react-avatar'
import { styled } from '@stitches/react'

const Root = styled(AvatarPrimitive.Root, {
display: 'inline-flex',
alignItems: 'center',
justifyContent: 'center',
verticalAlign: 'middle',
overflow: 'hidden',
userSelect: 'none',
})

const Image = styled(AvatarPrimitive.Image, {
width: '100%',
height: '100%',
objectFit: 'cover',
borderRadius: 'inherit',
})

const StyledFallback = styled(AvatarPrimitive.Fallback, {
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
position: 'relative',
})
const GradientSvg = styled('svg', {
position: 'absolute',
top: 0,
left: 0,
width: '100%',
height: '100%',
borderRadius: 'inherit',
})

type FallbackProps = AvatarFallbackProps & {
name?: string
randomColor?: boolean
}
const Fallback: Component<FallbackProps> = ({
id,
name,
randomColor,
children,
...props
}) => {
const [fallbackId, setFallbackId] = useState('')

useEffect(() => {
setFallbackId(id ?? name ?? nanoid())
}, [id, name])

if (randomColor) {
const encodedName = encodeURI(fallbackId.toString().split(' ').join('_'))
const hashed = stringHash(encodedName)
const c = tinycolor({ h: hashed % 360, s: 0.95, l: 0.5 })
const c1 = c.toHexString()
const c2 = c.triad()[1].toHexString()

const gradientBackground = (
<GradientSvg role="img" aria-label={name} viewBox="0 0 80 80">
<defs>
<linearGradient
x1="0%"
y1="0%"
x2="100%"
y2="100%"
id={hashed.toString()}
>
<stop stopColor={c1} offset="0%" />
<stop stopColor={c2} offset="100%" />
</linearGradient>
</defs>
<g stroke="none" strokeWidth="1" fill="none">
<rect
fill={`url(#${hashed.toString()})`}
x="0"
y="0"
width="100%"
height="100%"
/>
</g>
</GradientSvg>
)

return (
<StyledFallback {...props}>
{gradientBackground}
{children}
</StyledFallback>
)
}

return <StyledFallback {...props}>{children}</StyledFallback>
}

const StyledAvatar = Object.assign({}, { Root, Image, Fallback })

export { StyledAvatar }
4 changes: 4 additions & 0 deletions packages/react/src/Avatar/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import * as Avatar from '@radix-ui/react-avatar'

export { Avatar }
export * from './StyledAvatar'
1 change: 1 addition & 0 deletions packages/react/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
export * from './Avatar'
export * from './Stacked'
export type { Component, PrimitiveComponent } from '@/types'
2 changes: 1 addition & 1 deletion packages/react/vite.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,6 @@ export default defineConfig({
setupFiles: './tests/setup.ts',
// you might want to disable it, if you don't have tests that rely on CSS
// since parsing CSS is slow
css: false,
css: true,
},
})
117 changes: 117 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit e74f7b1

Please sign in to comment.