podo-ui 0.5.2 → 0.6.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.
@@ -1,15 +1,19 @@
1
1
  import React from 'react';
2
- export type TooltipPlacement = 'top' | 'top-start' | 'top-end' | 'bottom' | 'bottom-start' | 'bottom-end' | 'left' | 'left-start' | 'left-end' | 'right' | 'right-start' | 'right-end';
3
- export type TooltipTheme = 'default' | 'info' | 'light';
2
+ export type TooltipVariant = 'default' | 'info';
3
+ export type TooltipPosition = 'top' | 'topLeft' | 'topRight' | 'bottom' | 'bottomLeft' | 'bottomRight' | 'left' | 'leftTop' | 'leftBottom' | 'right' | 'rightTop' | 'rightBottom';
4
4
  export interface TooltipProps {
5
- children: React.ReactElement;
5
+ /** Trigger element (button, icon, etc.) */
6
+ children: React.ReactNode;
7
+ /** Tooltip content (can include any JSX) */
6
8
  content: React.ReactNode;
7
- placement?: TooltipPlacement;
8
- theme?: TooltipTheme;
9
- trigger?: 'hover' | 'focus' | 'both';
10
- delay?: number;
9
+ /** Tooltip visual variant */
10
+ variant?: TooltipVariant;
11
+ /** Arrow position */
12
+ position?: TooltipPosition;
13
+ /** Distance from trigger element in pixels */
14
+ offset?: number;
15
+ /** Additional CSS class */
11
16
  className?: string;
12
17
  }
13
- declare const Tooltip: React.FC<TooltipProps>;
14
- export default Tooltip;
18
+ export default function Tooltip({ children, content, variant, position, offset, className, }: TooltipProps): import("react/jsx-runtime").JSX.Element;
15
19
  //# sourceMappingURL=tooltip.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"tooltip.d.ts","sourceRoot":"","sources":["../../../react/atom/tooltip.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAsC,MAAM,OAAO,CAAC;AAE3D,MAAM,MAAM,gBAAgB,GACxB,KAAK,GACL,WAAW,GACX,SAAS,GACT,QAAQ,GACR,cAAc,GACd,YAAY,GACZ,MAAM,GACN,YAAY,GACZ,UAAU,GACV,OAAO,GACP,aAAa,GACb,WAAW,CAAC;AAEhB,MAAM,MAAM,YAAY,GAAG,SAAS,GAAG,MAAM,GAAG,OAAO,CAAC;AAExD,MAAM,WAAW,YAAY;IAC3B,QAAQ,EAAE,KAAK,CAAC,YAAY,CAAC;IAC7B,OAAO,EAAE,KAAK,CAAC,SAAS,CAAC;IACzB,SAAS,CAAC,EAAE,gBAAgB,CAAC;IAC7B,KAAK,CAAC,EAAE,YAAY,CAAC;IACrB,OAAO,CAAC,EAAE,OAAO,GAAG,OAAO,GAAG,MAAM,CAAC;IACrC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,QAAA,MAAM,OAAO,EAAE,KAAK,CAAC,EAAE,CAAC,YAAY,CA0JnC,CAAC;AAEF,eAAe,OAAO,CAAC"}
