podo-ui 0.4.3 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.ko.md +3 -2
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1
- package/dist/react/atom/avatar.d.ts +50 -0
- package/dist/react/atom/avatar.d.ts.map +1 -0
- package/dist/react/atom/avatar.js +54 -0
- package/dist/react/atom/avatar.module.scss +85 -0
- package/package.json +1 -1
- package/{README.md → readme.md} +3 -2
package/README.ko.md
CHANGED
|
@@ -37,10 +37,11 @@ import 'podo-ui/vite-fonts.scss'; // Vite 사용 시
|
|
|
37
37
|
|
|
38
38
|
```tsx
|
|
39
39
|
// Named imports로 개별 컴포넌트 가져오기
|
|
40
|
-
import { Input, Textarea, Editor, EditorView, Field, Pagination } from 'podo-ui';
|
|
40
|
+
import { Input, Textarea, Editor, EditorView, Avatar, Field, Pagination } from 'podo-ui';
|
|
41
41
|
|
|
42
42
|
// 또는 개별 컴포넌트 직접 import (레거시 방식)
|
|
43
43
|
import Input from 'podo-ui/react/atom/input';
|
|
44
|
+
import Avatar from 'podo-ui/react/atom/avatar';
|
|
44
45
|
import Field from 'podo-ui/react/molecule/field';
|
|
45
46
|
```
|
|
46
47
|
|
|
@@ -49,7 +50,7 @@ import Field from 'podo-ui/react/molecule/field';
|
|
|
49
50
|
- CSS 클래스 기반 디자인 시스템
|
|
50
51
|
- 반응형 그리드 시스템 (PC 12, Tablet 6, Mobile 4)
|
|
51
52
|
- 색상 시스템 및 다크 모드 지원
|
|
52
|
-
- React 컴포넌트 제공 (Input, Textarea, Editor, Field)
|
|
53
|
+
- React 컴포넌트 제공 (Avatar, Input, Textarea, Editor, Field)
|
|
53
54
|
|
|
54
55
|
## 문서
|
|
55
56
|
|
package/dist/index.d.ts
CHANGED
|
@@ -2,6 +2,7 @@ import Input from './react/atom/input';
|
|
|
2
2
|
import Textarea from './react/atom/textarea';
|
|
3
3
|
import Editor from './react/atom/editor';
|
|
4
4
|
import EditorView from './react/atom/editor-view';
|
|
5
|
+
import Avatar from './react/atom/avatar';
|
|
5
6
|
import Pagination from './react/molecule/pagination';
|
|
6
7
|
import Field from './react/molecule/field';
|
|
7
8
|
declare const Form: {
|
|
@@ -12,5 +13,5 @@ declare const Form: {
|
|
|
12
13
|
Field: ({ label, labelClass, required, helper, helperClass, children, validator, value, setClassName, className, }: import("./react/molecule/field").FieldProps) => import("react/jsx-runtime").JSX.Element;
|
|
13
14
|
};
|
|
14
15
|
export default Form;
|
|
15
|
-
export { Input, Textarea, Editor, EditorView, Pagination, Field };
|
|
16
|
+
export { Input, Textarea, Editor, EditorView, Avatar, Pagination, Field };
|
|
16
17
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,oBAAoB,CAAC;AACvC,OAAO,QAAQ,MAAM,uBAAuB,CAAC;AAC7C,OAAO,MAAM,MAAM,qBAAqB,CAAC;AACzC,OAAO,UAAU,MAAM,0BAA0B,CAAC;AAClD,OAAO,UAAU,MAAM,6BAA6B,CAAC;AACrD,OAAO,KAAK,MAAM,wBAAwB,CAAC;AAC3C,QAAA,MAAM,IAAI;;;;;;CAMT,CAAC;AAEF,eAAe,IAAI,CAAC;AAEpB,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,UAAU,EAAE,KAAK,EAAE,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,oBAAoB,CAAC;AACvC,OAAO,QAAQ,MAAM,uBAAuB,CAAC;AAC7C,OAAO,MAAM,MAAM,qBAAqB,CAAC;AACzC,OAAO,UAAU,MAAM,0BAA0B,CAAC;AAClD,OAAO,MAAM,MAAM,qBAAqB,CAAC;AACzC,OAAO,UAAU,MAAM,6BAA6B,CAAC;AACrD,OAAO,KAAK,MAAM,wBAAwB,CAAC;AAC3C,QAAA,MAAM,IAAI;;;;;;CAMT,CAAC;AAEF,eAAe,IAAI,CAAC;AAEpB,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,UAAU,EAAE,KAAK,EAAE,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -2,6 +2,7 @@ import Input from './react/atom/input';
|
|
|
2
2
|
import Textarea from './react/atom/textarea';
|
|
3
3
|
import Editor from './react/atom/editor';
|
|
4
4
|
import EditorView from './react/atom/editor-view';
|
|
5
|
+
import Avatar from './react/atom/avatar';
|
|
5
6
|
import Pagination from './react/molecule/pagination';
|
|
6
7
|
import Field from './react/molecule/field';
|
|
7
8
|
const Form = {
|
|
@@ -12,4 +13,4 @@ const Form = {
|
|
|
12
13
|
Field,
|
|
13
14
|
};
|
|
14
15
|
export default Form;
|
|
15
|
-
export { Input, Textarea, Editor, EditorView, Pagination, Field };
|
|
16
|
+
export { Input, Textarea, Editor, EditorView, Avatar, Pagination, Field };
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
export interface AvatarProps {
|
|
3
|
+
/**
|
|
4
|
+
* Avatar type
|
|
5
|
+
* - image: Display user uploaded image
|
|
6
|
+
* - icon: Display system provided icon with background
|
|
7
|
+
* - text: Display user name or initials with background
|
|
8
|
+
*/
|
|
9
|
+
type?: 'image' | 'icon' | 'text';
|
|
10
|
+
/**
|
|
11
|
+
* Image source URL (for type='image')
|
|
12
|
+
*/
|
|
13
|
+
src?: string;
|
|
14
|
+
/**
|
|
15
|
+
* Icon class name (for type='icon')
|
|
16
|
+
* @default 'icon-user'
|
|
17
|
+
*/
|
|
18
|
+
icon?: string;
|
|
19
|
+
/**
|
|
20
|
+
* Text content (for type='text')
|
|
21
|
+
* Will display first 2 characters if longer
|
|
22
|
+
* @example 'AB' or '보라'
|
|
23
|
+
*/
|
|
24
|
+
text?: string;
|
|
25
|
+
/**
|
|
26
|
+
* Avatar size in pixels
|
|
27
|
+
* @default 56
|
|
28
|
+
*/
|
|
29
|
+
size?: 16 | 20 | 24 | 28 | 32 | 36 | 40 | 48 | 56;
|
|
30
|
+
/**
|
|
31
|
+
* Show activity ring (indicates user is active)
|
|
32
|
+
* @default false
|
|
33
|
+
*/
|
|
34
|
+
activityRing?: boolean;
|
|
35
|
+
/**
|
|
36
|
+
* Additional CSS class names
|
|
37
|
+
*/
|
|
38
|
+
className?: string;
|
|
39
|
+
/**
|
|
40
|
+
* Alt text for image
|
|
41
|
+
*/
|
|
42
|
+
alt?: string;
|
|
43
|
+
/**
|
|
44
|
+
* Click handler
|
|
45
|
+
*/
|
|
46
|
+
onClick?: () => void;
|
|
47
|
+
}
|
|
48
|
+
declare const Avatar: React.FC<AvatarProps>;
|
|
49
|
+
export default Avatar;
|
|
50
|
+
//# sourceMappingURL=avatar.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"avatar.d.ts","sourceRoot":"","sources":["../../../react/atom/avatar.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAG1B,MAAM,WAAW,WAAW;IAC1B;;;;;OAKG;IACH,IAAI,CAAC,EAAE,OAAO,GAAG,MAAM,GAAG,MAAM,CAAC;IAEjC;;OAEG;IACH,GAAG,CAAC,EAAE,MAAM,CAAC;IAEb;;;OAGG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IAEd;;;;OAIG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IAEd;;;OAGG;IACH,IAAI,CAAC,EAAE,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC;IAElD;;;OAGG;IACH,YAAY,CAAC,EAAE,OAAO,CAAC;IAEvB;;OAEG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IAEnB;;OAEG;IACH,GAAG,CAAC,EAAE,MAAM,CAAC;IAEb;;OAEG;IACH,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;CACtB;AAED,QAAA,MAAM,MAAM,EAAE,KAAK,CAAC,EAAE,CAAC,WAAW,CAmFjC,CAAC;AAEF,eAAe,MAAM,CAAC"}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import styles from './avatar.module.scss';
|
|
3
|
+
const Avatar = ({ type = 'icon', src, icon = 'icon-user', text, size = 56, activityRing = false, className = '', alt = 'Avatar', onClick, }) => {
|
|
4
|
+
const wrapperClasses = [
|
|
5
|
+
styles.wrapper,
|
|
6
|
+
activityRing && styles.activityRing,
|
|
7
|
+
className,
|
|
8
|
+
]
|
|
9
|
+
.filter(Boolean)
|
|
10
|
+
.join(' ');
|
|
11
|
+
const avatarClasses = [
|
|
12
|
+
styles.avatar,
|
|
13
|
+
styles[`size-${size}`],
|
|
14
|
+
styles[`type-${type}`],
|
|
15
|
+
]
|
|
16
|
+
.filter(Boolean)
|
|
17
|
+
.join(' ');
|
|
18
|
+
// Calculate wrapper size for activity ring
|
|
19
|
+
const wrapperSize = activityRing ? size + 10 : size;
|
|
20
|
+
// Format text to show only first 2 characters
|
|
21
|
+
const displayText = text ? text.slice(0, 2) : '';
|
|
22
|
+
// Calculate font size based on avatar size and type
|
|
23
|
+
const getFontSize = (contentType) => {
|
|
24
|
+
if (contentType === 'icon') {
|
|
25
|
+
// 아이콘: size={56}일 때 44px 기준 (약 78.5%)
|
|
26
|
+
const iconRatio = 0.785;
|
|
27
|
+
return Math.round(size * iconRatio);
|
|
28
|
+
}
|
|
29
|
+
else {
|
|
30
|
+
// 텍스트: size={56}일 때 display6(24px) 기준 (약 43%)
|
|
31
|
+
const textRatio = 0.43;
|
|
32
|
+
return Math.round(size * textRatio);
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
const renderContent = () => {
|
|
36
|
+
if (type === 'image' && src) {
|
|
37
|
+
return _jsx("img", { src: src, alt: alt, className: styles.image });
|
|
38
|
+
}
|
|
39
|
+
if (type === 'icon') {
|
|
40
|
+
return _jsx("i", { className: icon, style: { fontSize: getFontSize('icon') } });
|
|
41
|
+
}
|
|
42
|
+
if (type === 'text' && displayText) {
|
|
43
|
+
return _jsx("span", { style: { fontSize: getFontSize('text') }, children: displayText });
|
|
44
|
+
}
|
|
45
|
+
// Fallback to icon
|
|
46
|
+
return _jsx("i", { className: icon, style: { fontSize: getFontSize('icon') } });
|
|
47
|
+
};
|
|
48
|
+
return (_jsx("div", { className: wrapperClasses, style: { width: wrapperSize, height: wrapperSize }, onClick: onClick, role: onClick ? 'button' : undefined, tabIndex: onClick ? 0 : undefined, children: _jsx("div", { className: avatarClasses, style: {
|
|
49
|
+
width: size,
|
|
50
|
+
height: size,
|
|
51
|
+
fontSize: type === 'text' ? getFontSize('text') : getFontSize('icon')
|
|
52
|
+
}, children: renderContent() }) }));
|
|
53
|
+
};
|
|
54
|
+
export default Avatar;
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
@use '../../../mixin' as *;
|
|
2
|
+
|
|
3
|
+
.wrapper {
|
|
4
|
+
position: relative;
|
|
5
|
+
display: inline-flex;
|
|
6
|
+
align-items: center;
|
|
7
|
+
justify-content: center;
|
|
8
|
+
flex-shrink: 0;
|
|
9
|
+
|
|
10
|
+
&[role="button"] {
|
|
11
|
+
cursor: pointer;
|
|
12
|
+
|
|
13
|
+
&:hover {
|
|
14
|
+
opacity: 0.9;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
.activityRing {
|
|
20
|
+
&::before {
|
|
21
|
+
content: '';
|
|
22
|
+
position: absolute;
|
|
23
|
+
top: 0;
|
|
24
|
+
left: 0;
|
|
25
|
+
right: 0;
|
|
26
|
+
bottom: 0;
|
|
27
|
+
border: 3px solid color('primary');
|
|
28
|
+
border-radius: r(full);
|
|
29
|
+
pointer-events: none;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
.avatar {
|
|
34
|
+
display: flex;
|
|
35
|
+
align-items: center;
|
|
36
|
+
justify-content: center;
|
|
37
|
+
border-radius: r(full);
|
|
38
|
+
overflow: hidden;
|
|
39
|
+
position: relative;
|
|
40
|
+
font-weight: 600;
|
|
41
|
+
user-select: none;
|
|
42
|
+
|
|
43
|
+
// Type variations
|
|
44
|
+
&.type-image {
|
|
45
|
+
background-color: transparent;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
&.type-icon,
|
|
49
|
+
&.type-text {
|
|
50
|
+
background-color: color('border');
|
|
51
|
+
color: color('text-sub');
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Image inside avatar
|
|
55
|
+
.image {
|
|
56
|
+
width: 100%;
|
|
57
|
+
height: 100%;
|
|
58
|
+
object-fit: cover;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Icon styling
|
|
62
|
+
i[class^='icon-'],
|
|
63
|
+
i[class*=' icon-'] {
|
|
64
|
+
display: flex;
|
|
65
|
+
align-items: center;
|
|
66
|
+
justify-content: center;
|
|
67
|
+
margin-right: 0 !important; // 전역 아이콘 margin 제거
|
|
68
|
+
font-size: inherit; // 부모로부터 font-size 상속
|
|
69
|
+
|
|
70
|
+
&::before {
|
|
71
|
+
font-size: inherit !important; // 전역 아이콘 font-size 오버라이드
|
|
72
|
+
line-height: 1;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Text styling
|
|
77
|
+
span {
|
|
78
|
+
text-transform: uppercase;
|
|
79
|
+
line-height: 1;
|
|
80
|
+
font-size: inherit; // 부모로부터 font-size 상속
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// Size classes for potential future use
|
|
85
|
+
// Font sizes are now calculated dynamically in JavaScript
|
package/package.json
CHANGED
package/{README.md → readme.md}
RENAMED
|
@@ -37,10 +37,11 @@ import 'podo-ui/vite-fonts.scss'; // When using Vite
|
|
|
37
37
|
|
|
38
38
|
```tsx
|
|
39
39
|
// Import individual components using named imports
|
|
40
|
-
import { Input, Textarea, Editor, EditorView, Field, Pagination } from 'podo-ui';
|
|
40
|
+
import { Input, Textarea, Editor, EditorView, Avatar, Field, Pagination } from 'podo-ui';
|
|
41
41
|
|
|
42
42
|
// Or import components directly (legacy method)
|
|
43
43
|
import Input from 'podo-ui/react/atom/input';
|
|
44
|
+
import Avatar from 'podo-ui/react/atom/avatar';
|
|
44
45
|
import Field from 'podo-ui/react/molecule/field';
|
|
45
46
|
```
|
|
46
47
|
|
|
@@ -49,7 +50,7 @@ import Field from 'podo-ui/react/molecule/field';
|
|
|
49
50
|
- CSS class-based design system
|
|
50
51
|
- Responsive grid system (PC 12, Tablet 6, Mobile 4)
|
|
51
52
|
- Color system with dark mode support
|
|
52
|
-
- React components (Input, Textarea, Editor, Field)
|
|
53
|
+
- React components (Avatar, Input, Textarea, Editor, Field)
|
|
53
54
|
|
|
54
55
|
## Documentation
|
|
55
56
|
|