react-miui 0.33.0 → 0.34.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 (207) hide show
  1. package/.claude/settings.json +12 -0
  2. package/.claude/settings.local.json +4 -1
  3. package/.storybook/preview.tsx +10 -4
  4. package/CHANGELOG.md +9 -0
  5. package/dist/components/ui/drawer/Drawer.d.ts +10 -1
  6. package/dist/components/ui/drawer/Drawer.d.ts.map +1 -1
  7. package/dist/components/ui/drawer/Drawer.js +135 -15
  8. package/dist/components/ui/drawer/Drawer.js.map +1 -1
  9. package/dist/components/ui/drawer/Drawer.styled.d.ts +86 -1
  10. package/dist/components/ui/drawer/Drawer.styled.d.ts.map +1 -1
  11. package/dist/components/ui/drawer/Drawer.styled.js +13 -1
  12. package/dist/components/ui/drawer/Drawer.styled.js.map +1 -1
  13. package/dist/components/ui/toaster/Toaster.d.ts.map +1 -1
  14. package/dist/components/ui/toaster/Toaster.js +7 -1
  15. package/dist/components/ui/toaster/Toaster.js.map +1 -1
  16. package/dist/components/ui/tooltip/Tooltip.d.ts +30 -0
  17. package/dist/components/ui/tooltip/Tooltip.d.ts.map +1 -0
  18. package/dist/components/ui/tooltip/Tooltip.js +81 -0
  19. package/dist/components/ui/tooltip/Tooltip.js.map +1 -0
  20. package/dist/components/ui/tooltip/Tooltip.styled.d.ts +173 -0
  21. package/dist/components/ui/tooltip/Tooltip.styled.d.ts.map +1 -0
  22. package/dist/components/ui/tooltip/Tooltip.styled.js +65 -0
  23. package/dist/components/ui/tooltip/Tooltip.styled.js.map +1 -0
  24. package/dist/index.d.ts +1 -0
  25. package/dist/index.d.ts.map +1 -1
  26. package/dist/index.js +1 -0
  27. package/dist/index.js.map +1 -1
  28. package/docs/assets/highlight.css +7 -0
  29. package/docs/assets/navigation.js +1 -1
  30. package/docs/assets/search.js +1 -1
  31. package/docs/classes/index.Pop.html +7 -7
  32. package/docs/documents/Test.html +2 -2
  33. package/docs/enums/index.ICON.html +2 -2
  34. package/docs/functions/index.Action.html +3 -3
  35. package/docs/functions/index.Button.html +4 -4
  36. package/docs/functions/index.Card.html +3 -3
  37. package/docs/functions/index.Checkbox.html +3 -3
  38. package/docs/functions/index.Choice.html +2 -2
  39. package/docs/functions/index.ColorPicker.html +3 -3
  40. package/docs/functions/index.CoveringLoader.html +3 -3
  41. package/docs/functions/index.DirectionPad.html +2 -2
  42. package/docs/functions/index.Drawer.html +2 -2
  43. package/docs/functions/index.EqualActions.html +2 -2
  44. package/docs/functions/index.FullLoader.html +3 -3
  45. package/docs/functions/index.Gap.html +3 -3
  46. package/docs/functions/index.HandleEsc.html +3 -3
  47. package/docs/functions/index.Header.html +3 -3
  48. package/docs/functions/index.HeaderIconAction.html +3 -3
  49. package/docs/functions/index.Icon-1.html +2 -2
  50. package/docs/functions/index.If.html +3 -3
  51. package/docs/functions/index.Input.html +1 -1
  52. package/docs/functions/index.KeyValue.html +2 -2
  53. package/docs/functions/index.Label.html +2 -2
  54. package/docs/functions/index.Line.html +4 -4
  55. package/docs/functions/index.List.html +2 -2
  56. package/docs/functions/index.Loader.html +3 -3
  57. package/docs/functions/index.Loading.html +3 -3
  58. package/docs/functions/index.Message.html +4 -4
  59. package/docs/functions/index.Modal.html +2 -2
  60. package/docs/functions/index.ModalButtons.html +3 -3
  61. package/docs/functions/index.PopLoader.html +3 -3
  62. package/docs/functions/index.PopOption.html +2 -2
  63. package/docs/functions/index.Progress.html +2 -2
  64. package/docs/functions/index.SearchContainer.html +3 -3
  65. package/docs/functions/index.Section.html +4 -4
  66. package/docs/functions/index.Select.html +3 -3
  67. package/docs/functions/index.Selector.html +2 -2
  68. package/docs/functions/index.Spacer.html +3 -3
  69. package/docs/functions/index.Stats.html +2 -2
  70. package/docs/functions/index.StickyHeader.html +4 -4
  71. package/docs/functions/index.Table.html +3 -3
  72. package/docs/functions/index.TextArea.html +2 -2
  73. package/docs/functions/index.ToasterProvider.html +3 -3
  74. package/docs/functions/index.Toggle.html +3 -3
  75. package/docs/functions/index.ToolButton.html +4 -4
  76. package/docs/functions/index.Tooltip.html +18 -0
  77. package/docs/functions/index.TooltipProvider.html +6 -0
  78. package/docs/functions/index.borderPxToRem.html +1 -1
  79. package/docs/functions/index.createTheme.html +1 -1
  80. package/docs/functions/index.css.html +1 -1
  81. package/docs/functions/index.dimensionsPxToRem.html +1 -1
  82. package/docs/functions/index.fontPxToRem.html +1 -1
  83. package/docs/functions/index.getCssText.html +1 -1
  84. package/docs/functions/index.globalCss.html +2 -2
  85. package/docs/functions/index.injectGlobalStyles.html +1 -1
  86. package/docs/functions/index.keyframes.html +1 -1
  87. package/docs/functions/index.pxToRem.html +1 -1
  88. package/docs/functions/index.styled.html +1 -1
  89. package/docs/functions/index.toast.html +2 -2
  90. package/docs/functions/index.useToaster.html +1 -1
  91. package/docs/index.html +2 -2
  92. package/docs/interfaces/index.IconProps.html +2 -2
  93. package/docs/interfaces/index.InputCustomProps.html +3 -3
  94. package/docs/interfaces/index.LoaderProps.html +6 -6
  95. package/docs/interfaces/index.StickyHeaderProps.html +4 -4
  96. package/docs/interfaces/index.ToasterProviderProps.html +3 -3
  97. package/docs/interfaces/index.TooltipProps.html +36 -0
  98. package/docs/interfaces/index.TooltipProviderProps.html +13 -0
  99. package/docs/modules/index.html +1 -1
  100. package/docs/modules.html +1 -1
  101. package/docs/types/index.ActionProps.html +1 -1
  102. package/docs/types/index.CardProps.html +1 -1
  103. package/docs/types/index.CheckboxProps.html +2 -2
  104. package/docs/types/index.ChoiceProps.html +1 -1
  105. package/docs/types/index.ColorPickerProps.html +1 -1
  106. package/docs/types/index.DirectionPadProps.html +1 -1
  107. package/docs/types/index.DrawerFrom.html +1 -0
  108. package/docs/types/index.DrawerProps.html +28 -1
  109. package/docs/types/index.EqualActionsProps.html +1 -1
  110. package/docs/types/index.HeaderProps.html +1 -1
  111. package/docs/types/index.InputProps.html +1 -1
  112. package/docs/types/index.KeyValueProps.html +1 -1
  113. package/docs/types/index.LabelProps.html +1 -1
  114. package/docs/types/index.OverwriteProps.html +1 -1
  115. package/docs/types/index.ProgressProps.html +2 -2
  116. package/docs/types/index.SelectProps.html +1 -1
  117. package/docs/types/index.SelectorProps.html +1 -1
  118. package/docs/types/index.Stat.html +1 -1
  119. package/docs/types/index.StatsProps.html +1 -1
  120. package/docs/types/index.TextAreaProps.html +1 -1
  121. package/docs/types/index.ThemeCSS.html +1 -1
  122. package/docs/types/index.ToggleProps.html +2 -2
  123. package/docs/variables/index.ActionBadgeSelector.html +1 -1
  124. package/docs/variables/index.ActionCircleSelector.html +1 -1
  125. package/docs/variables/index.CheckboxCheckmarkWrapperSelector.html +1 -1
  126. package/docs/variables/index.CheckboxTextLabelSelector.html +1 -1
  127. package/docs/variables/index.ChoiceItemSelector.html +1 -1
  128. package/docs/variables/index.ColorPickerColorDisplaySelector.html +1 -1
  129. package/docs/variables/index.DirectionPadButtonDotSelector.html +1 -1
  130. package/docs/variables/index.DirectionPadButtonSelector.html +1 -1
  131. package/docs/variables/index.DirectionPadLineSelector.html +1 -1
  132. package/docs/variables/index.DirectionPadMiddleSelector.html +1 -1
  133. package/docs/variables/index.DrawerContentSelector.html +1 -1
  134. package/docs/variables/index.HeaderAfterSelector.html +1 -1
  135. package/docs/variables/index.HeaderBeforeSelector.html +1 -1
  136. package/docs/variables/index.HeaderContentsSelector.html +1 -1
  137. package/docs/variables/index.HeaderIconActionIconSelector.html +1 -1
  138. package/docs/variables/index.InputContainerSelector.html +1 -1
  139. package/docs/variables/index.InputInputSelector.html +1 -1
  140. package/docs/variables/index.InputLabelSelector.html +1 -1
  141. package/docs/variables/index.InputPrefixSelector.html +1 -1
  142. package/docs/variables/index.InputSuffixSelector.html +1 -1
  143. package/docs/variables/index.KeyValueIconSelector.html +1 -1
  144. package/docs/variables/index.KeyValueItemSelector.html +1 -1
  145. package/docs/variables/index.KeyValueKeySelector.html +1 -1
  146. package/docs/variables/index.KeyValuePairSelector.html +1 -1
  147. package/docs/variables/index.KeyValueValueSelector.html +1 -1
  148. package/docs/variables/index.LabelTextSelector.html +1 -1
  149. package/docs/variables/index.ListItemInnerContainerClassNameSelector.html +1 -1
  150. package/docs/variables/index.ModalContainerSelector.html +1 -1
  151. package/docs/variables/index.ModalRemovePaddingSelector.html +1 -1
  152. package/docs/variables/index.ModalTitleSelector.html +1 -1
  153. package/docs/variables/index.PopListSelector.html +1 -1
  154. package/docs/variables/index.PopOptionButtonSelector.html +1 -1
  155. package/docs/variables/index.PopOptionIconSelector.html +1 -1
  156. package/docs/variables/index.PopOverlaySelector.html +1 -1
  157. package/docs/variables/index.ProgressBackgroundSelector.html +1 -1
  158. package/docs/variables/index.ProgressValueSelector.html +1 -1
  159. package/docs/variables/index.SelectorItemSelector.html +1 -1
  160. package/docs/variables/index.StatsItemSelector.html +1 -1
  161. package/docs/variables/index.StatsLabelSelector.html +1 -1
  162. package/docs/variables/index.StatsSeparatorSelector.html +1 -1
  163. package/docs/variables/index.StatsValueSelector.html +1 -1
  164. package/docs/variables/index.TextAreaLabelSelector.html +1 -1
  165. package/docs/variables/index.TextAreaTextAreaSelector.html +1 -1
  166. package/docs/variables/index.TextAreaWrapperSelector.html +1 -1
  167. package/docs/variables/index.ToggleStyledToggleSelector.html +1 -1
  168. package/docs/variables/index.TooltipContentSelector.html +1 -0
  169. package/docs/variables/index.config.html +1 -1
  170. package/docs/variables/index.cssReset.html +2 -2
  171. package/docs/variables/index.darkTheme.html +1 -1
  172. package/docs/variables/index.miuiScrollbars.html +1 -1
  173. package/docs/variables/index.theme.html +1 -1
  174. package/esm/components/ui/drawer/Drawer.d.ts +10 -1
  175. package/esm/components/ui/drawer/Drawer.d.ts.map +1 -1
  176. package/esm/components/ui/drawer/Drawer.js +139 -15
  177. package/esm/components/ui/drawer/Drawer.js.map +1 -1
  178. package/esm/components/ui/drawer/Drawer.styled.d.ts +86 -1
  179. package/esm/components/ui/drawer/Drawer.styled.d.ts.map +1 -1
  180. package/esm/components/ui/drawer/Drawer.styled.js +12 -1
  181. package/esm/components/ui/drawer/Drawer.styled.js.map +1 -1
  182. package/esm/components/ui/toaster/Toaster.d.ts.map +1 -1
  183. package/esm/components/ui/toaster/Toaster.js +8 -2
  184. package/esm/components/ui/toaster/Toaster.js.map +1 -1
  185. package/esm/components/ui/tooltip/Tooltip.d.ts +30 -0
  186. package/esm/components/ui/tooltip/Tooltip.d.ts.map +1 -0
  187. package/esm/components/ui/tooltip/Tooltip.js +43 -0
  188. package/esm/components/ui/tooltip/Tooltip.js.map +1 -0
  189. package/esm/components/ui/tooltip/Tooltip.styled.d.ts +173 -0
  190. package/esm/components/ui/tooltip/Tooltip.styled.d.ts.map +1 -0
  191. package/esm/components/ui/tooltip/Tooltip.styled.js +28 -0
  192. package/esm/components/ui/tooltip/Tooltip.styled.js.map +1 -0
  193. package/esm/index.d.ts +1 -0
  194. package/esm/index.d.ts.map +1 -1
  195. package/esm/index.js +1 -0
  196. package/esm/index.js.map +1 -1
  197. package/package.json +2 -1
  198. package/pnpm-workspace.yaml +3 -0
  199. package/src/bugfixes/ToastsFromModal.stories.tsx +59 -0
  200. package/src/components/ui/drawer/Drawer.stories.tsx +143 -59
  201. package/src/components/ui/drawer/Drawer.styled.ts +13 -0
  202. package/src/components/ui/drawer/Drawer.tsx +214 -20
  203. package/src/components/ui/toaster/Toaster.tsx +12 -2
  204. package/src/components/ui/tooltip/Tooltip.stories.tsx +285 -0
  205. package/src/components/ui/tooltip/Tooltip.styled.ts +36 -0
  206. package/src/components/ui/tooltip/Tooltip.tsx +195 -0
  207. package/src/index.ts +1 -0
