@x-plat/design-system 0.7.2 → 0.8.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.
Files changed (41) hide show
  1. package/dist/components/AutoResizeTextArea/index.css +2 -0
  2. package/dist/components/Button/index.css +2 -1
  3. package/dist/components/Button/index.d.cts +6 -0
  4. package/dist/components/Button/index.d.ts +6 -0
  5. package/dist/components/Chart/index.cjs +8 -1
  6. package/dist/components/Chart/index.js +8 -1
  7. package/dist/components/ChatInput/index.css +4 -0
  8. package/dist/components/DatePicker/index.css +4 -1
  9. package/dist/components/Input/index.css +2 -0
  10. package/dist/components/Link/index.cjs +64 -0
  11. package/dist/components/Link/index.css +25 -0
  12. package/dist/components/Link/index.d.cts +27 -0
  13. package/dist/components/Link/index.d.ts +27 -0
  14. package/dist/components/Link/index.js +37 -0
  15. package/dist/components/LinkButton/index.cjs +64 -0
  16. package/dist/components/LinkButton/index.css +106 -0
  17. package/dist/components/LinkButton/index.d.cts +28 -0
  18. package/dist/components/LinkButton/index.d.ts +28 -0
  19. package/dist/components/LinkButton/index.js +37 -0
  20. package/dist/components/TextArea/index.css +2 -0
  21. package/dist/components/Tooltip/index.cjs +77 -32
  22. package/dist/components/Tooltip/index.d.cts +10 -2
  23. package/dist/components/Tooltip/index.d.ts +10 -2
  24. package/dist/components/Tooltip/index.js +77 -32
  25. package/dist/components/index.cjs +247 -153
  26. package/dist/components/index.css +36 -1
  27. package/dist/components/index.d.cts +2 -0
  28. package/dist/components/index.d.ts +2 -0
  29. package/dist/components/index.js +222 -130
  30. package/dist/index.cjs +266 -172
  31. package/dist/index.css +36 -1
  32. package/dist/index.d.cts +2 -0
  33. package/dist/index.d.ts +2 -0
  34. package/dist/index.js +239 -147
  35. package/guidelines/AGENT_PROMPT.md +2 -2
  36. package/guidelines/Guidelines.md +2 -1
  37. package/guidelines/components/button.md +54 -0
  38. package/guidelines/components/card.md +15 -19
  39. package/guidelines/components/link.md +35 -0
  40. package/guidelines/components/overlay.md +30 -1
  41. package/package.json +1 -1
@@ -70,13 +70,15 @@ var clsx_default = clsx;
70
70
  // src/components/Tooltip/Tooltip.tsx
71
71
  var import_jsx_runtime2 = require("react/jsx-runtime");
72
72
  var OFFSET = 12;
73
+ var GAP = 8;
73
74
  var SHOW_DELAY = 300;
