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 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
@@ -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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "podo-ui",
3
- "version": "0.4.3",
3
+ "version": "0.5.0",
4
4
  "type": "module",
5
5
  "author": "hada0127 <work@tarucy.net>",
6
6
  "license": "MIT",
@@ -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