podo-ui 0.5.0 → 0.5.2

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/dist/index.d.ts CHANGED
@@ -3,6 +3,8 @@ import Textarea from './react/atom/textarea';
3
3
  import Editor from './react/atom/editor';
4
4
  import EditorView from './react/atom/editor-view';
5
5
  import Avatar from './react/atom/avatar';
6
+ import Chip from './react/atom/chip';
7
+ import Tooltip from './react/atom/tooltip';
6
8
  import Pagination from './react/molecule/pagination';
7
9
  import Field from './react/molecule/field';
8
10
  declare const Form: {
@@ -13,5 +15,5 @@ declare const Form: {
13
15
  Field: ({ label, labelClass, required, helper, helperClass, children, validator, value, setClassName, className, }: import("./react/molecule/field").FieldProps) => import("react/jsx-runtime").JSX.Element;
14
16
  };
15
17
  export default Form;
16
- export { Input, Textarea, Editor, EditorView, Avatar, Pagination, Field };
18
+ export { Input, Textarea, Editor, EditorView, Avatar, Chip, Tooltip, Pagination, Field };
17
19
  //# 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,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"}
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,IAAI,MAAM,mBAAmB,CAAC;AACrC,OAAO,OAAO,MAAM,sBAAsB,CAAC;AAC3C,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,IAAI,EAAE,OAAO,EAAE,UAAU,EAAE,KAAK,EAAE,CAAC"}
package/dist/index.js CHANGED
@@ -3,6 +3,8 @@ import Textarea from './react/atom/textarea';
3
3
  import Editor from './react/atom/editor';
4
4
  import EditorView from './react/atom/editor-view';
5
5
  import Avatar from './react/atom/avatar';
6
+ import Chip from './react/atom/chip';
7
+ import Tooltip from './react/atom/tooltip';
6
8
  import Pagination from './react/molecule/pagination';
7
9
  import Field from './react/molecule/field';
8
10
  const Form = {
@@ -13,4 +15,4 @@ const Form = {
13
15
  Field,
14
16
  };
15
17
  export default Form;
16
- export { Input, Textarea, Editor, EditorView, Avatar, Pagination, Field };
18
+ export { Input, Textarea, Editor, EditorView, Avatar, Chip, Tooltip, Pagination, Field };
@@ -0,0 +1,15 @@
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';
4
+ export interface TooltipProps {
5
+ children: React.ReactElement;
6
+ content: React.ReactNode;
7
+ placement?: TooltipPlacement;
8
+ theme?: TooltipTheme;
9
+ trigger?: 'hover' | 'focus' | 'both';
10
+ delay?: number;
11
+ className?: string;
12
+ }
13
+ declare const Tooltip: React.FC<TooltipProps>;
14
+ export default Tooltip;
15
+ //# sourceMappingURL=tooltip.d.ts.map
@@ -0,0 +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"}
@@ -0,0 +1,122 @@
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,
102
+ className,
103
+ ]
104
+ .filter(Boolean)
105
+ .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;
package/global.scss CHANGED
@@ -43,6 +43,7 @@
43
43
  Atom
44
44
  */
45
45
  @forward './scss/atom/chip';
46
+ @forward './scss/atom/tooltip';
46
47
 
47
48
  /*
48
49
  Form
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "podo-ui",
3
- "version": "0.5.0",
3
+ "version": "0.5.2",
4
4
  "type": "module",
5
5
  "author": "hada0127 <work@tarucy.net>",
6
6
  "license": "MIT",
@@ -0,0 +1,273 @@
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
+ }