74
75
  var Tooltip = (props) => {
75
76
  const {
76
- type = "dark",
77
+ children,
77
78
  title,
78
79
  description,
79
- children,
80
+ type = "dark",
81
+ follow = false,
80
82
  disabled = false
81
83
  } = props;
82
84
  const triggerRef = import_react2.default.useRef(null);
@@ -84,49 +86,91 @@ var Tooltip = (props) => {
84
86
  const [visible, setVisible] = import_react2.default.useState(false);
85
87
  const [pos, setPos] = import_react2.default.useState({ left: 0, top: 0 });
86
88
  const delayTimer = import_react2.default.useRef(0);
87
- const calculatePos = import_react2.default.useCallback((clientX, clientY) => {
89
+ const rafRef = import_react2.default.useRef(0);
90
+ const cursor = import_react2.default.useRef(null);
91
+ const clamp = import_react2.default.useCallback((left, top, w, h) => {
92
+ const vw = window.innerWidth;
93
+ const vh = window.innerHeight;
94
+ return {
95
+ left: Math.min(Math.max(GAP, left), Math.max(GAP, vw - w - GAP)),
96
+ top: Math.min(Math.max(GAP, top), Math.max(GAP, vh - h - GAP))
97
+ };
98
+ }, []);
99
+ const placeAtCursor = import_react2.default.useCallback((x, y) => {
88
100
  const el = tooltipRef.current;
89
101
  if (!el) return;
90
102
  const w = el.offsetWidth;
91
103
  const h = el.offsetHeight;
92
- const vw = window.innerWidth;
93
- let left = clientX + OFFSET;
94
- let top = clientY - h - OFFSET;
95
- if (left + w > vw - 8) left = clientX - w - OFFSET;
96
- if (top < 8) top = clientY + OFFSET;
97
- if (left < 8) left = 8;
98
- setPos({ left, top });
99
- }, []);
100
- const handleMouseEnter = import_react2.default.useCallback(() => {
104
+ let left = x + OFFSET;
105
+ let top = y - h - OFFSET;
106
+ if (left + w > window.innerWidth - GAP) left = x - w - OFFSET;
107
+ if (top < GAP) top = y + OFFSET;
108
+ setPos(clamp(left, top, w, h));
109
+ }, [clamp]);
110
+ const placeAtTrigger = import_react2.default.useCallback(() => {
111
+ const el = tooltipRef.current;
112
+ const trigger = triggerRef.current;
113
+ if (!el || !trigger) return;
114
+ const w = el.offsetWidth;
115
+ const h = el.offsetHeight;
116
+ const rect = trigger.getBoundingClientRect();
117
+ let top = rect.top - h - GAP;
118
+ if (top < GAP) top = rect.bottom + GAP;
119
+ const left = rect.left + rect.width / 2 - w / 2;
120
+ setPos(clamp(left, top, w, h));
121
+ }, [clamp]);
122
+ const reposition = import_react2.default.useCallback(() => {
123
+ if (follow && cursor.current) {
124
+ placeAtCursor(cursor.current.x, cursor.current.y);
125
+ } else {
126
+ placeAtTrigger();
127
+ }
128
+ }, [follow, placeAtCursor, placeAtTrigger]);
129
+ const handleMouseEnter = import_react2.default.useCallback((e) => {
101
130
  if (disabled) return;
102
- delayTimer.current = window.setTimeout(() => {
103
- setVisible(true);
104
- }, SHOW_DELAY);
131
+ cursor.current = { x: e.clientX, y: e.clientY };
132
+ delayTimer.current = window.setTimeout(() => setVisible(true), SHOW_DELAY);
105
133
  }, [disabled]);
106
134
  const handleMouseMove = import_react2.default.useCallback((e) => {
107
- if (!visible) return;
108
- calculatePos(e.clientX, e.clientY);
109
- }, [visible, calculatePos]);
110
- const handleMouseLeave = import_react2.default.useCallback(() => {
111
- window.clearTimeout(delayTimer.current);
112
- setVisible(false);
113
- }, []);
114
- const handleClick = import_react2.default.useCallback(() => {
135
+ cursor.current = { x: e.clientX, y: e.clientY };
136
+ if (!visible || !follow) return;
137
+ cancelAnimationFrame(rafRef.current);
138
+ rafRef.current = requestAnimationFrame(() => {
139
+ if (cursor.current) placeAtCursor(cursor.current.x, cursor.current.y);
140
+ });
141
+ }, [visible, follow, placeAtCursor]);
142
+ const close = import_react2.default.useCallback(() => {
115
143
  window.clearTimeout(delayTimer.current);
144
+ cancelAnimationFrame(rafRef.current);
116
145
  setVisible(false);
117
146
  }, []);
147
+ const handleMouseLeave = import_react2.default.useCallback(() => {
148
+ cursor.current = null;
149
+ close();
150
+ }, [close]);
118
151
  const handleFocus = import_react2.default.useCallback(() => {
119
152
  if (disabled) return;
153
+ cursor.current = null;
120
154
  setVisible(true);
121
155
  }, [disabled]);
122
- const handleBlur = import_react2.default.useCallback(() => {
123
- setVisible(false);
124
- }, []);
125
156
  import_react2.default.useLayoutEffect(() => {
126
- if (!visible || !triggerRef.current) return;
127
- const rect = triggerRef.current.getBoundingClientRect();
128
- calculatePos(rect.right, rect.top);
129
- }, [visible, calculatePos]);
157
+ if (!visible) return;
158
+ reposition();
159
+ }, [visible, reposition]);
160
+ import_react2.default.useEffect(() => {
161
+ if (!visible || follow) return;
162
+ const onChange = () => placeAtTrigger();
163
+ window.addEventListener("scroll", onChange, true);
164
+ window.addEventListener("resize", onChange);
165
+ return () => {
166
+ window.removeEventListener("scroll", onChange, true);
167
+ window.removeEventListener("resize", onChange);
168
+ };
169
+ }, [visible, follow, placeAtTrigger]);
170
+ import_react2.default.useEffect(() => () => {
171
+ window.clearTimeout(delayTimer.current);
172
+ cancelAnimationFrame(rafRef.current);
173
+ }, []);
130
174
  if (!title && !description) return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_jsx_runtime2.Fragment, { children });
131
175
  return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
132
176
  "div",
@@ -136,15 +180,16 @@ var Tooltip = (props) => {
136
180
  onMouseEnter: handleMouseEnter,
137
181
  onMouseMove: handleMouseMove,
138
182
  onMouseLeave: handleMouseLeave,
139
- onClick: handleClick,
183
+ onClick: close,
140
184
  onFocus: handleFocus,
141
- onBlur: handleBlur,
185
+ onBlur: close,
142
186
  children: [
143
187
  children,
144
188
  visible && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Portal_default, { children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
145
189
  "div",
146
190
  {
147
191
  ref: tooltipRef,
192
+ role: "tooltip",
148
193
  className: clsx_default("lib-xplat-tooltip", type),
149
194
  style: { position: "fixed", left: pos.left, top: pos.top },
150
195
  children: [
@@ -1,10 +1,18 @@
1
1
  import React from 'react';
2
2
 
3
3
  interface TooltipProps {
4
- type?: "dark" | "light";
4
+ children: React.ReactNode;
5
5
  title?: React.ReactNode;
6
6
  description?: React.ReactNode;
7
- children: React.ReactNode;
7
+ type?: "dark" | "light";
8
+ /**
9
+ * 커서를 따라다닌다. 기본값은 `false` — 트리거 요소 기준으로 고정된다
10
+ * (PopOver/Dropdown/Select 와 같은 방식).
11
+ *
12
+ * 넓은 영역 안에서 **가리키는 지점**이 의미를 가질 때만 켠다. 버튼이나
13
+ * 아이콘처럼 대상이 작은 경우는 고정이 읽기 편하다.
14
+ */
15
+ follow?: boolean;
8
16
  disabled?: boolean;
9
17
  }
10
18
  declare const Tooltip: React.FC<TooltipProps>;
@@ -1,10 +1,18 @@
1
1
  import React from 'react';
2
2
 
3
3
  interface TooltipProps {
4
- type?: "dark" | "light";
4
+ children: React.ReactNode;
5
5
  title?: React.ReactNode;
6
6
  description?: React.ReactNode;
7
- children: React.ReactNode;
7
+ type?: "dark" | "light";
8
+ /**
9
+ * 커서를 따라다닌다. 기본값은 `false` — 트리거 요소 기준으로 고정된다
10
+ * (PopOver/Dropdown/Select 와 같은 방식).
11
+ *
12
+ * 넓은 영역 안에서 **가리키는 지점**이 의미를 가질 때만 켠다. 버튼이나
13
+ * 아이콘처럼 대상이 작은 경우는 고정이 읽기 편하다.
14
+ */
15
+ follow?: boolean;
8
16
  disabled?: boolean;
9
17
  }
10
18
  declare const Tooltip: React.FC<TooltipProps>;
@@ -34,13 +34,15 @@ var clsx_default = clsx;
34
34
  // src/components/Tooltip/Tooltip.tsx
35
35
  import { Fragment, jsx as jsx2, jsxs } from "react/jsx-runtime";
36
36
  var OFFSET = 12;
37
+ var GAP = 8;
37
38
  var SHOW_DELAY = 300;
38
39
  var Tooltip = (props) => {
39
40
  const {
40
- type = "dark",
41
+ children,
41
42
  title,
42
43
  description,
43
- children,
44
+ type = "dark",
45
+ follow = false,
44
46
  disabled = false
45
47
  } = props;
46
48
  const triggerRef = React2.useRef(null);
@@ -48,49 +50,91 @@ var Tooltip = (props) => {
48
50
  const [visible, setVisible] = React2.useState(false);
49
51
  const [pos, setPos] = React2.useState({ left: 0, top: 0 });
50
52
  const delayTimer = React2.useRef(0);
51
- const calculatePos = React2.useCallback((clientX, clientY) => {
53
+ const rafRef = React2.useRef(0);
54
+ const cursor = React2.useRef(null);
55
+ const clamp = React2.useCallback((left, top, w, h) => {
56
+ const vw = window.innerWidth;
57
+ const vh = window.innerHeight;
58
+ return {
59
+ left: Math.min(Math.max(GAP, left), Math.max(GAP, vw - w - GAP)),
60
+ top: Math.min(Math.max(GAP, top), Math.max(GAP, vh - h - GAP))
61
+ };
62
+ }, []);
63
+ const placeAtCursor = React2.useCallback((x, y) => {
52
64
  const el = tooltipRef.current;
53
65
  if (!el) return;
54
66
  const w = el.offsetWidth;
55
67
  const h = el.offsetHeight;
56
- const vw = window.innerWidth;
57
- let left = clientX + OFFSET;
58
- let top = clientY - h - OFFSET;
59
- if (left + w > vw - 8) left = clientX - w - OFFSET;
60
- if (top < 8) top = clientY + OFFSET;
61
- if (left < 8) left = 8;
62
- setPos({ left, top });
63
- }, []);
64
- const handleMouseEnter = React2.useCallback(() => {
68
+ let left = x + OFFSET;
69
+ let top = y - h - OFFSET;
70
+ if (left + w > window.innerWidth - GAP) left = x - w - OFFSET;
71
+ if (top < GAP) top = y + OFFSET;
72
+ setPos(clamp(left, top, w, h));
73
+ }, [clamp]);
74
+ const placeAtTrigger = React2.useCallback(() => {
75
+ const el = tooltipRef.current;
76
+ const trigger = triggerRef.current;
77
+ if (!el || !trigger) return;
78
+ const w = el.offsetWidth;
79
+ const h = el.offsetHeight;
80
+ const rect = trigger.getBoundingClientRect();
81
+ let top = rect.top - h - GAP;
82
+ if (top < GAP) top = rect.bottom + GAP;
83
+ const left = rect.left + rect.width / 2 - w / 2;
84
+ setPos(clamp(left, top, w, h));
85
+ }, [clamp]);
86
+ const reposition = React2.useCallback(() => {
87
+ if (follow && cursor.current) {
88
+ placeAtCursor(cursor.current.x, cursor.current.y);
89
+ } else {
90
+ placeAtTrigger();
91
+ }
92
+ }, [follow, placeAtCursor, placeAtTrigger]);
93
+ const handleMouseEnter = React2.useCallback((e) => {
65
94
  if (disabled) return;
66
- delayTimer.current = window.setTimeout(() => {
67
- setVisible(true);
68
- }, SHOW_DELAY);
95
+ cursor.current = { x: e.clientX, y: e.clientY };
96
+ delayTimer.current = window.setTimeout(() => setVisible(true), SHOW_DELAY);
69
97
  }, [disabled]);
70
98
  const handleMouseMove = React2.useCallback((e) => {
71
- if (!visible) return;
72
- calculatePos(e.clientX, e.clientY);
73
- }, [visible, calculatePos]);
74
- const handleMouseLeave = React2.useCallback(() => {
75
- window.clearTimeout(delayTimer.current);
76
- setVisible(false);
77
- }, []);
78
- const handleClick = React2.useCallback(() => {
99
+ cursor.current = { x: e.clientX, y: e.clientY };
100
+ if (!visible || !follow) return;
101
+ cancelAnimationFrame(rafRef.current);
102
+ rafRef.current = requestAnimationFrame(() => {
103
+ if (cursor.current) placeAtCursor(cursor.current.x, cursor.current.y);
104
+ });
105
+ }, [visible, follow, placeAtCursor]);
106
+ const close = React2.useCallback(() => {
79
107
  window.clearTimeout(delayTimer.current);
108
+ cancelAnimationFrame(rafRef.current);
80
109
  setVisible(false);
81
110
  }, []);
111
+ const handleMouseLeave = React2.useCallback(() => {
112
+ cursor.current = null;
113
+ close();
114
+ }, [close]);
82
115
  const handleFocus = React2.useCallback(() => {
83
116
  if (disabled) return;
117
+ cursor.current = null;
84
118
  setVisible(true);
85
119
  }, [disabled]);
86
- const handleBlur = React2.useCallback(() => {
87
- setVisible(false);
88
- }, []);
89
120
  React2.useLayoutEffect(() => {
90
- if (!visible || !triggerRef.current) return;
91
- const rect = triggerRef.current.getBoundingClientRect();
92
- calculatePos(rect.right, rect.top);
93
- }, [visible, calculatePos]);
121
+ if (!visible) return;
122
+ reposition();
123
+ }, [visible, reposition]);
124
+ React2.useEffect(() => {
125
+ if (!visible || follow) return;
126
+ const onChange = () => placeAtTrigger();
127
+ window.addEventListener("scroll", onChange, true);
128
+ window.addEventListener("resize", onChange);
129
+ return () => {
130
+ window.removeEventListener("scroll", onChange, true);
131
+ window.removeEventListener("resize", onChange);
132
+ };
133
+ }, [visible, follow, placeAtTrigger]);
134
+ React2.useEffect(() => () => {
135
+ window.clearTimeout(delayTimer.current);
136
+ cancelAnimationFrame(rafRef.current);
137
+ }, []);
94
138
  if (!title && !description) return /* @__PURE__ */ jsx2(Fragment, { children });
95
139
  return /* @__PURE__ */ jsxs(
96
140
  "div",
@@ -100,15 +144,16 @@ var Tooltip = (props) => {
100
144
  onMouseEnter: handleMouseEnter,
101
145
  onMouseMove: handleMouseMove,
102
146
  onMouseLeave: handleMouseLeave,
103
- onClick: handleClick,
147
+ onClick: close,
104
148
  onFocus: handleFocus,
105
- onBlur: handleBlur,
149
+ onBlur: close,
106
150
  children: [
107
151
  children,
108
152
  visible && /* @__PURE__ */ jsx2(Portal_default, { children: /* @__PURE__ */ jsxs(
109
153
  "div",
110
154
  {
111
155
  ref: tooltipRef,
156
+ role: "tooltip",
112
157
  className: clsx_default("lib-xplat-tooltip", type),
113
158
  style: { position: "fixed", left: pos.left, top: pos.top },
114
159
  children: [