Skip to content

Commit

Permalink
feat(react): improve tooltip placement and offset calculations (#897)
Browse files Browse the repository at this point in the history
* feat(react): improve tooltip placement and offset calculations

* ci: add changeset `tonic-ui-893b.md`
  • Loading branch information
cheton authored Jul 21, 2024
1 parent 0736f0e commit 20fd94d
Show file tree
Hide file tree
Showing 10 changed files with 323 additions and 70 deletions.
5 changes: 5 additions & 0 deletions .changeset/tonic-ui-893b.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@tonic-ui/react": patch
---

feat(react): improve tooltip placement and offset calculations
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ const App = () => {
return (
<OverflowTooltip
label="This is a tooltip"
nextToCursor={false}
placement="bottom"
>
This text string will be truncated when exceeding its container width. To see this in action, try resizing your browser viewport. If the text overflows, a tooltip will appear, displaying the full content.
</OverflowTooltip>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,18 @@
import {
Flex,
TextLabel,
} from '@tonic-ui/react';
import {
AlertIcon,
} from '@tonic-ui/react-icons';

# OverflowTooltip

The `OverflowTooltip` component is a tooltip that only displays when text overflows its container.

> <TextLabel><Flex alignItems="center" columnGap="2x" mb="2x"><AlertIcon /> IMPORTANT NOTICE</Flex></TextLabel>
> By default, the `OverflowTooltip` component has the `placement` prop set to `bottom-end` and the `offset` prop set to `[8, 12]`. In this setup, the first value in the `offset` array controls the skidding, while the second value determines the distance. You may need to modify the `placement` and `offset` props to suit your specific requirements better.
## Import

```js
Expand Down Expand Up @@ -38,12 +49,6 @@ To resolve this issue, set `PopperProps={{ usePortal: true }}`.

{render('./faq-misalignment-with-menu-items')}

### Aligning the `OverflowTooltip` relative to the reference element

By default, the `OverflowTooltip` component is positioned next to the cursor with the `nextToCursor` prop set to `true`. To align the tooltip relative to the reference element, set the `nextToCursor` prop to `false`.

{render('./faq-relative-to-reference-element')}

## Props

### OverflowTooltip
Expand All @@ -70,8 +75,8 @@ By default, the `OverflowTooltip` component is positioned next to the cursor wit
| label | string \| ReactNode | | If the tooltip label is a blank or empty string, the tooltip will not display. |
| leaveDelay | number | 0 | The delay in milliseconds before the tooltip disappears. |
| nextToCursor | boolean | true | If `true`, the tooltip will be positioned next to the cursor. |
| offset | [skidding, distance] | [0, 8] | The skidding and distance of the tooltip. |
| offset | [skidding, distance] | [8, 12] | The skidding and distance of the tooltip. |
| onClose| function | | Callback fired when the tooltip is closed. |
| onOpen | function | | Callback fired when the tooltip is opened. |
| placement | PopperJS.Placement | 'bottom' | Position the tooltip relative to the trigger element as well as surrounding elements. One of: 'top', 'bottom', 'right', 'left', 'top-start', 'top-end', 'bottom-start', 'bottom-end', 'right-start', 'right-end', 'left-start', 'left-end' |
| placement | PopperJS.Placement | 'bottom-end' | Position the tooltip relative to the trigger element as well as surrounding elements. One of: 'top', 'bottom', 'right', 'left', 'top-start', 'top-end', 'bottom-start', 'bottom-end', 'right-start', 'right-end', 'left-start', 'left-end' |
| shouldWrapChildren | boolean | false | If `true`, the tooltip will be wrapped in a `Box` component. Otherwise, you have to ensure tooltip has only one child node. |
17 changes: 14 additions & 3 deletions packages/react-docs/pages/components/tooltip/index.page.mdx
Original file line number Diff line number Diff line change
@@ -1,8 +1,17 @@
import {
Flex,
TextLabel,
} from '@tonic-ui/react';
import {
AlertIcon,
} from '@tonic-ui/react-icons';

# Tooltip

A tooltip is a brief, informative message that appears when a user interacts with an element. Tooltips are usually initiated in one of two ways: through a mouse-hover or keyboard-hover action.

The `Tooltip` component follows the [WAI-ARIA](https://www.w3.org/TR/wai-aria-practices/#tooltip) Tooltip pattern.
> <TextLabel><Flex alignItems="center" columnGap="2x" mb="2x"><AlertIcon /> IMPORTANT NOTICE</Flex></TextLabel>
> By default, the `Tooltip` component has the `placement` prop set to `bottom` and the `offset` prop set to `[0, 8]`. In this setup, the first value in the `offset` array controls the skidding, while the second value determines the distance. You may need to modify the `placement` and `offset` props to suit your specific requirements better.
## Import

Expand Down Expand Up @@ -97,18 +106,20 @@ The `distance` number controls the distance between the `TooltipContent` and the

{render('./positioning-offset')}

> By default, the tooltip's offset is set to `[0, 8]`, where the first value represents the `skidding` and the second value represents the `distance`.<br/>In the `nextToCursor` and `followCursor` examples below, the `distance` value is set to 16. You may need to adjust this value to better suit your specific requirements.
#### Using the `nextToCursor` prop

The `nextToCursor` prop positions the tooltip next to the cursor.

In this example, the `placement` prop is set to `bottom-end`, and the `offset` prop is configured as `[8, 12]`. You can modify these values to observe different behaviors.

{render('./positioning-next-cursor')}

#### Using the `followCursor` prop

The `followCursor` prop makes the tooltip follow the cursor as it moves.

In this example, the `placement` prop is set to `bottom-end`, and the `offset` prop is configured as `[8, 12]`. You can modify these values to observe different behaviors.

{render('./positioning-follow-cursor')}

### Customizing tooltip appearance
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,76 @@
import { Box, Divider, Flex, Text, TextLabel, Tooltip, useColorStyle } from '@tonic-ui/react';
import {
Box,
Divider,
Flex,
Menu,
MenuButton,
MenuItem,
MenuList,
Text,
TextLabel,
Tooltip,
useColorStyle,
} from '@tonic-ui/react';
import {
CheckSIcon,
} from '@tonic-ui/react-icons';
import React, { useState } from 'react';

const FormGroup = (props) => (
<Box mb="4x" {...props} />
);

const useSelection = (defaultValue) => {
const [value, setValue] = useState(defaultValue);
const changeBy = (value) => () => setValue(value);
return [value, changeBy];
};

const App = () => {
const [colorStyle] = useColorStyle();
const [skidding, setSkidding] = useState(0);
const [distance, setDistance] = useState(16);
const [placement, changePlacementBy] = useSelection('bottom-end');
const [skidding, setSkidding] = useState(8);
const [distance, setDistance] = useState(12);

return (
<>
<Box mb="4x">
<FormGroup>
<Box mb="2x">
<TextLabel>
placement
</TextLabel>
</Box>
<Menu>
<MenuButton
variant="secondary"
minWidth={150}
>
{placement}
</MenuButton>
<MenuList
width="max-content"
minWidth={150}
>
{[
'top', 'top-start', 'top-end',
'bottom', 'bottom-start', 'bottom-end',
'left', 'left-start', 'left-end',
'right', 'right-start', 'right-end',
].map(_placement => (
<MenuItem
key={_placement}
onClick={changePlacementBy(_placement)}
>
<Flex columnGap="2x">
{placement === _placement ? <CheckSIcon /> : <Box width="4x" />}
{_placement}
</Flex>
</MenuItem>
))}
</MenuList>
</Menu>
</FormGroup>
<FormGroup>
<Box mb="2x">
<TextLabel>skidding</TextLabel>
</Box>
Expand All @@ -23,8 +85,8 @@ const App = () => {
/>
<Text>{skidding}</Text>
</Flex>
</Box>
<Box mb="4x">
</FormGroup>
<FormGroup>
<Box mb="2x">
<TextLabel>distance</TextLabel>
</Box>
Expand All @@ -39,20 +101,21 @@ const App = () => {
/>
<Text>{distance}</Text>
</Flex>
</Box>
</FormGroup>
<Divider my="4x" />
<Tooltip
label="This is a tooltip"
followCursor
offset={[skidding, distance]}
placement={placement}
>
<Flex
sx={{
border: 1,
backgroundColor: colorStyle.background.secondary,
borderColor: colorStyle.divider,
width: 100,
height: 100,
width: 240,
height: 180,
alignItems: 'center',
justifyContent: 'center',
}}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,76 @@
import { Box, Divider, Flex, Text, TextLabel, Tooltip, useColorStyle } from '@tonic-ui/react';
import {
Box,
Divider,
Flex,
Menu,
MenuButton,
MenuItem,
MenuList,
Text,
TextLabel,
Tooltip,
useColorStyle,
} from '@tonic-ui/react';
import {
CheckSIcon,
} from '@tonic-ui/react-icons';
import React, { useState } from 'react';

const FormGroup = (props) => (
<Box mb="4x" {...props} />
);

const useSelection = (defaultValue) => {
const [value, setValue] = useState(defaultValue);
const changeBy = (value) => () => setValue(value);
return [value, changeBy];
};

const App = () => {
const [colorStyle] = useColorStyle();
const [skidding, setSkidding] = useState(0);
const [distance, setDistance] = useState(16);
const [placement, changePlacementBy] = useSelection('bottom-end');
const [skidding, setSkidding] = useState(8);
const [distance, setDistance] = useState(12);

return (
<>
<Box mb="4x">
<FormGroup>
<Box mb="2x">
<TextLabel>
placement
</TextLabel>
</Box>
<Menu>
<MenuButton
variant="secondary"
minWidth={150}
>
{placement}
</MenuButton>
<MenuList
width="max-content"
minWidth={150}
>
{[
'top', 'top-start', 'top-end',
'bottom', 'bottom-start', 'bottom-end',
'left', 'left-start', 'left-end',
'right', 'right-start', 'right-end',
].map(_placement => (
<MenuItem
key={_placement}
onClick={changePlacementBy(_placement)}
>
<Flex columnGap="2x">
{placement === _placement ? <CheckSIcon /> : <Box width="4x" />}
{_placement}
</Flex>
</MenuItem>
))}
</MenuList>
</Menu>
</FormGroup>
<FormGroup>
<Box mb="2x">
<TextLabel>skidding</TextLabel>
</Box>
Expand All @@ -23,8 +85,8 @@ const App = () => {
/>
<Text>{skidding}</Text>
</Flex>
</Box>
<Box mb="4x">
</FormGroup>
<FormGroup>
<Box mb="2x">
<TextLabel>distance</TextLabel>
</Box>
Expand All @@ -39,20 +101,21 @@ const App = () => {
/>
<Text>{distance}</Text>
</Flex>
</Box>
</FormGroup>
<Divider my="4x" />
<Tooltip
label="This is a tooltip"
nextToCursor
offset={[skidding, distance]}
placement={placement}
>
<Flex
sx={{
backgroundColor: colorStyle.background.secondary,
border: 1,
borderColor: colorStyle.divider,
width: 100,
height: 100,
width: 240,
height: 180,
alignItems: 'center',
justifyContent: 'center',
}}
Expand Down
Loading

0 comments on commit 20fd94d

Please sign in to comment.