@nurimedia/react-ui 0.1.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/LICENSE +21 -0
- package/README.md +274 -0
- package/dist/AlertDialog/index.cjs +185 -0
- package/dist/AlertDialog/index.cjs.map +1 -0
- package/dist/AlertDialog/index.d.cts +49 -0
- package/dist/AlertDialog/index.d.ts +49 -0
- package/dist/AlertDialog/index.js +148 -0
- package/dist/AlertDialog/index.js.map +1 -0
- package/dist/AlertDialog/styles.css +187 -0
- package/dist/Button/index.cjs +136 -0
- package/dist/Button/index.cjs.map +1 -0
- package/dist/Button/index.d.cts +65 -0
- package/dist/Button/index.d.ts +65 -0
- package/dist/Button/index.js +109 -0
- package/dist/Button/index.js.map +1 -0
- package/dist/Button/styles.css +181 -0
- package/dist/Checkbox/index.cjs +83 -0
- package/dist/Checkbox/index.cjs.map +1 -0
- package/dist/Checkbox/index.d.cts +40 -0
- package/dist/Checkbox/index.d.ts +40 -0
- package/dist/Checkbox/index.js +46 -0
- package/dist/Checkbox/index.js.map +1 -0
- package/dist/Checkbox/styles.css +78 -0
- package/dist/Input/index.cjs +103 -0
- package/dist/Input/index.cjs.map +1 -0
- package/dist/Input/index.d.cts +53 -0
- package/dist/Input/index.d.ts +53 -0
- package/dist/Input/index.js +76 -0
- package/dist/Input/index.js.map +1 -0
- package/dist/Input/styles.css +135 -0
- package/dist/Modal/index.cjs +120 -0
- package/dist/Modal/index.cjs.map +1 -0
- package/dist/Modal/index.d.cts +62 -0
- package/dist/Modal/index.d.ts +62 -0
- package/dist/Modal/index.js +83 -0
- package/dist/Modal/index.js.map +1 -0
- package/dist/Modal/styles.css +229 -0
- package/dist/Radio/index.cjs +86 -0
- package/dist/Radio/index.cjs.map +1 -0
- package/dist/Radio/index.d.cts +46 -0
- package/dist/Radio/index.d.ts +46 -0
- package/dist/Radio/index.js +48 -0
- package/dist/Radio/index.js.map +1 -0
- package/dist/Radio/styles.css +76 -0
- package/dist/Select/index.cjs +116 -0
- package/dist/Select/index.cjs.map +1 -0
- package/dist/Select/index.d.cts +61 -0
- package/dist/Select/index.d.ts +61 -0
- package/dist/Select/index.js +79 -0
- package/dist/Select/index.js.map +1 -0
- package/dist/Select/styles.css +157 -0
- package/dist/Switch/index.cjs +71 -0
- package/dist/Switch/index.cjs.map +1 -0
- package/dist/Switch/index.d.cts +34 -0
- package/dist/Switch/index.d.ts +34 -0
- package/dist/Switch/index.js +34 -0
- package/dist/Switch/index.js.map +1 -0
- package/dist/Switch/styles.css +71 -0
- package/dist/Textarea/index.cjs +127 -0
- package/dist/Textarea/index.cjs.map +1 -0
- package/dist/Textarea/index.d.cts +48 -0
- package/dist/Textarea/index.d.ts +48 -0
- package/dist/Textarea/index.js +100 -0
- package/dist/Textarea/index.js.map +1 -0
- package/dist/Textarea/styles.css +95 -0
- package/dist/Toast/index.cjs +184 -0
- package/dist/Toast/index.cjs.map +1 -0
- package/dist/Toast/index.d.cts +67 -0
- package/dist/Toast/index.d.ts +67 -0
- package/dist/Toast/index.js +146 -0
- package/dist/Toast/index.js.map +1 -0
- package/dist/Toast/styles.css +122 -0
- package/dist/Tooltip/index.cjs +105 -0
- package/dist/Tooltip/index.cjs.map +1 -0
- package/dist/Tooltip/index.d.cts +65 -0
- package/dist/Tooltip/index.d.ts +65 -0
- package/dist/Tooltip/index.js +67 -0
- package/dist/Tooltip/index.js.map +1 -0
- package/dist/Tooltip/styles.css +42 -0
- package/dist/index.cjs +900 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +16 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.js +850 -0
- package/dist/index.js.map +1 -0
- package/dist/styles.css +11 -0
- package/dist/tokens.css +232 -0
- package/dist/typography.css +104 -0
- package/llms.txt +369 -0
- package/package.json +183 -0
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
|
|
30
|
+
// src/Tooltip/index.ts
|
|
31
|
+
var Tooltip_exports = {};
|
|
32
|
+
__export(Tooltip_exports, {
|
|
33
|
+
Tooltip: () => Tooltip,
|
|
34
|
+
TooltipProvider: () => TooltipProvider
|
|
35
|
+
});
|
|
36
|
+
module.exports = __toCommonJS(Tooltip_exports);
|
|
37
|
+
|
|
38
|
+
// src/Tooltip/Tooltip.tsx
|
|
39
|
+
var import_styles = require("./styles.css");
|
|
40
|
+
var RadixTooltip = __toESM(require("@radix-ui/react-tooltip"), 1);
|
|
41
|
+
var import_react = require("react");
|
|
42
|
+
|
|
43
|
+
// src/utils/cx.ts
|
|
44
|
+
function cx(...classes) {
|
|
45
|
+
return classes.filter(Boolean).join(" ");
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// src/Tooltip/Tooltip.tsx
|
|
49
|
+
var import_jsx_runtime = require("react/jsx-runtime");
|
|
50
|
+
var HasProviderContext = (0, import_react.createContext)(false);
|
|
51
|
+
function TooltipProvider({ children, ...props }) {
|
|
52
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(HasProviderContext.Provider, { value: true, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(RadixTooltip.Provider, { ...props, children }) });
|
|
53
|
+
}
|
|
54
|
+
function Tooltip({
|
|
55
|
+
content,
|
|
56
|
+
children,
|
|
57
|
+
side = "top",
|
|
58
|
+
align = "center",
|
|
59
|
+
arrow = true,
|
|
60
|
+
delayDuration,
|
|
61
|
+
open,
|
|
62
|
+
defaultOpen,
|
|
63
|
+
onOpenChange,
|
|
64
|
+
disabled = false,
|
|
65
|
+
container,
|
|
66
|
+
className
|
|
67
|
+
}) {
|
|
68
|
+
const hasProvider = (0, import_react.useContext)(HasProviderContext);
|
|
69
|
+
if (disabled) return children;
|
|
70
|
+
const root = /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
71
|
+
RadixTooltip.Root,
|
|
72
|
+
{
|
|
73
|
+
open,
|
|
74
|
+
defaultOpen,
|
|
75
|
+
onOpenChange,
|
|
76
|
+
delayDuration,
|
|
77
|
+
children: [
|
|
78
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(RadixTooltip.Trigger, { asChild: true, children }),
|
|
79
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(RadixTooltip.Portal, { container, children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
80
|
+
RadixTooltip.Content,
|
|
81
|
+
{
|
|
82
|
+
className: cx("nuri-tooltip", className),
|
|
83
|
+
side,
|
|
84
|
+
align,
|
|
85
|
+
sideOffset: 4,
|
|
86
|
+
collisionPadding: 8,
|
|
87
|
+
arrowPadding: 6,
|
|
88
|
+
hideWhenDetached: true,
|
|
89
|
+
children: [
|
|
90
|
+
content,
|
|
91
|
+
arrow && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(RadixTooltip.Arrow, { className: "nuri-tooltip__arrow", width: 14, height: 7 })
|
|
92
|
+
]
|
|
93
|
+
}
|
|
94
|
+
) })
|
|
95
|
+
]
|
|
96
|
+
}
|
|
97
|
+
);
|
|
98
|
+
return hasProvider ? root : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(RadixTooltip.Provider, { delayDuration: 100, children: root });
|
|
99
|
+
}
|
|
100
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
101
|
+
0 && (module.exports = {
|
|
102
|
+
Tooltip,
|
|
103
|
+
TooltipProvider
|
|
104
|
+
});
|
|
105
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/Tooltip/index.ts","../../src/Tooltip/Tooltip.tsx","../../src/utils/cx.ts"],"sourcesContent":["export { Tooltip, TooltipProvider } from './Tooltip.js';\nexport type { TooltipProps, TooltipProviderProps } from './Tooltip.js';\n","'use client';\n\nimport './styles.css';\nimport * as RadixTooltip from '@radix-ui/react-tooltip';\nimport { createContext, useContext } from 'react';\nimport type { ReactElement, ReactNode } from 'react';\nimport { cx } from '../utils/cx.js';\n\ntype Side = 'top' | 'bottom' | 'left' | 'right';\ntype Align = 'start' | 'center' | 'end';\n\nexport interface TooltipProps {\n /** 툴팁 내용. */\n content: ReactNode;\n /** 트리거 — 단일 엘리먼트에 asChild로 위임 (Button, 아이콘 버튼 등). */\n children: ReactElement;\n /**\n * 툴팁이 뜨는 방향. (디자인의 Direction은 꼬리 방향 기준이라 반대 —\n * 예: 디자인 Direction=Bottom ⇒ side=\"top\")\n * 공간이 부족하면 자동으로 반대쪽으로 반전된다.\n * @default 'top'\n */\n side?: Side;\n /** 트리거 기준 정렬. @default 'center' */\n align?: Align;\n /** 꼬리(화살표) 표시 여부 — 디자인의 showTail. @default true */\n arrow?: boolean;\n /**\n * 열림 지연 ms. 미지정 시 — 단독 사용은 100ms(거의 즉시, 스쳐 지나갈 때 깜빡임만 방지),\n * TooltipProvider 안에서는 프로바이더 설정을 따름. 0이면 즉시.\n * 키보드 포커스는 지연 없이 항상 즉시 표시된다.\n */\n delayDuration?: number;\n /** 열림 상태 제어 — 항상 표시(코치마크류)나 수동 제어에 사용. */\n open?: boolean;\n defaultOpen?: boolean;\n onOpenChange?: (open: boolean) => void;\n /** true면 툴팁 없이 트리거만 렌더 (조건부 툴팁). @default false */\n disabled?: boolean;\n /** 포털 렌더 대상. 기본 document.body. */\n container?: HTMLElement | null;\n /** 툴팁 패널에 추가할 className. */\n className?: string;\n}\n\n// TooltipProvider 존재 감지용 — 없으면 Tooltip이 자체 Provider로 감싼다(제로 셋업).\nconst HasProviderContext = createContext(false);\n\nexport type TooltipProviderProps = RadixTooltip.TooltipProviderProps;\n\n/**\n * 지연 시간을 공유하는 프로바이더 (선택). 앱 루트에 1회 두면 툴팁 하나를 본 직후\n * 인접 트리거로 이동할 때 지연 없이 바로 표시된다(skipDelayDuration).\n * 없어도 Tooltip은 단독으로 동작한다.\n *\n * @example\n * <TooltipProvider delayDuration={300}><App /></TooltipProvider>\n */\nexport function TooltipProvider({ children, ...props }: TooltipProviderProps) {\n return (\n <HasProviderContext.Provider value={true}>\n <RadixTooltip.Provider {...props}>{children}</RadixTooltip.Provider>\n </HasProviderContext.Provider>\n );\n}\n\n/**\n * 툴팁. Radix Tooltip 기반 — hover와 **키보드 포커스** 모두에서 열리고,\n * Esc로 닫히며, `aria-describedby`가 자동 연결된다. 포털 + 충돌 감지\n * 포지셔닝이라 부모 overflow에 잘리지 않고 화면 밖으로 나가지 않는다.\n *\n * 터치 기기에서는 표시되지 않는다(hover 개념 부재 — Radix 기본 동작).\n * 닫기 버튼이 있는 지속형 안내(코치마크)는 툴팁 범위가 아니다.\n *\n * @example\n * <Tooltip content=\"즐겨찾기에 추가\">\n * <Button iconOnly aria-label=\"즐겨찾기\">★</Button>\n * </Tooltip>\n */\nexport function Tooltip({\n content,\n children,\n side = 'top',\n align = 'center',\n arrow = true,\n delayDuration,\n open,\n defaultOpen,\n onOpenChange,\n disabled = false,\n container,\n className,\n}: TooltipProps) {\n const hasProvider = useContext(HasProviderContext);\n\n if (disabled) return children;\n\n const root = (\n <RadixTooltip.Root\n open={open}\n defaultOpen={defaultOpen}\n onOpenChange={onOpenChange}\n delayDuration={delayDuration}\n >\n <RadixTooltip.Trigger asChild>{children}</RadixTooltip.Trigger>\n <RadixTooltip.Portal container={container}>\n <RadixTooltip.Content\n className={cx('nuri-tooltip', className)}\n side={side}\n align={align}\n sideOffset={4}\n collisionPadding={8}\n arrowPadding={6}\n // 트리거가 뷰포트/스크롤 영역에서 완전히 사라지면 툴팁도 숨김\n // (기준 요소 없이 툴팁만 떠 있는 현상 방지)\n hideWhenDetached\n >\n {content}\n {arrow && <RadixTooltip.Arrow className=\"nuri-tooltip__arrow\" width={14} height={7} />}\n </RadixTooltip.Content>\n </RadixTooltip.Portal>\n </RadixTooltip.Root>\n );\n\n // 앱에 TooltipProvider가 없어도 동작하도록 자체 Provider로 폴백.\n // 앱 Provider가 있으면 감싸지 않는다 — 안쪽 컨텍스트가 이기므로 여기서 또 감싸면\n // 앱이 설정한 delayDuration·skipDelayDuration이 무시된다.\n // 폴백 지연 100ms — 거의 즉시로 느껴지되, 마우스가 스쳐 지나갈 때 연쇄 깜빡임은 방지\n // (버튼 하나를 가로지르는 데 ~30–50ms — 그보다 길게 머문 경우만 의도로 판정).\n return hasProvider ? (\n root\n ) : (\n <RadixTooltip.Provider delayDuration={100}>{root}</RadixTooltip.Provider>\n );\n}\n","/**\n * 조건부 className 합성 유틸. falsy 값(undefined/false/null/'')은 제거.\n * @example cx('btn', isActive && 'btn--active', className)\n */\nexport function cx(...classes: Array<string | false | null | undefined>): string {\n return classes.filter(Boolean).join(' ');\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACEA,oBAAO;AACP,mBAA8B;AAC9B,mBAA0C;;;ACAnC,SAAS,MAAM,SAA2D;AAC/E,SAAO,QAAQ,OAAO,OAAO,EAAE,KAAK,GAAG;AACzC;;;ADuDM;AAfN,IAAM,yBAAqB,4BAAc,KAAK;AAYvC,SAAS,gBAAgB,EAAE,UAAU,GAAG,MAAM,GAAyB;AAC5E,SACE,4CAAC,mBAAmB,UAAnB,EAA4B,OAAO,MAClC,sDAAc,uBAAb,EAAuB,GAAG,OAAQ,UAAS,GAC9C;AAEJ;AAeO,SAAS,QAAQ;AAAA,EACtB;AAAA,EACA;AAAA,EACA,OAAO;AAAA,EACP,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,WAAW;AAAA,EACX;AAAA,EACA;AACF,GAAiB;AACf,QAAM,kBAAc,yBAAW,kBAAkB;AAEjD,MAAI,SAAU,QAAO;AAErB,QAAM,OACJ;AAAA,IAAc;AAAA,IAAb;AAAA,MACC;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MAEA;AAAA,oDAAc,sBAAb,EAAqB,SAAO,MAAE,UAAS;AAAA,QACxC,4CAAc,qBAAb,EAAoB,WACnB;AAAA,UAAc;AAAA,UAAb;AAAA,YACC,WAAW,GAAG,gBAAgB,SAAS;AAAA,YACvC;AAAA,YACA;AAAA,YACA,YAAY;AAAA,YACZ,kBAAkB;AAAA,YAClB,cAAc;AAAA,YAGd,kBAAgB;AAAA,YAEf;AAAA;AAAA,cACA,SAAS,4CAAc,oBAAb,EAAmB,WAAU,uBAAsB,OAAO,IAAI,QAAQ,GAAG;AAAA;AAAA;AAAA,QACtF,GACF;AAAA;AAAA;AAAA,EACF;AAQF,SAAO,cACL,OAEA,4CAAc,uBAAb,EAAsB,eAAe,KAAM,gBAAK;AAErD;","names":[]}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import * as react from 'react';
|
|
2
|
+
import { ReactNode, ReactElement } from 'react';
|
|
3
|
+
import * as RadixTooltip from '@radix-ui/react-tooltip';
|
|
4
|
+
|
|
5
|
+
type Side = 'top' | 'bottom' | 'left' | 'right';
|
|
6
|
+
type Align = 'start' | 'center' | 'end';
|
|
7
|
+
interface TooltipProps {
|
|
8
|
+
/** 툴팁 내용. */
|
|
9
|
+
content: ReactNode;
|
|
10
|
+
/** 트리거 — 단일 엘리먼트에 asChild로 위임 (Button, 아이콘 버튼 등). */
|
|
11
|
+
children: ReactElement;
|
|
12
|
+
/**
|
|
13
|
+
* 툴팁이 뜨는 방향. (디자인의 Direction은 꼬리 방향 기준이라 반대 —
|
|
14
|
+
* 예: 디자인 Direction=Bottom ⇒ side="top")
|
|
15
|
+
* 공간이 부족하면 자동으로 반대쪽으로 반전된다.
|
|
16
|
+
* @default 'top'
|
|
17
|
+
*/
|
|
18
|
+
side?: Side;
|
|
19
|
+
/** 트리거 기준 정렬. @default 'center' */
|
|
20
|
+
align?: Align;
|
|
21
|
+
/** 꼬리(화살표) 표시 여부 — 디자인의 showTail. @default true */
|
|
22
|
+
arrow?: boolean;
|
|
23
|
+
/**
|
|
24
|
+
* 열림 지연 ms. 미지정 시 — 단독 사용은 100ms(거의 즉시, 스쳐 지나갈 때 깜빡임만 방지),
|
|
25
|
+
* TooltipProvider 안에서는 프로바이더 설정을 따름. 0이면 즉시.
|
|
26
|
+
* 키보드 포커스는 지연 없이 항상 즉시 표시된다.
|
|
27
|
+
*/
|
|
28
|
+
delayDuration?: number;
|
|
29
|
+
/** 열림 상태 제어 — 항상 표시(코치마크류)나 수동 제어에 사용. */
|
|
30
|
+
open?: boolean;
|
|
31
|
+
defaultOpen?: boolean;
|
|
32
|
+
onOpenChange?: (open: boolean) => void;
|
|
33
|
+
/** true면 툴팁 없이 트리거만 렌더 (조건부 툴팁). @default false */
|
|
34
|
+
disabled?: boolean;
|
|
35
|
+
/** 포털 렌더 대상. 기본 document.body. */
|
|
36
|
+
container?: HTMLElement | null;
|
|
37
|
+
/** 툴팁 패널에 추가할 className. */
|
|
38
|
+
className?: string;
|
|
39
|
+
}
|
|
40
|
+
type TooltipProviderProps = RadixTooltip.TooltipProviderProps;
|
|
41
|
+
/**
|
|
42
|
+
* 지연 시간을 공유하는 프로바이더 (선택). 앱 루트에 1회 두면 툴팁 하나를 본 직후
|
|
43
|
+
* 인접 트리거로 이동할 때 지연 없이 바로 표시된다(skipDelayDuration).
|
|
44
|
+
* 없어도 Tooltip은 단독으로 동작한다.
|
|
45
|
+
*
|
|
46
|
+
* @example
|
|
47
|
+
* <TooltipProvider delayDuration={300}><App /></TooltipProvider>
|
|
48
|
+
*/
|
|
49
|
+
declare function TooltipProvider({ children, ...props }: TooltipProviderProps): react.JSX.Element;
|
|
50
|
+
/**
|
|
51
|
+
* 툴팁. Radix Tooltip 기반 — hover와 **키보드 포커스** 모두에서 열리고,
|
|
52
|
+
* Esc로 닫히며, `aria-describedby`가 자동 연결된다. 포털 + 충돌 감지
|
|
53
|
+
* 포지셔닝이라 부모 overflow에 잘리지 않고 화면 밖으로 나가지 않는다.
|
|
54
|
+
*
|
|
55
|
+
* 터치 기기에서는 표시되지 않는다(hover 개념 부재 — Radix 기본 동작).
|
|
56
|
+
* 닫기 버튼이 있는 지속형 안내(코치마크)는 툴팁 범위가 아니다.
|
|
57
|
+
*
|
|
58
|
+
* @example
|
|
59
|
+
* <Tooltip content="즐겨찾기에 추가">
|
|
60
|
+
* <Button iconOnly aria-label="즐겨찾기">★</Button>
|
|
61
|
+
* </Tooltip>
|
|
62
|
+
*/
|
|
63
|
+
declare function Tooltip({ content, children, side, align, arrow, delayDuration, open, defaultOpen, onOpenChange, disabled, container, className, }: TooltipProps): react.JSX.Element;
|
|
64
|
+
|
|
65
|
+
export { Tooltip, type TooltipProps, TooltipProvider, type TooltipProviderProps };
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import * as react from 'react';
|
|
2
|
+
import { ReactNode, ReactElement } from 'react';
|
|
3
|
+
import * as RadixTooltip from '@radix-ui/react-tooltip';
|
|
4
|
+
|
|
5
|
+
type Side = 'top' | 'bottom' | 'left' | 'right';
|
|
6
|
+
type Align = 'start' | 'center' | 'end';
|
|
7
|
+
interface TooltipProps {
|
|
8
|
+
/** 툴팁 내용. */
|
|
9
|
+
content: ReactNode;
|
|
10
|
+
/** 트리거 — 단일 엘리먼트에 asChild로 위임 (Button, 아이콘 버튼 등). */
|
|
11
|
+
children: ReactElement;
|
|
12
|
+
/**
|
|
13
|
+
* 툴팁이 뜨는 방향. (디자인의 Direction은 꼬리 방향 기준이라 반대 —
|
|
14
|
+
* 예: 디자인 Direction=Bottom ⇒ side="top")
|
|
15
|
+
* 공간이 부족하면 자동으로 반대쪽으로 반전된다.
|
|
16
|
+
* @default 'top'
|
|
17
|
+
*/
|
|
18
|
+
side?: Side;
|
|
19
|
+
/** 트리거 기준 정렬. @default 'center' */
|
|
20
|
+
align?: Align;
|
|
21
|
+
/** 꼬리(화살표) 표시 여부 — 디자인의 showTail. @default true */
|
|
22
|
+
arrow?: boolean;
|
|
23
|
+
/**
|
|
24
|
+
* 열림 지연 ms. 미지정 시 — 단독 사용은 100ms(거의 즉시, 스쳐 지나갈 때 깜빡임만 방지),
|
|
25
|
+
* TooltipProvider 안에서는 프로바이더 설정을 따름. 0이면 즉시.
|
|
26
|
+
* 키보드 포커스는 지연 없이 항상 즉시 표시된다.
|
|
27
|
+
*/
|
|
28
|
+
delayDuration?: number;
|
|
29
|
+
/** 열림 상태 제어 — 항상 표시(코치마크류)나 수동 제어에 사용. */
|
|
30
|
+
open?: boolean;
|
|
31
|
+
defaultOpen?: boolean;
|
|
32
|
+
onOpenChange?: (open: boolean) => void;
|
|
33
|
+
/** true면 툴팁 없이 트리거만 렌더 (조건부 툴팁). @default false */
|
|
34
|
+
disabled?: boolean;
|
|
35
|
+
/** 포털 렌더 대상. 기본 document.body. */
|
|
36
|
+
container?: HTMLElement | null;
|
|
37
|
+
/** 툴팁 패널에 추가할 className. */
|
|
38
|
+
className?: string;
|
|
39
|
+
}
|
|
40
|
+
type TooltipProviderProps = RadixTooltip.TooltipProviderProps;
|
|
41
|
+
/**
|
|
42
|
+
* 지연 시간을 공유하는 프로바이더 (선택). 앱 루트에 1회 두면 툴팁 하나를 본 직후
|
|
43
|
+
* 인접 트리거로 이동할 때 지연 없이 바로 표시된다(skipDelayDuration).
|
|
44
|
+
* 없어도 Tooltip은 단독으로 동작한다.
|
|
45
|
+
*
|
|
46
|
+
* @example
|
|
47
|
+
* <TooltipProvider delayDuration={300}><App /></TooltipProvider>
|
|
48
|
+
*/
|
|
49
|
+
declare function TooltipProvider({ children, ...props }: TooltipProviderProps): react.JSX.Element;
|
|
50
|
+
/**
|
|
51
|
+
* 툴팁. Radix Tooltip 기반 — hover와 **키보드 포커스** 모두에서 열리고,
|
|
52
|
+
* Esc로 닫히며, `aria-describedby`가 자동 연결된다. 포털 + 충돌 감지
|
|
53
|
+
* 포지셔닝이라 부모 overflow에 잘리지 않고 화면 밖으로 나가지 않는다.
|
|
54
|
+
*
|
|
55
|
+
* 터치 기기에서는 표시되지 않는다(hover 개념 부재 — Radix 기본 동작).
|
|
56
|
+
* 닫기 버튼이 있는 지속형 안내(코치마크)는 툴팁 범위가 아니다.
|
|
57
|
+
*
|
|
58
|
+
* @example
|
|
59
|
+
* <Tooltip content="즐겨찾기에 추가">
|
|
60
|
+
* <Button iconOnly aria-label="즐겨찾기">★</Button>
|
|
61
|
+
* </Tooltip>
|
|
62
|
+
*/
|
|
63
|
+
declare function Tooltip({ content, children, side, align, arrow, delayDuration, open, defaultOpen, onOpenChange, disabled, container, className, }: TooltipProps): react.JSX.Element;
|
|
64
|
+
|
|
65
|
+
export { Tooltip, type TooltipProps, TooltipProvider, type TooltipProviderProps };
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
// src/Tooltip/Tooltip.tsx
|
|
2
|
+
import "./styles.css";
|
|
3
|
+
import * as RadixTooltip from "@radix-ui/react-tooltip";
|
|
4
|
+
import { createContext, useContext } from "react";
|
|
5
|
+
|
|
6
|
+
// src/utils/cx.ts
|
|
7
|
+
function cx(...classes) {
|
|
8
|
+
return classes.filter(Boolean).join(" ");
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
// src/Tooltip/Tooltip.tsx
|
|
12
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
13
|
+
var HasProviderContext = createContext(false);
|
|
14
|
+
function TooltipProvider({ children, ...props }) {
|
|
15
|
+
return /* @__PURE__ */ jsx(HasProviderContext.Provider, { value: true, children: /* @__PURE__ */ jsx(RadixTooltip.Provider, { ...props, children }) });
|
|
16
|
+
}
|
|
17
|
+
function Tooltip({
|
|
18
|
+
content,
|
|
19
|
+
children,
|
|
20
|
+
side = "top",
|
|
21
|
+
align = "center",
|
|
22
|
+
arrow = true,
|
|
23
|
+
delayDuration,
|
|
24
|
+
open,
|
|
25
|
+
defaultOpen,
|
|
26
|
+
onOpenChange,
|
|
27
|
+
disabled = false,
|
|
28
|
+
container,
|
|
29
|
+
className
|
|
30
|
+
}) {
|
|
31
|
+
const hasProvider = useContext(HasProviderContext);
|
|
32
|
+
if (disabled) return children;
|
|
33
|
+
const root = /* @__PURE__ */ jsxs(
|
|
34
|
+
RadixTooltip.Root,
|
|
35
|
+
{
|
|
36
|
+
open,
|
|
37
|
+
defaultOpen,
|
|
38
|
+
onOpenChange,
|
|
39
|
+
delayDuration,
|
|
40
|
+
children: [
|
|
41
|
+
/* @__PURE__ */ jsx(RadixTooltip.Trigger, { asChild: true, children }),
|
|
42
|
+
/* @__PURE__ */ jsx(RadixTooltip.Portal, { container, children: /* @__PURE__ */ jsxs(
|
|
43
|
+
RadixTooltip.Content,
|
|
44
|
+
{
|
|
45
|
+
className: cx("nuri-tooltip", className),
|
|
46
|
+
side,
|
|
47
|
+
align,
|
|
48
|
+
sideOffset: 4,
|
|
49
|
+
collisionPadding: 8,
|
|
50
|
+
arrowPadding: 6,
|
|
51
|
+
hideWhenDetached: true,
|
|
52
|
+
children: [
|
|
53
|
+
content,
|
|
54
|
+
arrow && /* @__PURE__ */ jsx(RadixTooltip.Arrow, { className: "nuri-tooltip__arrow", width: 14, height: 7 })
|
|
55
|
+
]
|
|
56
|
+
}
|
|
57
|
+
) })
|
|
58
|
+
]
|
|
59
|
+
}
|
|
60
|
+
);
|
|
61
|
+
return hasProvider ? root : /* @__PURE__ */ jsx(RadixTooltip.Provider, { delayDuration: 100, children: root });
|
|
62
|
+
}
|
|
63
|
+
export {
|
|
64
|
+
Tooltip,
|
|
65
|
+
TooltipProvider
|
|
66
|
+
};
|
|
67
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/Tooltip/Tooltip.tsx","../../src/utils/cx.ts"],"sourcesContent":["'use client';\n\nimport './styles.css';\nimport * as RadixTooltip from '@radix-ui/react-tooltip';\nimport { createContext, useContext } from 'react';\nimport type { ReactElement, ReactNode } from 'react';\nimport { cx } from '../utils/cx.js';\n\ntype Side = 'top' | 'bottom' | 'left' | 'right';\ntype Align = 'start' | 'center' | 'end';\n\nexport interface TooltipProps {\n /** 툴팁 내용. */\n content: ReactNode;\n /** 트리거 — 단일 엘리먼트에 asChild로 위임 (Button, 아이콘 버튼 등). */\n children: ReactElement;\n /**\n * 툴팁이 뜨는 방향. (디자인의 Direction은 꼬리 방향 기준이라 반대 —\n * 예: 디자인 Direction=Bottom ⇒ side=\"top\")\n * 공간이 부족하면 자동으로 반대쪽으로 반전된다.\n * @default 'top'\n */\n side?: Side;\n /** 트리거 기준 정렬. @default 'center' */\n align?: Align;\n /** 꼬리(화살표) 표시 여부 — 디자인의 showTail. @default true */\n arrow?: boolean;\n /**\n * 열림 지연 ms. 미지정 시 — 단독 사용은 100ms(거의 즉시, 스쳐 지나갈 때 깜빡임만 방지),\n * TooltipProvider 안에서는 프로바이더 설정을 따름. 0이면 즉시.\n * 키보드 포커스는 지연 없이 항상 즉시 표시된다.\n */\n delayDuration?: number;\n /** 열림 상태 제어 — 항상 표시(코치마크류)나 수동 제어에 사용. */\n open?: boolean;\n defaultOpen?: boolean;\n onOpenChange?: (open: boolean) => void;\n /** true면 툴팁 없이 트리거만 렌더 (조건부 툴팁). @default false */\n disabled?: boolean;\n /** 포털 렌더 대상. 기본 document.body. */\n container?: HTMLElement | null;\n /** 툴팁 패널에 추가할 className. */\n className?: string;\n}\n\n// TooltipProvider 존재 감지용 — 없으면 Tooltip이 자체 Provider로 감싼다(제로 셋업).\nconst HasProviderContext = createContext(false);\n\nexport type TooltipProviderProps = RadixTooltip.TooltipProviderProps;\n\n/**\n * 지연 시간을 공유하는 프로바이더 (선택). 앱 루트에 1회 두면 툴팁 하나를 본 직후\n * 인접 트리거로 이동할 때 지연 없이 바로 표시된다(skipDelayDuration).\n * 없어도 Tooltip은 단독으로 동작한다.\n *\n * @example\n * <TooltipProvider delayDuration={300}><App /></TooltipProvider>\n */\nexport function TooltipProvider({ children, ...props }: TooltipProviderProps) {\n return (\n <HasProviderContext.Provider value={true}>\n <RadixTooltip.Provider {...props}>{children}</RadixTooltip.Provider>\n </HasProviderContext.Provider>\n );\n}\n\n/**\n * 툴팁. Radix Tooltip 기반 — hover와 **키보드 포커스** 모두에서 열리고,\n * Esc로 닫히며, `aria-describedby`가 자동 연결된다. 포털 + 충돌 감지\n * 포지셔닝이라 부모 overflow에 잘리지 않고 화면 밖으로 나가지 않는다.\n *\n * 터치 기기에서는 표시되지 않는다(hover 개념 부재 — Radix 기본 동작).\n * 닫기 버튼이 있는 지속형 안내(코치마크)는 툴팁 범위가 아니다.\n *\n * @example\n * <Tooltip content=\"즐겨찾기에 추가\">\n * <Button iconOnly aria-label=\"즐겨찾기\">★</Button>\n * </Tooltip>\n */\nexport function Tooltip({\n content,\n children,\n side = 'top',\n align = 'center',\n arrow = true,\n delayDuration,\n open,\n defaultOpen,\n onOpenChange,\n disabled = false,\n container,\n className,\n}: TooltipProps) {\n const hasProvider = useContext(HasProviderContext);\n\n if (disabled) return children;\n\n const root = (\n <RadixTooltip.Root\n open={open}\n defaultOpen={defaultOpen}\n onOpenChange={onOpenChange}\n delayDuration={delayDuration}\n >\n <RadixTooltip.Trigger asChild>{children}</RadixTooltip.Trigger>\n <RadixTooltip.Portal container={container}>\n <RadixTooltip.Content\n className={cx('nuri-tooltip', className)}\n side={side}\n align={align}\n sideOffset={4}\n collisionPadding={8}\n arrowPadding={6}\n // 트리거가 뷰포트/스크롤 영역에서 완전히 사라지면 툴팁도 숨김\n // (기준 요소 없이 툴팁만 떠 있는 현상 방지)\n hideWhenDetached\n >\n {content}\n {arrow && <RadixTooltip.Arrow className=\"nuri-tooltip__arrow\" width={14} height={7} />}\n </RadixTooltip.Content>\n </RadixTooltip.Portal>\n </RadixTooltip.Root>\n );\n\n // 앱에 TooltipProvider가 없어도 동작하도록 자체 Provider로 폴백.\n // 앱 Provider가 있으면 감싸지 않는다 — 안쪽 컨텍스트가 이기므로 여기서 또 감싸면\n // 앱이 설정한 delayDuration·skipDelayDuration이 무시된다.\n // 폴백 지연 100ms — 거의 즉시로 느껴지되, 마우스가 스쳐 지나갈 때 연쇄 깜빡임은 방지\n // (버튼 하나를 가로지르는 데 ~30–50ms — 그보다 길게 머문 경우만 의도로 판정).\n return hasProvider ? (\n root\n ) : (\n <RadixTooltip.Provider delayDuration={100}>{root}</RadixTooltip.Provider>\n );\n}\n","/**\n * 조건부 className 합성 유틸. falsy 값(undefined/false/null/'')은 제거.\n * @example cx('btn', isActive && 'btn--active', className)\n */\nexport function cx(...classes: Array<string | false | null | undefined>): string {\n return classes.filter(Boolean).join(' ');\n}\n"],"mappings":";AAEA,OAAO;AACP,YAAY,kBAAkB;AAC9B,SAAS,eAAe,kBAAkB;;;ACAnC,SAAS,MAAM,SAA2D;AAC/E,SAAO,QAAQ,OAAO,OAAO,EAAE,KAAK,GAAG;AACzC;;;ADuDM,cA6CE,YA7CF;AAfN,IAAM,qBAAqB,cAAc,KAAK;AAYvC,SAAS,gBAAgB,EAAE,UAAU,GAAG,MAAM,GAAyB;AAC5E,SACE,oBAAC,mBAAmB,UAAnB,EAA4B,OAAO,MAClC,8BAAc,uBAAb,EAAuB,GAAG,OAAQ,UAAS,GAC9C;AAEJ;AAeO,SAAS,QAAQ;AAAA,EACtB;AAAA,EACA;AAAA,EACA,OAAO;AAAA,EACP,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,WAAW;AAAA,EACX;AAAA,EACA;AACF,GAAiB;AACf,QAAM,cAAc,WAAW,kBAAkB;AAEjD,MAAI,SAAU,QAAO;AAErB,QAAM,OACJ;AAAA,IAAc;AAAA,IAAb;AAAA,MACC;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MAEA;AAAA,4BAAc,sBAAb,EAAqB,SAAO,MAAE,UAAS;AAAA,QACxC,oBAAc,qBAAb,EAAoB,WACnB;AAAA,UAAc;AAAA,UAAb;AAAA,YACC,WAAW,GAAG,gBAAgB,SAAS;AAAA,YACvC;AAAA,YACA;AAAA,YACA,YAAY;AAAA,YACZ,kBAAkB;AAAA,YAClB,cAAc;AAAA,YAGd,kBAAgB;AAAA,YAEf;AAAA;AAAA,cACA,SAAS,oBAAc,oBAAb,EAAmB,WAAU,uBAAsB,OAAO,IAAI,QAAQ,GAAG;AAAA;AAAA;AAAA,QACtF,GACF;AAAA;AAAA;AAAA,EACF;AAQF,SAAO,cACL,OAEA,oBAAc,uBAAb,EAAsB,eAAe,KAAM,gBAAK;AAErD;","names":[]}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
.nuri-tooltip {
|
|
2
|
+
box-sizing: border-box;
|
|
3
|
+
z-index: var(--nuri-tooltip-z-index);
|
|
4
|
+
max-width: var(--nuri-tooltip-max-width);
|
|
5
|
+
padding: var(--nuri-tooltip-padding-y) var(--nuri-tooltip-padding-x);
|
|
6
|
+
border-radius: var(--nuri-tooltip-radius);
|
|
7
|
+
background-color: var(--nuri-tooltip-bg);
|
|
8
|
+
color: var(--nuri-tooltip-text);
|
|
9
|
+
font-family: inherit; /* 앱 폰트 상속 */
|
|
10
|
+
font-size: var(--nuri-tooltip-font-size);
|
|
11
|
+
font-weight: var(--nuri-tooltip-font-weight);
|
|
12
|
+
line-height: var(--nuri-tooltip-line-height);
|
|
13
|
+
word-break: break-word; /* 긴 콘텐츠는 max-width에서 줄바꿈 */
|
|
14
|
+
user-select: none;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
.nuri-tooltip__arrow {
|
|
18
|
+
fill: var(--nuri-tooltip-bg);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/* ── 애니메이션 — 뜨는 방향에 맞춰 살짝 이동 + 페이드 ── */
|
|
22
|
+
.nuri-tooltip[data-state='delayed-open'],
|
|
23
|
+
.nuri-tooltip[data-state='instant-open'] {
|
|
24
|
+
animation: nuri-tooltip-in 120ms ease;
|
|
25
|
+
}
|
|
26
|
+
@keyframes nuri-tooltip-in {
|
|
27
|
+
from {
|
|
28
|
+
opacity: 0;
|
|
29
|
+
transform: scale(0.97);
|
|
30
|
+
}
|
|
31
|
+
to {
|
|
32
|
+
opacity: 1;
|
|
33
|
+
transform: scale(1);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
@media (prefers-reduced-motion: reduce) {
|
|
38
|
+
.nuri-tooltip[data-state='delayed-open'],
|
|
39
|
+
.nuri-tooltip[data-state='instant-open'] {
|
|
40
|
+
animation-duration: 1ms;
|
|
41
|
+
}
|
|
42
|
+
}
|