|
| 1 | +import * as CheckboxPrimitive from '@radix-ui/react-checkbox' |
| 2 | + |
1 | 3 | import { |
2 | | - type Foundation, |
3 | | - type SemanticNames, |
4 | | - css, |
5 | | - smoothCorners, |
| 4 | + absoluteCenter, |
6 | 5 | styled, |
7 | 6 | } from '~/src/foundation' |
8 | 7 |
|
9 | 8 | import { ZIndex } from '~/src/constants/ZIndex' |
10 | | -import { SmoothCornersFeature } from '~/src/features' |
11 | | -import type { |
12 | | - InjectedInterpolation, |
13 | | - InterpolationProps, |
14 | | -} from '~/src/types/Foundation' |
15 | | - |
16 | | -import { AvatarSize } from '~/src/components/Avatars/Avatar' |
17 | | -import { AvatarImage } from '~/src/components/Avatars/Avatar/Avatar.styled' |
| 9 | + |
| 10 | +import { AlphaSmoothCornersBox } from '~/src/components/AlphaSmoothCornersBox' |
| 11 | +import { |
| 12 | + AvatarSize, |
| 13 | + Avatar as BaseAvatar, |
| 14 | +} from '~/src/components/Avatars/Avatar' |
18 | 15 | import { AVATAR_BORDER_RADIUS_PERCENTAGE } from '~/src/components/Avatars/AvatarStyle' |
19 | 16 | import { |
20 | 17 | CheckIcon as CheckIconSource, |
21 | 18 | Icon, |
22 | 19 | } from '~/src/components/Icon' |
23 | 20 |
|
24 | | -interface CheckableAvatarWrapperProps extends InterpolationProps { |
25 | | - isChecked: boolean |
26 | | - isCheckable: boolean |
27 | | -} |
28 | | - |
29 | | -interface CheckIconProps { |
30 | | - avatarSize: AvatarSize |
31 | | -} |
32 | | - |
33 | | -const getCheckIconSize = (avatarSize: AvatarSize) => ({ |
34 | | - [AvatarSize.Size20]: 14, |
35 | | - [AvatarSize.Size24]: 16, |
36 | | - [AvatarSize.Size30]: 20, |
37 | | - [AvatarSize.Size36]: 22, |
38 | | - [AvatarSize.Size42]: 24, |
39 | | - [AvatarSize.Size48]: 28, |
40 | | - [AvatarSize.Size90]: 46, |
41 | | - [AvatarSize.Size120]: 60, |
42 | | -}[avatarSize]) |
43 | | - |
44 | | -export const CheckIcon = styled(Icon).attrs({ source: CheckIconSource })<CheckIconProps>` |
| 21 | +export const CheckIcon = styled(Icon).attrs({ |
| 22 | + source: CheckIconSource, |
| 23 | + color: 'bgtxt-absolute-white-normal', |
| 24 | +})` |
45 | 25 | position: absolute; |
46 | 26 | z-index: ${ZIndex.Float}; |
47 | | - pointer-events: none; |
48 | | - opacity: 0; |
49 | | -
|
50 | | - ${({ avatarSize }) => { |
51 | | - const iconSize = getCheckIconSize(avatarSize) |
52 | | - return css` |
53 | | - width: ${iconSize}px; |
54 | | - height: ${iconSize}px; |
55 | | - ` |
56 | | - }}; |
| 27 | + visibility: hidden; |
| 28 | +
|
| 29 | + ${absoluteCenter()} |
| 30 | +
|
| 31 | + &.size-${AvatarSize.Size20} { |
| 32 | + width: 14px; |
| 33 | + height: 14px; |
| 34 | + } |
| 35 | +
|
| 36 | + &.size-${AvatarSize.Size24} { |
| 37 | + width: 16px; |
| 38 | + height: 16px; |
| 39 | + } |
| 40 | +
|
| 41 | + &.size-${AvatarSize.Size30} { |
| 42 | + width: 20px; |
| 43 | + height: 20px; |
| 44 | + } |
| 45 | +
|
| 46 | + &.size-${AvatarSize.Size36} { |
| 47 | + width: 22px; |
| 48 | + height: 22px; |
| 49 | + } |
| 50 | +
|
| 51 | + &.size-${AvatarSize.Size42} { |
| 52 | + width: 24px; |
| 53 | + height: 24px; |
| 54 | + } |
| 55 | +
|
| 56 | + &.size-${AvatarSize.Size48} { |
| 57 | + width: 28px; |
| 58 | + height: 28px; |
| 59 | + } |
| 60 | +
|
| 61 | + &.size-${AvatarSize.Size72} { |
| 62 | + width: 42px; |
| 63 | + height: 42px; |
| 64 | + } |
| 65 | +
|
| 66 | + &.size-${AvatarSize.Size90} { |
| 67 | + width: 52px; |
| 68 | + height: 52px; |
| 69 | + } |
| 70 | +
|
| 71 | + &.size-${AvatarSize.Size120} { |
| 72 | + width: 68px; |
| 73 | + height: 68px; |
| 74 | + } |
57 | 75 | ` |
58 | 76 |
|
59 | | -const getBackgroundColor = (isChecked: boolean, checkedBackgroundColor: SemanticNames, foundation?: Foundation) => |
60 | | - foundation?.theme?.[isChecked ? checkedBackgroundColor : 'bg-grey-dark'] |
61 | | - |
62 | | -/* eslint-disable @typescript-eslint/indent */ |
63 | | -export const getAvatarImageStyle = ( |
64 | | - isChecked: boolean, |
65 | | - isCheckable: boolean, |
66 | | - checkedBackgroundColor: SemanticNames, |
67 | | - interpolation?: InjectedInterpolation, |
68 | | -) => css` |
69 | | - ${isCheckable && css` |
70 | | - &::before { |
71 | | - display: block; |
72 | | - width: 100%; |
73 | | - height: 100%; |
74 | | - content: ''; |
75 | | - opacity: ${isChecked ? 1 : 0}; |
76 | | - ${'' /** |
77 | | - * NOTE: (@ed) smooth corner가 적용된 상태에선 background 색상을 background-color 속성을 통해 그리지 않으므로 (background: paint(smooth-corners)) |
78 | | - * smooth-corners가 사용 가능한 브라우저에선 background-color 트랜지션또한 불가능합니다. |
79 | | - * 발생하지 않는 트랜지션에 will-change 속성을 주는 건 불필요하므로, will-change 속성에서 background-color를 제거합니다. |
80 | | - */} |
81 | | - will-change: ${SmoothCornersFeature.activated ? 'opacity' : 'opacity, background-color'}; |
82 | | -
|
83 | | - ${({ foundation }) => foundation?.transition.getTransitionsCSS(['opacity', 'background-color'])} |
84 | | -
|
85 | | - ${({ foundation }) => smoothCorners({ |
86 | | - backgroundColor: getBackgroundColor(isChecked, checkedBackgroundColor, foundation), |
87 | | - borderRadius: `${AVATAR_BORDER_RADIUS_PERCENTAGE}%`, |
88 | | - })}; |
89 | | - } |
90 | | - `} |
| 77 | +export const Avatar = styled(BaseAvatar)`` |
91 | 78 |
|
92 | | - ${interpolation} |
93 | | -` |
| 79 | +export const Container = styled(AlphaSmoothCornersBox).attrs({ |
| 80 | + borderRadius: `${AVATAR_BORDER_RADIUS_PERCENTAGE}%`, |
| 81 | +})`` |
| 82 | + |
| 83 | +export const CheckboxPrimitiveRoot = styled(CheckboxPrimitive.Root)` |
| 84 | + all: unset; |
| 85 | + position: relative; |
| 86 | + z-index: ${ZIndex.Base}; |
| 87 | + cursor: pointer; |
| 88 | + outline: none; |
94 | 89 |
|
95 | | -const getCheckableStyle = (isChecked: boolean, isCheckable: boolean) => |
96 | | - (!isCheckable |
97 | | - ? css` |
| 90 | + &[data-disabled] { |
98 | 91 | cursor: not-allowed; |
99 | | - ` |
100 | | - : css` |
101 | | - cursor: pointer; |
| 92 | + } |
102 | 93 |
|
| 94 | + &:not([data-state='unchecked']) { |
103 | 95 | ${CheckIcon} { |
104 | | - opacity: ${isChecked ? 1 : 0}; |
105 | | - will-change: opacity; |
106 | | -
|
107 | | - ${({ foundation }) => foundation?.transition.getTransitionsCSS('opacity')} |
| 96 | + visibility: visible; |
108 | 97 | } |
109 | 98 |
|
110 | | - &:hover ${CheckIcon}, |
111 | | - &:hover ${AvatarImage}::before { |
112 | | - opacity: 1; |
| 99 | + ${Avatar} { |
| 100 | + --bezier-alpha-smooth-corners-box-background-image: none !important; |
| 101 | + --bezier-alpha-smooth-corners-box-background-color: var(--bgtxt-green-normal) !important; |
| 102 | + } |
| 103 | + } |
| 104 | +
|
| 105 | + &:not(&[data-disabled]) { |
| 106 | + &:focus-visible { |
| 107 | + ${Container} { |
| 108 | + --bezier-alpha-smooth-corners-box-padding: 4px !important; |
| 109 | + --bezier-alpha-smooth-corners-box-shadow-spread-radius: 2px !important; |
| 110 | + --bezier-alpha-smooth-corners-box-shadow-color: var(--bgtxt-blue-light) !important; |
| 111 | + } |
113 | 112 | } |
114 | | - `) |
115 | | -/* eslint-enable @typescript-eslint/indent */ |
116 | 113 |
|
117 | | -export const CheckableAvatarWrapper = styled.div<CheckableAvatarWrapperProps>` |
118 | | - position: relative; |
119 | | - display: flex; |
120 | | - align-items: center; |
121 | | - justify-content: center; |
122 | | - user-select: none; |
| 114 | + &:hover, |
| 115 | + &:focus-visible { |
| 116 | + ${CheckIcon} { |
| 117 | + visibility: visible; |
| 118 | + } |
123 | 119 |
|
124 | | - ${({ isChecked, isCheckable }) => getCheckableStyle(isChecked, isCheckable)} |
| 120 | + ${Avatar} { |
| 121 | + --bezier-alpha-smooth-corners-box-background-image: none !important; |
| 122 | + } |
| 123 | + } |
125 | 124 |
|
126 | | - ${({ interpolation }) => interpolation} |
| 125 | + &[data-state='unchecked'] { |
| 126 | + &:hover, |
| 127 | + &:focus-visible { |
| 128 | + ${Avatar} { |
| 129 | + --bezier-alpha-smooth-corners-box-background-color: var(--bg-grey-dark) !important; |
| 130 | + } |
| 131 | + } |
| 132 | + } |
| 133 | +
|
| 134 | + &:not([data-state='unchecked']) { |
| 135 | + &:hover { |
| 136 | + ${Avatar} { |
| 137 | + --bezier-alpha-smooth-corners-box-background-color: var(--bgtxt-green-dark) !important; |
| 138 | + } |
| 139 | + } |
| 140 | + } |
| 141 | + } |
127 | 142 | ` |
0 commit comments