1
+ {"version":3,"file":"tooltip.d.ts","sourceRoot":"","sources":["../../../react/atom/tooltip.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAmB,MAAM,OAAO,CAAC;AAGxC,MAAM,MAAM,cAAc,GAAG,SAAS,GAAG,MAAM,CAAC;AAEhD,MAAM,MAAM,eAAe,GACvB,KAAK,GACL,SAAS,GACT,UAAU,GACV,QAAQ,GACR,YAAY,GACZ,aAAa,GACb,MAAM,GACN,SAAS,GACT,YAAY,GACZ,OAAO,GACP,UAAU,GACV,aAAa,CAAC;AAElB,MAAM,WAAW,YAAY;IAC3B,2CAA2C;IAC3C,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;IAC1B,4CAA4C;IAC5C,OAAO,EAAE,KAAK,CAAC,SAAS,CAAC;IACzB,6BAA6B;IAC7B,OAAO,CAAC,EAAE,cAAc,CAAC;IACzB,qBAAqB;IACrB,QAAQ,CAAC,EAAE,eAAe,CAAC;IAC3B,8CAA8C;IAC9C,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,2BAA2B;IAC3B,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,CAAC,OAAO,UAAU,OAAO,CAAC,EAC9B,QAAQ,EACR,OAAO,EACP,OAAmB,EACnB,QAAgB,EAChB,MAAU,EACV,SAAc,GACf,EAAE,YAAY,2CA+Bd"}
@@ -1,122 +1,16 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { useState, useRef, useEffect } from 'react';
3
- const Tooltip = ({ children, content, placement = 'top', theme = 'default', trigger = 'hover', delay = 0, className = '', }) => {
4
- const [visible, setVisible] = useState(false);
5
- const [position, setPosition] = useState({ top: 0, left: 0 });
6
- const wrapperRef = useRef(null);
7
- const tooltipRef = useRef(null);
8
- const timeoutRef = useRef(null);
9
- const calculatePosition = () => {
10
- if (!wrapperRef.current || !tooltipRef.current)
11
- return;
12
- const wrapperRect = wrapperRef.current.getBoundingClientRect();
13
- const tooltipRect = tooltipRef.current.getBoundingClientRect();
14
- const gap = 8; // 화살표 높이 + 여유 공간
15
- let top = 0;
16
- let left = 0;
17
- switch (placement) {
18
- case 'top':
19
- top = -tooltipRect.height - gap;
20
- left = (wrapperRect.width - tooltipRect.width) / 2;
21
- break;
22
- case 'top-start':
23
- top = -tooltipRect.height - gap;
24
- left = 0;
25
- break;
26
- case 'top-end':
27
- top = -tooltipRect.height - gap;
28
- left = wrapperRect.width - tooltipRect.width;
29
- break;
30
- case 'bottom':
31
- top = wrapperRect.height + gap;
32
- left = (wrapperRect.width - tooltipRect.width) / 2;
33
- break;
34
- case 'bottom-start':
35
- top = wrapperRect.height + gap;
36
- left = 0;
37
- break;
38
- case 'bottom-end':
39
- top = wrapperRect.height + gap;
40
- left = wrapperRect.width - tooltipRect.width;
41
- break;
42
- case 'left':
43
- top = (wrapperRect.height - tooltipRect.height) / 2;
44
- left = -tooltipRect.width - gap;
45
- break;
46
- case 'left-start':
47
- top = 0;
48
- left = -tooltipRect.width - gap;
49
- break;
50
- case 'left-end':
51
- top = wrapperRect.height - tooltipRect.height;
52
- left = -tooltipRect.width - gap;
53
- break;
54
- case 'right':
55
- top = (wrapperRect.height - tooltipRect.height) / 2;
56
- left = wrapperRect.width + gap;
57
- break;
58
- case 'right-start':
59
- top = 0;
60
- left = wrapperRect.width + gap;
61
- break;
62
- case 'right-end':
63
- top = wrapperRect.height - tooltipRect.height;
64
- left = wrapperRect.width + gap;
65
- break;
66
- }
67
- setPosition({ top, left });
68
- };
69
- const showTooltip = () => {
70
- if (delay > 0) {
71
- timeoutRef.current = setTimeout(() => {
72
- setVisible(true);
73
- }, delay);
74
- }
75
- else {
76
- setVisible(true);
77
- }
78
- };
79
- const hideTooltip = () => {
80
- if (timeoutRef.current) {
81
- clearTimeout(timeoutRef.current);
82
- timeoutRef.current = null;
83
- }
84
- setVisible(false);
85
- };
86
- useEffect(() => {
87
- if (visible) {
88
- calculatePosition();
89
- }
90
- }, [visible]);
91
- useEffect(() => {
92
- return () => {
93
- if (timeoutRef.current) {
94
- clearTimeout(timeoutRef.current);
95
- }
96
- };
97
- }, []);
98
- const tooltipClasses = [
99
- 'tooltip',
100
- placement,
101
- theme !== 'default' && theme,
2
+ import { useState } from 'react';
3
+ import styles from './tooltip.module.scss';
4
+ export default function Tooltip({ children, content, variant = 'default', position = 'top', offset = 8, className = '', }) {
5
+ const [isVisible, setIsVisible] = useState(false);
6
+ const variantClass = variant === 'default' ? styles.variantDefault : styles.variantInfo;
7
+ const tooltipClassNames = [
8
+ styles.tooltipBox,
9
+ variantClass,
10
+ styles[position],
102
11
  className,
103
12
  ]
104
13
  .filter(Boolean)
105
14
  .join(' ');
106
- const handlers = {};
107
- if (trigger === 'hover' || trigger === 'both') {
108
- handlers.onMouseEnter = showTooltip;
109
- handlers.onMouseLeave = hideTooltip;
110
- }
111
- if (trigger === 'focus' || trigger === 'both') {
112
- handlers.onFocus = showTooltip;
113
- handlers.onBlur = hideTooltip;
114
- }
115
- return (_jsxs("div", { className: "tooltip-wrapper", ref: wrapperRef, ...handlers, children: [children, _jsx("div", { ref: tooltipRef, className: tooltipClasses, style: {
116
- top: `${position.top}px`,
117
- left: `${position.left}px`,
118
- opacity: visible ? 1 : 0,
119
- visibility: visible ? 'visible' : 'hidden',
120
- }, children: content })] }));
121
- };
122
- export default Tooltip;
15
+ return (_jsxs("div", { className: styles.tooltipWrapper, onMouseEnter: () => setIsVisible(true), onMouseLeave: () => setIsVisible(false), children: [children, isVisible && (_jsx("div", { className: tooltipClassNames, style: { '--tooltip-offset': `${offset}px` }, children: content }))] }));
16
+ }
@@ -0,0 +1,227 @@
1
+ @use '../../../mixin.scss' as *;
2
+
3
+ .tooltipWrapper {
4
+ position: relative;
5
+ display: inline-flex;
6
+ }
7
+
8
+ .tooltipBox {
9
+ position: absolute;
10
+ padding: s(2) s(4);
11
+ border-radius: r(3);
12
+ font-family: 'Pretendard', -apple-system, sans-serif;
13
+ font-size: 14px;
14
+ font-weight: 400;
15
+ line-height: 22.4px;
16
+ white-space: nowrap;
17
+ z-index: 1000;
18
+
19
+ // Default variant
20
+ &.variantDefault {
21
+ background-color: color(default-deep);
22
+ color: color(text-action-reverse) !important;
23
+ }
24
+
25
+ // Info variant
26
+ &.variantInfo {
27
+ background-color: color(info);
28
+ color: color(text-action-reverse) !important;
29
+ }
30
+
31
+ // Arrow styles
32
+ &::before {
33
+ content: '';
34
+ position: absolute;
35
+ width: 12px;
36
+ height: 12px;
37
+ transform-origin: center;
38
+ }
39
+
40
+ // Top positions
41
+ &.top,
42
+ &.topLeft,
43
+ &.topRight {
44
+ bottom: calc(100% + var(--tooltip-offset, 8px));
45
+
46
+ &::before {
47
+ bottom: -6px;
48
+ border-radius: 0 0 2px 0;
49
+ transform: rotate(45deg);
50
+ }
51
+
52
+ &.variantDefault::before {
53
+ background-color: color(default-deep);
54
+ }
55
+
56
+ &.variantInfo::before {
57
+ background-color: color(info);
58
+ }
59
+ }
60
+
61
+ &.top {
62
+ left: 50%;
63
+ transform: translateX(-50%);
64
+
65
+ &::before {
66
+ left: 50%;
67
+ transform: translateX(-50%) rotate(45deg);
68
+ }
69
+ }
70
+
71
+ &.topLeft {
72
+ left: 0;
73
+
74
+ &::before {
75
+ left: 8px;
76
+ }
77
+ }
78
+
79
+ &.topRight {
80
+ right: 0;
81
+
82
+ &::before {
83
+ right: 8px;
84
+ }
85
+ }
86
+
87
+ // Bottom positions
88
+ &.bottom,
89
+ &.bottomLeft,
90
+ &.bottomRight {
91
+ top: calc(100% + var(--tooltip-offset, 8px));
92
+
93
+ &::before {
94
+ top: -6px;
95
+ border-radius: 2px 0 0 0;
96
+ transform: rotate(45deg);
97
+ }
98
+
99
+ &.variantDefault::before {
100
+ background-color: color(default-deep);
101
+ }
102
+
103
+ &.variantInfo::before {
104
+ background-color: color(info);
105
+ }
106
+ }
107
+
108
+ &.bottom {
109
+ left: 50%;
110
+ transform: translateX(-50%);
111
+
112
+ &::before {
113
+ left: 50%;
114
+ transform: translateX(-50%) rotate(45deg);
115
+ }
116
+ }
117
+
118
+ &.bottomLeft {
119
+ left: 0;
120
+
121
+ &::before {
122
+ left: 8px;
123
+ }
124
+ }
125
+
126
+ &.bottomRight {
127
+ right: 0;
128
+
129
+ &::before {
130
+ right: 8px;
131
+ }
132
+ }
133
+
134
+ // Left positions
135
+ &.left,
136
+ &.leftTop,
137
+ &.leftBottom {
138
+ right: calc(100% + var(--tooltip-offset, 8px));
139
+
140
+ &::before {
141
+ right: -6px;
142
+ border-radius: 0 2px 0 0;
143
+ transform: rotate(45deg);
144
+ }
145
+
146
+ &.variantDefault::before {
147
+ background-color: color(default-deep);
148
+ }
149
+
150
+ &.variantInfo::before {
151
+ background-color: color(info);
152
+ }
153
+ }
154
+
155
+ &.left {
156
+ top: 50%;
157
+ transform: translateY(-50%);
158
+
159
+ &::before {
160
+ top: 50%;
161
+ transform: translateY(-50%) rotate(45deg);
162
+ }
163
+ }
164
+
165
+ &.leftTop {
166
+ top: 0;
167
+
168
+ &::before {
169
+ top: 8px;
170
+ }
171
+ }
172
+
173
+ &.leftBottom {
174
+ bottom: 0;
175
+
176
+ &::before {
177
+ bottom: 8px;
178
+ }
179
+ }
180
+
181
+ // Right positions
182
+ &.right,
183
+ &.rightTop,
184
+ &.rightBottom {
185
+ left: calc(100% + var(--tooltip-offset, 8px));
186
+
187
+ &::before {
188
+ left: -6px;
189
+ border-radius: 0 0 0 2px;
190
+ transform: rotate(45deg);
191
+ }
192
+
193
+ &.variantDefault::before {
194
+ background-color: color(default-deep);
195
+ }
196
+
197
+ &.variantInfo::before {
198
+ background-color: color(info);
199
+ }
200
+ }
201
+
202
+ &.right {
203
+ top: 50%;
204
+ transform: translateY(-50%);
205
+
206
+ &::before {
207
+ top: 50%;
208
+ transform: translateY(-50%) rotate(45deg);
209
+ }
210
+ }
211
+
212
+ &.rightTop {
213
+ top: 0;
214
+
215
+ &::before {
216
+ top: 8px;
217
+ }
218
+ }
219
+
220
+ &.rightBottom {
221
+ bottom: 0;
222
+
223
+ &::before {
224
+ bottom: 8px;
225
+ }
226
+ }
227
+ }
package/global.scss CHANGED
@@ -43,7 +43,6 @@
43
43
  Atom
44
44
  */
45
45
  @forward './scss/atom/chip';
46
- @forward './scss/atom/tooltip';
47
46
 
48
47
  /*
49
48
  Form
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "podo-ui",
3
- "version": "0.5.2",
3
+ "version": "0.6.0",
4
4
  "type": "module",
5
5
  "author": "hada0127 <work@tarucy.net>",
6
6
  "license": "MIT",
@@ -1,273 +0,0 @@
1
- @use '../color/function' as *;
2
- @use '../layout/bg-elevation' as *;
3
-
4
- .tooltip {
5
- position: absolute;
6
- z-index: 1000;
7
- padding: 4px 8px;
8
- font-family: var(--font-family);
9
- font-size: 12px;
10
- font-weight: 400;
11
- line-height: 1.6;
12
- border-radius: 4px;
13
- white-space: nowrap;
14
- box-shadow: shadow(2);
15
- pointer-events: none;
16
-
17
- // Default theme (dark gray)
18
- background-color: color('default-deep');
19
- color: color('default-deep-reverse');
20
-
21
- // Arrow (화살표)
22
- &::before {
23
- content: '';
24
- position: absolute;
25
- width: 0;
26
- height: 0;
27
- border: 4px solid transparent;
28
- }
29
-
30
- // Theme: info (blue)
31
- &.info {
32
- background-color: color('info');
33
- color: color('info-reverse');
34
- }
35
-
36
- // Theme: light (white with border)
37
- &.light {
38
- background-color: color('bg');
39
- color: color('text-body');
40
- border: 1px solid color('border');
41
- box-shadow: shadow(1);
42
- }
43
-
44
- // Direction: top
45
- &.top {
46
- &::before {
47
- bottom: -8px;
48
- left: 50%;
49
- transform: translateX(-50%);
50
- border-top-color: color('default-deep');
51
- border-bottom: none;
52
- }
53
-
54
- &.info::before {
55
- border-top-color: color('info');
56
- }
57
-
58
- &.light::before {
59
- border-top-color: color('border');
60
- }
61
- }
62
-
63
- &.top-start {
64
- &::before {
65
- bottom: -8px;
66
- left: 12px;
67
- border-top-color: color('default-deep');
68
- border-bottom: none;
69
- }
70
-
71
- &.info::before {
72
- border-top-color: color('info');
73
- }
74
-
75
- &.light::before {
76
- border-top-color: color('border');
77
- }
78
- }
79
-
80
- &.top-end {
81
- &::before {
82
- bottom: -8px;
83
- right: 12px;
84
- border-top-color: color('default-deep');
85
- border-bottom: none;
86
- }
87
-
88
- &.info::before {
89
- border-top-color: color('info');
90
- }
91
-
92
- &.light::before {
93
- border-top-color: color('border');
94
- }
95
- }
96
-
97
- // Direction: bottom
98
- &.bottom {
99
- &::before {
100
- top: -8px;
101
- left: 50%;
102
- transform: translateX(-50%);
103
- border-bottom-color: color('default-deep');
104
- border-top: none;
105
- }
106
-
107
- &.info::before {
108
- border-bottom-color: color('info');
109
- }
110
-
111
- &.light::before {
112
- border-bottom-color: color('border');
113
- }
114
- }
115
-
116
- &.bottom-start {
117
- &::before {
118
- top: -8px;
119
- left: 12px;
120
- border-bottom-color: color('default-deep');
121
- border-top: none;
122
- }
123
-
124
- &.info::before {
125
- border-bottom-color: color('info');
126
- }
127
-
128
- &.light::before {
129
- border-bottom-color: color('border');
130
- }
131
- }
132
-
133
- &.bottom-end {
134
- &::before {
135
- top: -8px;
136
- right: 12px;
137
- border-bottom-color: color('default-deep');
138
- border-top: none;
139
- }
140
-
141
- &.info::before {
142
- border-bottom-color: color('info');
143
- }
144
-
145
- &.light::before {
146
- border-bottom-color: color('border');
147
- }
148
- }
149
-
150
- // Direction: left
151
- &.left {
152
- &::before {
153
- right: -8px;
154
- top: 50%;
155
- transform: translateY(-50%);
156
- border-left-color: color('default-deep');
157
- border-right: none;
158
- }
159
-
160
- &.info::before {
161
- border-left-color: color('info');
162
- }
163
-
164
- &.light::before {
165
- border-left-color: color('border');
166
- }
167
- }
168
-
169
- &.left-start {
170
- &::before {
171
- right: -8px;
172
- top: 8px;
173
- border-left-color: color('default-deep');
174
- border-right: none;
175
- }
176
-
177
- &.info::before {
178
- border-left-color: color('info');
179
- }
180
-
181
- &.light::before {
182
- border-left-color: color('border');
183
- }
184
- }
185
-
186
- &.left-end {
187
- &::before {
188
- right: -8px;
189
- bottom: 8px;
190
- border-left-color: color('default-deep');
191
- border-right: none;
192
- }
193
-
194
- &.info::before {
195
- border-left-color: color('info');
196
- }
197
-
198
- &.light::before {
199
- border-left-color: color('border');
200
- }
201
- }
202
-
203
- // Direction: right
204
- &.right {
205
- &::before {
206
- left: -8px;
207
- top: 50%;
208
- transform: translateY(-50%);
209
- border-right-color: color('default-deep');
210
- border-left: none;
211
- }
212
-
213
- &.info::before {
214
- border-right-color: color('info');
215
- }
216
-
217
- &.light::before {
218
- border-right-color: color('border');
219
- }
220
- }
221
-
222
- &.right-start {
223
- &::before {
224
- left: -8px;
225
- top: 8px;
226
- border-right-color: color('default-deep');
227
- border-left: none;
228
- }
229
-
230
- &.info::before {
231
- border-right-color: color('info');
232
- }
233
-
234
- &.light::before {
235
- border-right-color: color('border');
236
- }
237
- }
238
-
239
- &.right-end {
240
- &::before {
241
- left: -8px;
242
- bottom: 8px;
243
- border-right-color: color('default-deep');
244
- border-left: none;
245
- }
246
-
247
- &.info::before {
248
- border-right-color: color('info');
249
- }
250
-
251
- &.light::before {
252
- border-right-color: color('border');
253
- }
254
- }
255
- }
256
-
257
- // Tooltip wrapper for hover/focus trigger
258
- .tooltip-wrapper {
259
- position: relative;
260
- display: inline-flex;
261
-
262
- .tooltip {
263
- opacity: 0;
264
- visibility: hidden;
265
- transition: opacity 0.2s, visibility 0.2s;
266
- }
267
-
268
- &:hover .tooltip,
269
- &:focus-within .tooltip {
270
- opacity: 1;
271
- visibility: visible;
272
- }
273
- }