@pisodev/test-component-library 1.1.2 → 2.0.1
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/components/Anchor/Anchor.native.tsx +44 -0
- package/components/Anchor/Anchor.types.ts +11 -0
- package/components/Anchor/Anchor.web.module.scss +33 -0
- package/components/Anchor/Anchor.web.tsx +35 -0
- package/components/Anchor/README.md +58 -0
- package/components/Anchor/index.ts +2 -0
- package/components/README.md +195 -0
- package/components/StatusBar/README.md +56 -0
- package/components/StatusBar/StatusBar.tsx +17 -0
- package/components/StatusBar/StatusBar.types.ts +5 -0
- package/components/StatusBar/index.ts +3 -0
- package/components/StatusBar/index.web.ts +14 -0
- package/components/Tooltip/README.md +45 -0
- package/components/Tooltip/Tooltip.module.scss +44 -0
- package/components/Tooltip/Tooltip.tsx +26 -0
- package/components/Tooltip/Tooltip.types.ts +7 -0
- package/components/Tooltip/index.native.ts +15 -0
- package/components/Tooltip/index.ts +3 -0
- package/components/index.ts +8 -0
- package/dist/Anchor/Anchor.native.d.ts +3 -0
- package/dist/Anchor/Anchor.native.js +34 -0
- package/dist/Anchor/Anchor.types.d.ts +10 -0
- package/dist/Anchor/Anchor.types.js +2 -0
- package/dist/Anchor/Anchor.web.d.ts +3 -0
- package/dist/Anchor/index.d.ts +2 -0
- package/dist/StatusBar/StatusBar.d.ts +3 -0
- package/dist/StatusBar/StatusBar.types.d.ts +5 -0
- package/dist/StatusBar/StatusBar.types.js +2 -0
- package/dist/StatusBar/index.d.ts +2 -0
- package/dist/StatusBar/index.web.d.ts +3 -0
- package/dist/Tooltip/Tooltip.d.ts +3 -0
- package/dist/Tooltip/Tooltip.types.d.ts +6 -0
- package/dist/Tooltip/Tooltip.types.js +2 -0
- package/dist/Tooltip/index.d.ts +2 -0
- package/dist/Tooltip/index.native.d.ts +4 -0
- package/dist/Tooltip/index.native.js +12 -0
- package/dist/index.d.ts +3 -2
- package/dist/index.esm.js +23 -143
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +23 -146
- package/dist/index.js.map +1 -1
- package/package.json +22 -9
- package/README.md +0 -67
- package/dist/components/Anchor/Anchor.d.ts +0 -11
- package/dist/components/Anchor/index.d.ts +0 -2
- package/dist/components/Button/Button.d.ts +0 -11
- package/dist/components/Button/index.d.ts +0 -2
- package/dist/components/Card/Card.d.ts +0 -11
- package/dist/components/Card/index.d.ts +0 -2
- package/dist/components/index.d.ts +0 -6
- package/dist/theme/ThemeProvider.d.ts +0 -12
- package/dist/theme/defaultTheme.d.ts +0 -2
- package/dist/theme/index.d.ts +0 -3
- package/dist/theme/types.d.ts +0 -23
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { Text, Pressable, Linking, StyleSheet } from 'react-native';
|
|
3
|
+
import { AnchorProps } from './Anchor.types';
|
|
4
|
+
|
|
5
|
+
export const Anchor: React.FC<AnchorProps> = ({
|
|
6
|
+
children,
|
|
7
|
+
href,
|
|
8
|
+
title,
|
|
9
|
+
onPress,
|
|
10
|
+
}) => {
|
|
11
|
+
const handlePress = () => {
|
|
12
|
+
if (onPress) {
|
|
13
|
+
onPress();
|
|
14
|
+
} else if (href) {
|
|
15
|
+
Linking.openURL(href);
|
|
16
|
+
}
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
return (
|
|
20
|
+
<Pressable
|
|
21
|
+
onPress={handlePress}
|
|
22
|
+
accessibilityRole="link"
|
|
23
|
+
accessibilityLabel={title}
|
|
24
|
+
accessibilityHint={`Opens ${href}`}
|
|
25
|
+
>
|
|
26
|
+
{({ pressed }: any) => (
|
|
27
|
+
<Text style={[styles.anchor, pressed && styles.anchorPressed]}>
|
|
28
|
+
{children}
|
|
29
|
+
</Text>
|
|
30
|
+
)}
|
|
31
|
+
</Pressable>
|
|
32
|
+
);
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
const styles = StyleSheet.create({
|
|
36
|
+
anchor: {
|
|
37
|
+
color: '#2196f3',
|
|
38
|
+
textDecorationLine: 'none',
|
|
39
|
+
},
|
|
40
|
+
anchorPressed: {
|
|
41
|
+
color: '#1976d2',
|
|
42
|
+
textDecorationLine: 'underline',
|
|
43
|
+
},
|
|
44
|
+
});
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
$transition: all 0.2s ease;
|
|
2
|
+
|
|
3
|
+
// CSS Variables fallback
|
|
4
|
+
$primary-color: var(--color-primary-600, #2196f3);
|
|
5
|
+
$primary-hover: var(--color-primary-700, #1976d2);
|
|
6
|
+
$primary-visited: var(--color-primary-800, #1565c0);
|
|
7
|
+
$primary-active: var(--color-primary-900, #0d47a1);
|
|
8
|
+
|
|
9
|
+
.anchor {
|
|
10
|
+
color: $primary-color;
|
|
11
|
+
text-decoration: none;
|
|
12
|
+
transition: $transition;
|
|
13
|
+
cursor: pointer;
|
|
14
|
+
position: relative;
|
|
15
|
+
|
|
16
|
+
&:hover {
|
|
17
|
+
color: $primary-hover;
|
|
18
|
+
text-decoration: underline;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
&:focus {
|
|
22
|
+
outline: 2px solid $primary-color;
|
|
23
|
+
outline-offset: 2px;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
&:visited {
|
|
27
|
+
color: $primary-visited;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
&:active {
|
|
31
|
+
color: $primary-active;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { AnchorProps } from './Anchor.types';
|
|
3
|
+
import styles from './Anchor.web.module.scss';
|
|
4
|
+
|
|
5
|
+
export const Anchor: React.FC<AnchorProps> = ({
|
|
6
|
+
children,
|
|
7
|
+
href,
|
|
8
|
+
rel,
|
|
9
|
+
title,
|
|
10
|
+
target = '_self',
|
|
11
|
+
className = '',
|
|
12
|
+
onPress,
|
|
13
|
+
}) => {
|
|
14
|
+
const anchorClasses = [styles.anchor, className].filter(Boolean).join(' ');
|
|
15
|
+
|
|
16
|
+
const handleClick = (e: React.MouseEvent<HTMLAnchorElement>) => {
|
|
17
|
+
if (onPress) {
|
|
18
|
+
e.preventDefault();
|
|
19
|
+
onPress();
|
|
20
|
+
}
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
return (
|
|
24
|
+
<a
|
|
25
|
+
href={href}
|
|
26
|
+
rel={rel}
|
|
27
|
+
title={title}
|
|
28
|
+
target={target}
|
|
29
|
+
className={anchorClasses}
|
|
30
|
+
onClick={handleClick}
|
|
31
|
+
>
|
|
32
|
+
{children}
|
|
33
|
+
</a>
|
|
34
|
+
);
|
|
35
|
+
};
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# Anchor
|
|
2
|
+
|
|
3
|
+
링크 컴포넌트 - 웹과 React Native 모두 지원
|
|
4
|
+
|
|
5
|
+
## Props
|
|
6
|
+
|
|
7
|
+
| Prop | Type | Required | Platform | Description |
|
|
8
|
+
|------|------|----------|----------|-------------|
|
|
9
|
+
| children | ReactNode | ✓ | All | 링크 텍스트 |
|
|
10
|
+
| href | string | ✓ | All | URL |
|
|
11
|
+
| rel | string | - | Web only | rel 속성 |
|
|
12
|
+
| title | string | - | All | title 속성 / 접근성 레이블 |
|
|
13
|
+
| target | '_blank' \| '_self' \| '_parent' \| '_top' | - | Web only | target 속성 |
|
|
14
|
+
| className | string | - | Web only | CSS 클래스 |
|
|
15
|
+
| onPress | () => void | - | All | 클릭 핸들러 |
|
|
16
|
+
|
|
17
|
+
## 웹 사용법
|
|
18
|
+
|
|
19
|
+
```tsx
|
|
20
|
+
import { Anchor } from '@pisodev/test-component-library';
|
|
21
|
+
|
|
22
|
+
<Anchor
|
|
23
|
+
href="https://example.com"
|
|
24
|
+
title="Example"
|
|
25
|
+
rel="noopener"
|
|
26
|
+
target="_blank"
|
|
27
|
+
>
|
|
28
|
+
Click me
|
|
29
|
+
</Anchor>
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## React Native 사용법
|
|
33
|
+
|
|
34
|
+
```tsx
|
|
35
|
+
import { Anchor } from '@pisodev/test-component-library';
|
|
36
|
+
|
|
37
|
+
<Anchor
|
|
38
|
+
href="https://example.com"
|
|
39
|
+
title="Example"
|
|
40
|
+
>
|
|
41
|
+
Click me
|
|
42
|
+
</Anchor>
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## 구현 차이점
|
|
46
|
+
|
|
47
|
+
### 웹 (Anchor.web.tsx)
|
|
48
|
+
- `<a>` 태그 사용
|
|
49
|
+
- SCSS 모듈 스타일링
|
|
50
|
+
- :hover, :focus, :visited, :active 가상 선택자
|
|
51
|
+
- target, rel 속성 지원
|
|
52
|
+
|
|
53
|
+
### React Native (Anchor.native.tsx)
|
|
54
|
+
- Pressable + Text 컴포넌트 사용
|
|
55
|
+
- StyleSheet 기반 스타일링
|
|
56
|
+
- Pressed 상태로 hover 효과 구현
|
|
57
|
+
- Linking API로 URL 열기
|
|
58
|
+
- 접근성 속성 자동 적용
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
# 컴포넌트 가이드
|
|
2
|
+
|
|
3
|
+
## 컴포넌트 타입
|
|
4
|
+
|
|
5
|
+
### 1. 크로스 플랫폼 컴포넌트 (웹 + RN 모두 지원)
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
ComponentName/
|
|
9
|
+
├── ComponentName.types.ts # 공유 타입
|
|
10
|
+
├── ComponentName.web.tsx # 웹 구현
|
|
11
|
+
├── ComponentName.native.tsx # RN 구현
|
|
12
|
+
├── index.ts # export
|
|
13
|
+
└── README.md
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
**예시**: Anchor, Button, Card
|
|
17
|
+
|
|
18
|
+
**index.ts**:
|
|
19
|
+
```typescript
|
|
20
|
+
export { ComponentName } from './ComponentName';
|
|
21
|
+
export type { ComponentNameProps } from './ComponentName.types';
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
### 2. 웹 전용 컴포넌트
|
|
27
|
+
|
|
28
|
+
```
|
|
29
|
+
WebOnlyComponent/
|
|
30
|
+
├── WebOnlyComponent.types.ts # 타입
|
|
31
|
+
├── WebOnlyComponent.tsx # 웹 구현 (.web.tsx 아님!)
|
|
32
|
+
├── WebOnlyComponent.module.scss # 스타일
|
|
33
|
+
├── index.ts # export
|
|
34
|
+
└── README.md
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
**예시**: Table, Tooltip, Dropdown
|
|
38
|
+
|
|
39
|
+
**index.ts**:
|
|
40
|
+
```typescript
|
|
41
|
+
export { WebOnlyComponent } from './WebOnlyComponent';
|
|
42
|
+
export type { WebOnlyComponentProps } from './WebOnlyComponent.types';
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
**중요**: RN에서 import 시 에러 방지를 위한 처리
|
|
46
|
+
|
|
47
|
+
**Option 1 - 타입만 제공**:
|
|
48
|
+
```typescript
|
|
49
|
+
// index.native.ts (별도 파일)
|
|
50
|
+
import { WebOnlyComponentProps } from './WebOnlyComponent.types';
|
|
51
|
+
|
|
52
|
+
export const WebOnlyComponent = () => {
|
|
53
|
+
if (__DEV__) {
|
|
54
|
+
console.warn('WebOnlyComponent is not available on React Native');
|
|
55
|
+
}
|
|
56
|
+
return null;
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
export type { WebOnlyComponentProps };
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
**Option 2 - 완전히 제외**:
|
|
63
|
+
```typescript
|
|
64
|
+
// components/index.ts에서
|
|
65
|
+
export * from './Anchor';
|
|
66
|
+
export * from './Button';
|
|
67
|
+
|
|
68
|
+
// 조건부 export (빌드 설정에서 처리)
|
|
69
|
+
if (typeof window !== 'undefined') {
|
|
70
|
+
export * from './WebOnlyComponent';
|
|
71
|
+
}
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
---
|
|
75
|
+
|
|
76
|
+
### 3. React Native 전용 컴포넌트
|
|
77
|
+
|
|
78
|
+
```
|
|
79
|
+
NativeOnlyComponent/
|
|
80
|
+
├── NativeOnlyComponent.types.ts # 타입
|
|
81
|
+
├── NativeOnlyComponent.tsx # RN 구현 (.native.tsx 아님!)
|
|
82
|
+
├── index.ts # export
|
|
83
|
+
└── README.md
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
**예시**: MapView, Camera, BiometricAuth
|
|
87
|
+
|
|
88
|
+
**index.ts**:
|
|
89
|
+
```typescript
|
|
90
|
+
export { NativeOnlyComponent } from './NativeOnlyComponent';
|
|
91
|
+
export type { NativeOnlyComponentProps } from './NativeOnlyComponent.types';
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
**웹에서 import 시 에러 방지**:
|
|
95
|
+
|
|
96
|
+
**index.web.ts (별도 파일)**:
|
|
97
|
+
```typescript
|
|
98
|
+
import { NativeOnlyComponentProps } from './NativeOnlyComponent.types';
|
|
99
|
+
|
|
100
|
+
export const NativeOnlyComponent = () => {
|
|
101
|
+
if (process.env.NODE_ENV === 'development') {
|
|
102
|
+
console.warn('NativeOnlyComponent is only available on React Native');
|
|
103
|
+
}
|
|
104
|
+
return null;
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
export type { NativeOnlyComponentProps };
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
---
|
|
111
|
+
|
|
112
|
+
## 실전 예시
|
|
113
|
+
|
|
114
|
+
### 예시 1: Tooltip (웹 전용)
|
|
115
|
+
|
|
116
|
+
DOM API를 사용하므로 RN에서 불가능
|
|
117
|
+
|
|
118
|
+
```
|
|
119
|
+
Tooltip/
|
|
120
|
+
├── Tooltip.types.ts
|
|
121
|
+
├── Tooltip.tsx # 웹 구현
|
|
122
|
+
├── Tooltip.module.scss
|
|
123
|
+
├── index.ts # 웹용 export
|
|
124
|
+
├── index.native.ts # RN용 - null 반환 또는 대안
|
|
125
|
+
└── README.md
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
**index.native.ts**:
|
|
129
|
+
```typescript
|
|
130
|
+
import { TooltipProps } from './Tooltip.types';
|
|
131
|
+
|
|
132
|
+
// RN에서는 React Native의 Tooltip 사용 권장
|
|
133
|
+
export const Tooltip: React.FC<TooltipProps> = ({ children }) => {
|
|
134
|
+
return <>{children}</>; // 툴팁 없이 children만 렌더
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
export type { TooltipProps };
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
### 예시 2: DatePicker (플랫폼별 구현 차이가 큼)
|
|
141
|
+
|
|
142
|
+
웹과 RN에서 완전히 다른 API 사용
|
|
143
|
+
|
|
144
|
+
```
|
|
145
|
+
DatePicker/
|
|
146
|
+
├── DatePicker.types.ts # 공통 props만
|
|
147
|
+
├── DatePicker.web.tsx # HTML input type="date"
|
|
148
|
+
├── DatePicker.native.tsx # @react-native-community/datetimepicker
|
|
149
|
+
├── index.ts
|
|
150
|
+
└── README.md
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
### 예시 3: MapView (RN 전용)
|
|
154
|
+
|
|
155
|
+
react-native-maps 사용
|
|
156
|
+
|
|
157
|
+
```
|
|
158
|
+
MapView/
|
|
159
|
+
├── MapView.types.ts
|
|
160
|
+
├── MapView.tsx # RN 구현
|
|
161
|
+
├── index.ts # RN export
|
|
162
|
+
├── index.web.ts # 웹에서는 에러 또는 대안
|
|
163
|
+
└── README.md
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
**index.web.ts**:
|
|
167
|
+
```typescript
|
|
168
|
+
export const MapView = () => {
|
|
169
|
+
throw new Error(
|
|
170
|
+
'MapView is only available on React Native. Use a web map library like react-leaflet instead.'
|
|
171
|
+
);
|
|
172
|
+
};
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
---
|
|
176
|
+
|
|
177
|
+
## 권장 사항
|
|
178
|
+
|
|
179
|
+
### 플랫폼 전용 컴포넌트를 만들기 전에
|
|
180
|
+
|
|
181
|
+
1. **정말 플랫폼 전용이 필요한가?**
|
|
182
|
+
- 대부분의 UI 컴포넌트는 크로스 플랫폼 가능
|
|
183
|
+
- 로직만 분리하고 렌더링만 다르게 할 수 있는지 검토
|
|
184
|
+
|
|
185
|
+
2. **대안 제공**
|
|
186
|
+
- 웹 전용: RN에서는 null 반환 + 경고
|
|
187
|
+
- RN 전용: 웹에서는 에러 throw + 대안 라이브러리 제안
|
|
188
|
+
|
|
189
|
+
3. **문서화**
|
|
190
|
+
- README.md에 플랫폼 제한 명시
|
|
191
|
+
- 대안 라이브러리/방법 제시
|
|
192
|
+
|
|
193
|
+
4. **TypeScript 타입은 공유**
|
|
194
|
+
- Props 타입은 양쪽에서 사용 가능하도록
|
|
195
|
+
- 개발자 경험(DX) 향상
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# StatusBar
|
|
2
|
+
|
|
3
|
+
**플랫폼**: React Native 전용
|
|
4
|
+
|
|
5
|
+
모바일 상단 상태바(시간, 배터리 등)의 스타일을 제어하는 컴포넌트입니다.
|
|
6
|
+
|
|
7
|
+
## 플랫폼 지원
|
|
8
|
+
|
|
9
|
+
- ⚠️ **Web**: 지원 안 함 (null 반환)
|
|
10
|
+
- ✅ **React Native**: 완전 지원
|
|
11
|
+
|
|
12
|
+
## Props
|
|
13
|
+
|
|
14
|
+
| Prop | Type | Required | Description |
|
|
15
|
+
|------|------|----------|-------------|
|
|
16
|
+
| barStyle | 'default' \| 'light-content' \| 'dark-content' | - | 상태바 텍스트 색상 |
|
|
17
|
+
| backgroundColor | string | - | 상태바 배경색 (Android) |
|
|
18
|
+
| hidden | boolean | - | 상태바 숨김 여부 |
|
|
19
|
+
|
|
20
|
+
## React Native 사용법
|
|
21
|
+
|
|
22
|
+
```tsx
|
|
23
|
+
import { StatusBar } from '@pisodev/test-component-library';
|
|
24
|
+
|
|
25
|
+
function App() {
|
|
26
|
+
return (
|
|
27
|
+
<>
|
|
28
|
+
<StatusBar
|
|
29
|
+
barStyle="dark-content"
|
|
30
|
+
backgroundColor="#ffffff"
|
|
31
|
+
/>
|
|
32
|
+
{/* 나머지 앱 내용 */}
|
|
33
|
+
</>
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## 웹 대안
|
|
39
|
+
|
|
40
|
+
웹에서는 HTML meta 태그로 브라우저 테마 색상을 제어할 수 있습니다:
|
|
41
|
+
|
|
42
|
+
```html
|
|
43
|
+
<!-- public/index.html -->
|
|
44
|
+
<meta name="theme-color" content="#ffffff">
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
또는 JavaScript로 동적 제어:
|
|
48
|
+
|
|
49
|
+
```tsx
|
|
50
|
+
useEffect(() => {
|
|
51
|
+
const metaThemeColor = document.querySelector('meta[name="theme-color"]');
|
|
52
|
+
if (metaThemeColor) {
|
|
53
|
+
metaThemeColor.setAttribute('content', '#ffffff');
|
|
54
|
+
}
|
|
55
|
+
}, []);
|
|
56
|
+
```
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { StatusBar as RNStatusBar } from 'react-native';
|
|
3
|
+
import { StatusBarProps } from './StatusBar.types';
|
|
4
|
+
|
|
5
|
+
export const StatusBar: React.FC<StatusBarProps> = ({
|
|
6
|
+
barStyle = 'default',
|
|
7
|
+
backgroundColor,
|
|
8
|
+
hidden = false,
|
|
9
|
+
}) => {
|
|
10
|
+
return (
|
|
11
|
+
<RNStatusBar
|
|
12
|
+
barStyle={barStyle}
|
|
13
|
+
backgroundColor={backgroundColor}
|
|
14
|
+
hidden={hidden}
|
|
15
|
+
/>
|
|
16
|
+
);
|
|
17
|
+
};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { StatusBarProps } from './StatusBar.types';
|
|
2
|
+
|
|
3
|
+
// 웹에서는 StatusBar 개념이 없음
|
|
4
|
+
export const StatusBar = (_props: StatusBarProps) => {
|
|
5
|
+
if (process.env.NODE_ENV === 'development') {
|
|
6
|
+
console.warn(
|
|
7
|
+
'StatusBar is only available on React Native. ' +
|
|
8
|
+
'For web, you can use <meta name="theme-color"> in your HTML.'
|
|
9
|
+
);
|
|
10
|
+
}
|
|
11
|
+
return null;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
export type { StatusBarProps };
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# Tooltip
|
|
2
|
+
|
|
3
|
+
**플랫폼**: 웹 전용
|
|
4
|
+
|
|
5
|
+
마우스 호버 시 툴팁을 표시하는 컴포넌트입니다.
|
|
6
|
+
|
|
7
|
+
## 플랫폼 지원
|
|
8
|
+
|
|
9
|
+
- ✅ **Web**: 완전 지원
|
|
10
|
+
- ⚠️ **React Native**: children만 렌더링 (툴팁 미표시)
|
|
11
|
+
|
|
12
|
+
## Props
|
|
13
|
+
|
|
14
|
+
| Prop | Type | Required | Description |
|
|
15
|
+
|------|------|----------|-------------|
|
|
16
|
+
| children | ReactNode | ✓ | 툴팁을 표시할 요소 |
|
|
17
|
+
| content | string | ✓ | 툴팁 내용 |
|
|
18
|
+
| position | 'top' \| 'bottom' \| 'left' \| 'right' | - | 툴팁 위치 (기본: 'top') |
|
|
19
|
+
|
|
20
|
+
## 웹 사용법
|
|
21
|
+
|
|
22
|
+
```tsx
|
|
23
|
+
import { Tooltip } from '@pisodev/test-component-library';
|
|
24
|
+
|
|
25
|
+
<Tooltip content="This is a tooltip" position="top">
|
|
26
|
+
<button>Hover me</button>
|
|
27
|
+
</Tooltip>
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## React Native 대안
|
|
31
|
+
|
|
32
|
+
React Native에서는 모바일에 hover 개념이 없으므로:
|
|
33
|
+
|
|
34
|
+
1. **react-native-tooltip** 사용
|
|
35
|
+
2. **Modal + Pressable** 조합
|
|
36
|
+
3. **Long press** 제스처로 툴팁 표시
|
|
37
|
+
|
|
38
|
+
```tsx
|
|
39
|
+
// RN 대안 예시
|
|
40
|
+
import { Tooltip } from 'react-native-elements';
|
|
41
|
+
|
|
42
|
+
<Tooltip popover={<Text>Info here</Text>}>
|
|
43
|
+
<Text>Press me</Text>
|
|
44
|
+
</Tooltip>
|
|
45
|
+
```
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
.tooltipWrapper {
|
|
2
|
+
position: relative;
|
|
3
|
+
display: inline-block;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
.tooltip {
|
|
7
|
+
position: absolute;
|
|
8
|
+
background-color: rgba(0, 0, 0, 0.8);
|
|
9
|
+
color: white;
|
|
10
|
+
padding: 8px 12px;
|
|
11
|
+
border-radius: 4px;
|
|
12
|
+
font-size: 14px;
|
|
13
|
+
white-space: nowrap;
|
|
14
|
+
z-index: 1000;
|
|
15
|
+
pointer-events: none;
|
|
16
|
+
|
|
17
|
+
&.top {
|
|
18
|
+
bottom: 100%;
|
|
19
|
+
left: 50%;
|
|
20
|
+
transform: translateX(-50%);
|
|
21
|
+
margin-bottom: 8px;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
&.bottom {
|
|
25
|
+
top: 100%;
|
|
26
|
+
left: 50%;
|
|
27
|
+
transform: translateX(-50%);
|
|
28
|
+
margin-top: 8px;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
&.left {
|
|
32
|
+
right: 100%;
|
|
33
|
+
top: 50%;
|
|
34
|
+
transform: translateY(-50%);
|
|
35
|
+
margin-right: 8px;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
&.right {
|
|
39
|
+
left: 100%;
|
|
40
|
+
top: 50%;
|
|
41
|
+
transform: translateY(-50%);
|
|
42
|
+
margin-left: 8px;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import React, { useState } from 'react';
|
|
2
|
+
import { TooltipProps } from './Tooltip.types';
|
|
3
|
+
import styles from './Tooltip.module.scss';
|
|
4
|
+
|
|
5
|
+
export const Tooltip: React.FC<TooltipProps> = ({
|
|
6
|
+
children,
|
|
7
|
+
content,
|
|
8
|
+
position = 'top',
|
|
9
|
+
}) => {
|
|
10
|
+
const [isVisible, setIsVisible] = useState(false);
|
|
11
|
+
|
|
12
|
+
return (
|
|
13
|
+
<div
|
|
14
|
+
className={styles.tooltipWrapper}
|
|
15
|
+
onMouseEnter={() => setIsVisible(true)}
|
|
16
|
+
onMouseLeave={() => setIsVisible(false)}
|
|
17
|
+
>
|
|
18
|
+
{children}
|
|
19
|
+
{isVisible && (
|
|
20
|
+
<div className={`${styles.tooltip} ${styles[position]}`}>
|
|
21
|
+
{content}
|
|
22
|
+
</div>
|
|
23
|
+
)}
|
|
24
|
+
</div>
|
|
25
|
+
);
|
|
26
|
+
};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { TooltipProps } from './Tooltip.types';
|
|
3
|
+
|
|
4
|
+
// React Native에서는 Tooltip 미지원
|
|
5
|
+
// children만 렌더링하고 tooltip은 무시
|
|
6
|
+
export const Tooltip: React.FC<TooltipProps> = ({ children }) => {
|
|
7
|
+
if (process.env.NODE_ENV === 'development') {
|
|
8
|
+
console.warn(
|
|
9
|
+
'Tooltip is web-only. On React Native, consider using a modal or overlay library.'
|
|
10
|
+
);
|
|
11
|
+
}
|
|
12
|
+
return children as React.ReactElement;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
export type { TooltipProps };
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.Anchor = void 0;
|
|
7
|
+
const react_1 = __importDefault(require("react"));
|
|
8
|
+
const react_native_1 = require("react-native");
|
|
9
|
+
const Anchor = ({ children, href, title, onPress, }) => {
|
|
10
|
+
const handlePress = () => {
|
|
11
|
+
if (onPress) {
|
|
12
|
+
onPress();
|
|
13
|
+
}
|
|
14
|
+
else if (href) {
|
|
15
|
+
react_native_1.Linking.openURL(href);
|
|
16
|
+
}
|
|
17
|
+
};
|
|
18
|
+
return (<react_native_1.Pressable onPress={handlePress} accessibilityRole="link" accessibilityLabel={title} accessibilityHint={`Opens ${href}`}>
|
|
19
|
+
{({ pressed }) => (<react_native_1.Text style={[styles.anchor, pressed && styles.anchorPressed]}>
|
|
20
|
+
{children}
|
|
21
|
+
</react_native_1.Text>)}
|
|
22
|
+
</react_native_1.Pressable>);
|
|
23
|
+
};
|
|
24
|
+
exports.Anchor = Anchor;
|
|
25
|
+
const styles = react_native_1.StyleSheet.create({
|
|
26
|
+
anchor: {
|
|
27
|
+
color: '#2196f3',
|
|
28
|
+
textDecorationLine: 'none',
|
|
29
|
+
},
|
|
30
|
+
anchorPressed: {
|
|
31
|
+
color: '#1976d2',
|
|
32
|
+
textDecorationLine: 'underline',
|
|
33
|
+
},
|
|
34
|
+
});
|