@@ -0,0 +1,285 @@
1
+ /* eslint-disable max-lines */
2
+ import React, { useCallback, useState } from "react";
3
+
4
+ import type { Meta, StoryObj } from "@storybook/react-vite";
5
+
6
+ import { Gap } from "../../utils/Gap";
7
+ import { Button } from "../button/Button";
8
+ import { Drawer } from "../drawer/Drawer";
9
+ import { Modal } from "../modal/Modal";
10
+ import { ModalButtons } from "../modal/ModalButtons";
11
+ import { Tooltip, TooltipProvider } from "./Tooltip";
12
+
13
+ const meta: Meta<typeof Tooltip> = {
14
+ title: "Components/UI/Tooltip",
15
+ component: Tooltip,
16
+ tags: ["autodocs", "ui"],
17
+ argTypes: {
18
+ side: {
19
+ control: "inline-radio",
20
+ options: ["top", "right", "bottom", "left"],
21
+ },
22
+ align: {
23
+ control: "inline-radio",
24
+ options: ["start", "center", "end"],
25
+ },
26
+ sideOffset: { control: { type: "number", min: 0, max: 40 } },
27
+ alignOffset: { control: { type: "number", min: -40, max: 40 } },
28
+ delayDuration: { control: { type: "number", min: 0, max: 2000, step: 100 } },
29
+ avoidCollisions: { type: "boolean" },
30
+ arrow: { type: "boolean" },
31
+ content: { control: "text" },
32
+ children: { table: { disable: true } },
33
+ open: { table: { disable: true } },
34
+ defaultOpen: { table: { disable: true } },
35
+ onOpenChange: { table: { disable: true } },
36
+ },
37
+ };
38
+
39
+ type Story = StoryObj<typeof Tooltip>;
40
+
41
+ /**
42
+ * Playground. Tweak `side`, `align`, `sideOffset`, `delayDuration`, `arrow`,
43
+ * and `avoidCollisions` from the controls panel.
44
+ */
45
+ const Primary: Story = {
46
+ args: {
47
+ content: "Save your changes",
48
+ side: "top",
49
+ align: "center",
50
+ sideOffset: 6,
51
+ alignOffset: 0,
52
+ delayDuration: 500,
53
+ avoidCollisions: true,
54
+ arrow: true,
55
+ },
56
+ render: (args) => (
57
+ <div style={{ padding: 80, display: "flex", justifyContent: "center" }}>
58
+ <Tooltip {...args}>
59
+ <Button>Hover me</Button>
60
+ </Tooltip>
61
+ </div>
62
+ ),
63
+ };
64
+
65
+ /**
66
+ * All four sides shown at once.
67
+ */
68
+ const AllSides: Story = {
69
+ render: () => (
70
+ <div
71
+ style={{
72
+ padding: 120,
73
+ display: "grid",
74
+ gridTemplateColumns: "repeat(2, max-content)",
75
+ gap: 40,
76
+ justifyContent: "center",
77
+ }}
78
+ >
79
+ <Tooltip content={"Top"} side={"top"} arrow={true}>
80
+ <Button>Top</Button>
81
+ </Tooltip>
82
+ <Tooltip content={"Right"} side={"right"} arrow={true}>
83
+ <Button>Right</Button>
84
+ </Tooltip>
85
+ <Tooltip content={"Bottom"} side={"bottom"} arrow={true}>
86
+ <Button>Bottom</Button>
87
+ </Tooltip>
88
+ <Tooltip content={"Left"} side={"left"} arrow={true}>
89
+ <Button>Left</Button>
90
+ </Tooltip>
91
+ </div>
92
+ ),
93
+ };
94
+
95
+ /**
96
+ * `TooltipProvider` enables group-delay behavior: once any tooltip is open, the next ones
97
+ * appear without waiting for `delayDuration` (until `skipDelayDuration` elapses with all
98
+ * tooltips closed). Hover one button slowly, then sweep across the rest — they pop instantly.
99
+ */
100
+ const GroupDelay: Story = {
101
+ render: () => (
102
+ <TooltipProvider delayDuration={500} skipDelayDuration={400}>
103
+ <div style={{ padding: 80, display: "flex", gap: 12, justifyContent: "center" }}>
104
+ <Tooltip content={"First"}>
105
+ <Button>One</Button>
106
+ </Tooltip>
107
+ <Tooltip content={"Second"}>
108
+ <Button>Two</Button>
109
+ </Tooltip>
110
+ <Tooltip content={"Third"}>
111
+ <Button>Three</Button>
112
+ </Tooltip>
113
+ <Tooltip content={"Fourth"}>
114
+ <Button>Four</Button>
115
+ </Tooltip>
116
+ </div>
117
+ </TooltipProvider>
118
+ ),
119
+ };
120
+
121
+ /**
122
+ * Triggers placed near viewport edges. With `avoidCollisions` (default) the tooltip
123
+ * flips to fit; toggle the arg in the playground story to see the difference.
124
+ */
125
+ const NearViewportEdge: Story = {
126
+ render: () => (
127
+ <div style={{ height: "90vh", position: "relative" }}>
128
+ <div style={{ position: "absolute", top: 4, left: 4 }}>
129
+ <Tooltip content={"I would overflow up-left, so I flip"} side={"top"} arrow={true}>
130
+ <Button>Top-left corner</Button>
131
+ </Tooltip>
132
+ </div>
133
+ <div style={{ position: "absolute", top: 4, right: 4 }}>
134
+ <Tooltip content={"I would overflow up-right, so I flip"} side={"top"} arrow={true}>
135
+ <Button>Top-right corner</Button>
136
+ </Tooltip>
137
+ </div>
138
+ <div style={{ position: "absolute", bottom: 4, left: 4 }}>
139
+ <Tooltip content={"I would overflow down-left"} side={"bottom"} arrow={true}>
140
+ <Button>Bottom-left corner</Button>
141
+ </Tooltip>
142
+ </div>
143
+ <div style={{ position: "absolute", bottom: 4, right: 4 }}>
144
+ <Tooltip content={"I would overflow down-right"} side={"bottom"} arrow={true}>
145
+ <Button>Bottom-right corner</Button>
146
+ </Tooltip>
147
+ </div>
148
+ </div>
149
+ ),
150
+ };
151
+
152
+ /**
153
+ * Long content wraps to the configured `maxWidth`.
154
+ */
155
+ const LongContent: Story = {
156
+ render: () => (
157
+ <div style={{ padding: 80, display: "flex", justifyContent: "center" }}>
158
+ <Tooltip
159
+ content={"This tooltip has a longer description that wraps onto multiple lines once it "
160
+ + "exceeds the maximum content width set in the styles."}
161
+ side={"bottom"}
162
+ >
163
+ <Button>Long description</Button>
164
+ </Tooltip>
165
+ </div>
166
+ ),
167
+ };
168
+
169
+ /**
170
+ * Edge case: tooltip rendered from inside a `Modal`. Because the content is portalled to
171
+ * `document.body`, it escapes the modal's stacking context and renders above it.
172
+ */
173
+ const InModal: Story = {
174
+ render: () => {
175
+ const [open, setOpen] = useState(false);
176
+
177
+ const handleOpen = useCallback(() => { setOpen(true); }, []);
178
+ const handleClose = useCallback(() => { setOpen(false); }, []);
179
+
180
+ return (
181
+ <div>
182
+ <Button onClick={handleOpen}>Open modal</Button>
183
+ <Modal isOpen={open} onClose={handleClose} title={"Tooltip in a modal"}>
184
+ <Gap>
185
+ <div>Hover the buttons below — the tooltip should appear above the modal.</div>
186
+ <div style={{ display: "flex", gap: 12 }}>
187
+ <Tooltip content={"Above modal, side=top"} side={"top"} arrow={true}>
188
+ <Button>Top</Button>
189
+ </Tooltip>
190
+ <Tooltip content={"Above modal, side=right"} side={"right"} arrow={true}>
191
+ <Button>Right</Button>
192
+ </Tooltip>
193
+ <Tooltip content={"Above modal, side=bottom"} side={"bottom"} arrow={true}>
194
+ <Button>Bottom</Button>
195
+ </Tooltip>
196
+ </div>
197
+ </Gap>
198
+ <ModalButtons>
199
+ <ModalButtons.Button variant={"main"} onClick={handleClose}>Close</ModalButtons.Button>
200
+ </ModalButtons>
201
+ </Modal>
202
+ </div>
203
+ );
204
+ },
205
+ };
206
+
207
+ /**
208
+ * Edge case: tooltip rendered from inside a `Drawer`. Same portal behavior — content
209
+ * floats above the drawer regardless of the drawer's stacking context.
210
+ */
211
+ const InDrawer: Story = {
212
+ render: () => {
213
+ const [open, setOpen] = useState(false);
214
+
215
+ const handleOpen = useCallback(() => { setOpen(true); }, []);
216
+ const handleClose = useCallback(() => { setOpen(false); }, []);
217
+
218
+ return (
219
+ <div>
220
+ <Button onClick={handleOpen}>Open drawer</Button>
221
+ <Drawer isOpen={open} onClose={handleClose}>
222
+ <div style={{ padding: 24, display: "flex", flexDirection: "column", gap: 16 }}>
223
+ <div>Tooltips work from inside a drawer.</div>
224
+ <Tooltip content={"Hello from inside the drawer"} side={"bottom"} arrow={true}>
225
+ <Button>Hover me</Button>
226
+ </Tooltip>
227
+ <Button onClick={handleClose}>Close drawer</Button>
228
+ </div>
229
+ </Drawer>
230
+ </div>
231
+ );
232
+ },
233
+ };
234
+
235
+ /**
236
+ * Controlled tooltip — open state is driven by parent. Useful for tutorials,
237
+ * onboarding tours, or pinning a tooltip open while a user interacts elsewhere.
238
+ */
239
+ const Controlled: Story = {
240
+ render: () => {
241
+ const [open, setOpen] = useState(false);
242
+
243
+ const handleToggle = useCallback(() => { setOpen(p => !p); }, []);
244
+
245
+ return (
246
+ <div style={{ padding: 80, display: "flex", gap: 16, justifyContent: "center" }}>
247
+ <Button onClick={handleToggle}>{open ? "Hide" : "Show"} tooltip</Button>
248
+ <Tooltip content={"I'm controlled"} open={open} side={"right"}>
249
+ <Button>Target</Button>
250
+ </Tooltip>
251
+ </div>
252
+ );
253
+ },
254
+ };
255
+
256
+ /**
257
+ * Tooltip wrapping a non-button trigger. Any element that can receive a ref works
258
+ * — here, an inline word with a dotted underline.
259
+ */
260
+ const InlineTrigger: Story = {
261
+ render: () => (
262
+ <div style={{ padding: 80, fontSize: 18, lineHeight: 1.6, maxWidth: 500, margin: "0 auto" }}>
263
+ This sentence contains a{" "}
264
+ <Tooltip content={"A short, contextual explanation."}>
265
+ <span style={{ borderBottom: "1px dotted currentColor", cursor: "help" }}>
266
+ glossed term
267
+ </span>
268
+ </Tooltip>
269
+ {" "}that reveals more on hover or focus.
270
+ </div>
271
+ ),
272
+ };
273
+
274
+ export default meta;
275
+ export {
276
+ Primary,
277
+ AllSides,
278
+ GroupDelay,
279
+ NearViewportEdge,
280
+ LongContent,
281
+ InModal,
282
+ InDrawer,
283
+ Controlled,
284
+ InlineTrigger,
285
+ };
@@ -0,0 +1,36 @@
1
+ import * as RadixTooltip from "@radix-ui/react-tooltip";
2
+
3
+ import { dimensionsPxToRem, fontPxToRem, keyframes, pxToRem, styled } from "../../../theme";
4
+
5
+ const fadeIn = keyframes({
6
+ from: { opacity: 0, transform: "scale(0.96)" },
7
+ to: { opacity: 1, transform: "scale(1)" },
8
+ });
9
+
10
+ const StyledContent = styled(RadixTooltip.Content, {
11
+ "zIndex": 10,
12
+ "background": "$text2",
13
+ "color": "$background",
14
+ "padding": `${dimensionsPxToRem(18)} ${dimensionsPxToRem(36)}`,
15
+ "borderRadius": dimensionsPxToRem(12),
16
+ "fontSize": fontPxToRem(24),
17
+ "lineHeight": 1.3,
18
+ "maxWidth": pxToRem(280),
19
+ "boxShadow": `0 ${pxToRem(2)} ${pxToRem(8)} rgba(0, 0, 0, 0.18)`,
20
+ "userSelect": "none",
21
+ "transformOrigin": "var(--radix-tooltip-content-transform-origin)",
22
+ "animation": `${fadeIn.toString()} 120ms ease-out`,
23
+
24
+ "&[data-state=\"closed\"]": {
25
+ animationDirection: "reverse",
26
+ },
27
+ });
28
+
29
+ const StyledArrow = styled(RadixTooltip.Arrow, {
30
+ fill: "$text2",
31
+ });
32
+
33
+ export {
34
+ StyledContent,
35
+ StyledArrow,
36
+ };
@@ -0,0 +1,195 @@
1
+ import React, { forwardRef } from "react";
2
+
3
+ import * as RadixTooltip from "@radix-ui/react-tooltip";
4
+
5
+ import { StyledArrow, StyledContent } from "./Tooltip.styled";
6
+
7
+ type Side = "top" | "right" | "bottom" | "left";
8
+ type Align = "start" | "center" | "end";
9
+
10
+ interface TooltipProviderProps {
11
+ /**
12
+ * The duration from when the pointer enters the trigger until the tooltip opens, in milliseconds.
13
+ * Default: `500`.
14
+ */
15
+ delayDuration?: number;
16
+ /**
17
+ * If a tooltip has opened, the duration during which other tooltips open instantly
18
+ * (without waiting for `delayDuration`), in milliseconds.
19
+ * Default: `300`.
20
+ */
21
+ skipDelayDuration?: number;
22
+ /**
23
+ * When `true`, the tooltip content stays open while the pointer is over it.
24
+ * Set to `false` for stricter "hover the trigger only" behavior.
25
+ * Default: `true`.
26
+ */
27
+ disableHoverableContent?: boolean;
28
+ children: React.ReactNode;
29
+ }
30
+
31
+ /**
32
+ * Required wrapper for `<Tooltip>`. Render it once near the root of your app — usually
33
+ * around (or just inside) your top-level layout. It enables group-delay behavior via
34
+ * `skipDelayDuration`: once one tooltip in the subtree has been seen, the next ones
35
+ * open without waiting for `delayDuration`.
36
+ *
37
+ * A single `<Tooltip>` without a `<TooltipProvider>` ancestor will throw at runtime.
38
+ */
39
+ const TooltipProvider = (props: TooltipProviderProps) => {
40
+ const radixProps: Omit<React.ComponentProps<typeof RadixTooltip.Provider>, "children"> = {};
41
+ if (props.delayDuration !== undefined) { radixProps.delayDuration = props.delayDuration; }
42
+ if (props.skipDelayDuration !== undefined) { radixProps.skipDelayDuration = props.skipDelayDuration; }
43
+ if (props.disableHoverableContent !== undefined) {
44
+ radixProps.disableHoverableContent = props.disableHoverableContent;
45
+ }
46
+
47
+ return (
48
+ <RadixTooltip.Provider {...radixProps}>
49
+ {props.children}
50
+ </RadixTooltip.Provider>
51
+ );
52
+ };
53
+
54
+ interface TooltipProps {
55
+ /**
56
+ * The content to render inside the tooltip.
57
+ */
58
+ content: React.ReactNode;
59
+ /**
60
+ * Preferred side of the trigger to render the tooltip against. Will be flipped
61
+ * automatically on collision unless `avoidCollisions` is `false`.
62
+ * Default: `"top"`.
63
+ */
64
+ side?: Side;
65
+ /**
66
+ * Distance in pixels between the trigger and the tooltip.
67
+ * Default: `6`.
68
+ */
69
+ sideOffset?: number;
70
+ /**
71
+ * Alignment along the chosen side.
72
+ * Default: `"center"`.
73
+ */
74
+ align?: Align;
75
+ /**
76
+ * Offset in pixels from the `align` edge.
77
+ * Default: `0`.
78
+ */
79
+ alignOffset?: number;
80
+ /**
81
+ * The duration from when the pointer enters the trigger until the tooltip opens.
82
+ * Overrides the provider's `delayDuration` for this tooltip only.
83
+ */
84
+ delayDuration?: number;
85
+ /**
86
+ * When `true`, the tooltip will not flip to the opposite side on collision.
87
+ * Default: `true` (collisions are avoided).
88
+ */
89
+ avoidCollisions?: boolean;
90
+ /**
91
+ * When `true`, renders an arrow pointing at the trigger.
92
+ * Default: `true`.
93
+ */
94
+ arrow?: boolean;
95
+ /**
96
+ * Controlled open state. If omitted, the tooltip is uncontrolled.
97
+ */
98
+ open?: boolean;
99
+ /**
100
+ * Initial open state for the uncontrolled mode.
101
+ */
102
+ defaultOpen?: boolean;
103
+ /**
104
+ * Called when the open state changes.
105
+ */
106
+ onOpenChange?: (open: boolean) => void;
107
+ /**
108
+ * Additional class name applied to the tooltip content.
109
+ */
110
+ className?: string;
111
+ /**
112
+ * The element that triggers the tooltip on hover/focus. Must be a single React element
113
+ * that can receive a ref and event handlers (a DOM element or a component using forwardRef).
114
+ */
115
+ children: React.ReactElement;
116
+ }
117
+
118
+ /**
119
+ * A tooltip that appears on hover or keyboard focus of its child trigger.
120
+ *
121
+ * Powered by Radix UI under the hood, so accessibility (ARIA, focus, escape, group delay)
122
+ * is handled. The content is portalled to `document.body`, so it escapes stacking contexts
123
+ * and renders above modals/drawers.
124
+ *
125
+ * Requires a `<TooltipProvider>` somewhere up the tree.
126
+ *
127
+ * @example
128
+ * ```tsx
129
+ * <TooltipProvider>
130
+ * <Tooltip content="Save changes" side="bottom">
131
+ * <Button>Save</Button>
132
+ * </Tooltip>
133
+ * </TooltipProvider>
134
+ * ```
135
+ */
136
+ // eslint-disable-next-line react/no-multi-comp
137
+ const Tooltip = forwardRef<HTMLDivElement, TooltipProps>((props, ref) => {
138
+ const {
139
+ content,
140
+ side = "top",
141
+ // eslint-disable-next-line @typescript-eslint/no-magic-numbers
142
+ sideOffset = 6,
143
+ align = "center",
144
+ alignOffset = 0,
145
+ delayDuration,
146
+ avoidCollisions = true,
147
+ arrow = true,
148
+ open,
149
+ defaultOpen,
150
+ onOpenChange,
151
+ className,
152
+ children,
153
+ } = props;
154
+
155
+ const rootProps: React.ComponentProps<typeof RadixTooltip.Root> = {};
156
+ if (open !== undefined) { rootProps.open = open; }
157
+ if (defaultOpen !== undefined) { rootProps.defaultOpen = defaultOpen; }
158
+ if (onOpenChange !== undefined) { rootProps.onOpenChange = onOpenChange; }
159
+ if (delayDuration !== undefined) { rootProps.delayDuration = delayDuration; }
160
+
161
+ return (
162
+ <RadixTooltip.Root {...rootProps}>
163
+ <RadixTooltip.Trigger asChild={true}>
164
+ {children}
165
+ </RadixTooltip.Trigger>
166
+ <RadixTooltip.Portal>
167
+ <StyledContent
168
+ ref={ref}
169
+ side={side}
170
+ sideOffset={sideOffset}
171
+ align={align}
172
+ alignOffset={alignOffset}
173
+ avoidCollisions={avoidCollisions}
174
+ className={className}
175
+ >
176
+ {content}
177
+ {/* eslint-disable-next-line react/jsx-no-leaked-render */}
178
+ {arrow && <StyledArrow width={10} height={5} />}
179
+ </StyledContent>
180
+ </RadixTooltip.Portal>
181
+ </RadixTooltip.Root>
182
+ );
183
+ });
184
+
185
+ Tooltip.displayName = "Tooltip";
186
+ Tooltip.toString = () => StyledContent.toString();
187
+
188
+ const TooltipContentSelector = StyledContent.toString();
189
+
190
+ export {
191
+ Tooltip,
192
+ TooltipProvider,
193
+ TooltipContentSelector,
194
+ };
195
+ export type { TooltipProps, TooltipProviderProps };
package/src/index.ts CHANGED
@@ -42,6 +42,7 @@ export * from "./components/ui/stats/Stats";
42
42
  export * from "./components/ui/tabs/Selector";
43
43
  export * from "./components/ui/toaster/Toaster";
44
44
  export * from "./components/ui/toolButton/ToolButton";
45
+ export * from "./components/ui/tooltip/Tooltip";
45
46
 
46
47
  export * from "./components/utils/Gap";
47
48
  export * from "./components/utils/HandleEsc";