Skip to content

Commit

Permalink
a solution without width
Browse files Browse the repository at this point in the history
  • Loading branch information
hannojg committed Mar 6, 2023
1 parent c005817 commit 37a4d7d
Show file tree
Hide file tree
Showing 2 changed files with 14 additions and 84 deletions.
83 changes: 11 additions & 72 deletions src/components/Tooltip/TooltipRenderedOnPageBody.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,8 @@ import React from 'react';
import PropTypes from 'prop-types';
import {Animated, View} from 'react-native';
import ReactDOM from 'react-dom';
import _ from 'underscore';
import getTooltipStyles from '../../styles/getTooltipStyles';
import Text from '../Text';
import styles from '../../styles/styles';

const propTypes = {
/** Window width */
Expand Down Expand Up @@ -64,48 +62,16 @@ class TooltipRenderedOnPageBody extends React.PureComponent {
constructor(props) {
super(props);
this.state = {
// The width of tooltip's inner content
tooltipContentWidth: 0,

// The width and height of the tooltip itself
tooltipWidth: 0,
tooltipHeight: 0,
};

this.measureTooltip = this.measureTooltip.bind(this);
this.updateTooltipContentWidth = this.updateTooltipContentWidth.bind(this);
}

componentDidMount() {
this.updateTooltipContentWidth();
}

componentDidUpdate(prevProps) {
if (prevProps.text === this.props.text) {
return;
}

// Reset the tooltip text width to 0 so that we can measure it again.
// eslint-disable-next-line react/no-did-update-set-state
this.setState({tooltipContentWidth: 0}, this.updateTooltipContentWidth);
}

/**
* Updates the tooltipContentWidth state with the width of the tooltip's inner content.
* Can be called in onLayout, or if you are using text just call it after the first render
* to get the text's width.
*
* @param {Object} [event]
*/
updateTooltipContentWidth(event) {
if (!this.textRef && !event) {
return;
}

const widthFromEvent = _.get(event, ['nativeEvent', 'layout', 'width']);
this.setState({
tooltipContentWidth: widthFromEvent || this.textRef.offsetWidth,
});
// this.updateTooltipContentWidth();
}

/**
Expand Down Expand Up @@ -137,7 +103,6 @@ class TooltipRenderedOnPageBody extends React.PureComponent {
this.props.maxWidth,
this.state.tooltipWidth,
this.state.tooltipHeight,
this.state.tooltipContentWidth,
this.props.shiftHorizontal,
this.props.shiftVertical,
);
Expand All @@ -148,49 +113,23 @@ class TooltipRenderedOnPageBody extends React.PureComponent {
} else {
content = (
<Text numberOfLines={this.props.numberOfLines} style={tooltipTextStyle}>
<Text
style={tooltipTextStyle}
ref={(ref) => {
// Once the text for the tooltip first renders, update the width of the tooltip dynamically to fit the width of the text.
// Note that we can't have this code in componentDidMount because the ref for the text won't be set until after the first render
if (this.textRef) {
return;
}

this.textRef = ref;
this.updateTooltipContentWidth();
}}
>
<Text style={tooltipTextStyle}>
{this.props.text}
</Text>
</Text>
);
}

const isCustomContent = _.isFunction(this.props.renderTooltipContent);

return ReactDOM.createPortal(
<>
{/* If rendering custom content always render an invisible version of
it to detect layout size changes, if the content updates. */}
{isCustomContent && (
<View
style={styles.invisible}
onLayout={this.updateTooltipContentWidth}
>
{this.props.renderTooltipContent()}
</View>
)}
<Animated.View
onLayout={this.measureTooltip}
style={[tooltipWrapperStyle, animationStyle]}
>
{content}
<View style={pointerWrapperStyle}>
<View style={pointerStyle} />
</View>
</Animated.View>
</>,
<Animated.View
onLayout={this.measureTooltip}
style={[tooltipWrapperStyle, animationStyle]}
>
{content}
<View style={pointerWrapperStyle}>
<View style={pointerStyle} />
</View>
</Animated.View>,
document.querySelector('body'),
);
}
Expand Down
15 changes: 3 additions & 12 deletions src/styles/getTooltipStyles.js
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,6 @@ function computeHorizontalShift(windowWidth, xOffset, componentWidth, tooltipWid
* @param {Number} maxWidth - The tooltip's max width.
* @param {Number} tooltipWidth - The width of the tooltip itself.
* @param {Number} tooltipHeight - The height of the tooltip itself.
* @param {Number} tooltipContentWidth - The tooltip's inner content width.
* @param {Number} [manualShiftHorizontal] - Any additional amount to manually shift the tooltip to the left or right.
* A positive value shifts it to the right,
* and a negative value shifts it to the left.
Expand All @@ -81,7 +80,6 @@ export default function getTooltipStyles(
maxWidth,
tooltipWidth,
tooltipHeight,
tooltipContentWidth,
manualShiftHorizontal = 0,
manualShiftVertical = 0,
) {
Expand All @@ -97,21 +95,14 @@ export default function getTooltipStyles(
const tooltipVerticalPadding = spacing.pv1;
const tooltipFontSize = variables.fontSizeSmall;

// We get wrapper width based on the tooltip's inner text width so the wrapper is just big enough to fit text and prevent white space.
// If the text width is less than the maximum available width, add horizontal padding.
// Note: tooltipContentWidth ignores the fractions (OffsetWidth) so add 1px to fit the text properly.
const wrapperWidth = tooltipContentWidth && tooltipContentWidth < maxWidth
? tooltipContentWidth + (spacing.ph2.paddingHorizontal * 2) + 1
: maxWidth;

// Hide the tooltip entirely if it's position hasn't finished measuring yet. This prevents UI jank where the tooltip flashes in the top left corner of the screen.
const opacity = (xOffset === 0 && yOffset === 0) ? 0 : 1;

return {
animationStyle: {
// remember Transform causes a new Local cordinate system
// remember Transform causes a new Local coordinate system
// https://drafts.csswg.org/css-transforms-1/#transform-rendering
// so Position fixed children will be relative to this new Local cordinate system
// so Position fixed children will be relative to this new Local coordinate system
transform: [{
scale: currentSize,
}],
Expand All @@ -123,7 +114,7 @@ export default function getTooltipStyles(
...tooltipVerticalPadding,
...spacing.ph2,
zIndex: variables.tooltipzIndex,
width: wrapperWidth,
maxWidth,

// Because it uses fixed positioning, the top-left corner of the tooltip is aligned
// with the top-left corner of the window by default.
Expand Down

0 comments on commit 37a4d7d

Please sign in to comment.