@khanacademy/wonder-blocks-tooltip 4.1.85 → 4.2.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/CHANGELOG.md +6 -0
- package/dist/components/tooltip-bubble.d.ts +6 -1
- package/dist/components/tooltip-tail.d.ts +7 -1
- package/dist/components/tooltip.d.ts +14 -1
- package/dist/es/index.js +3 -3
- package/dist/index.d.ts +2 -2
- package/dist/index.js +3 -3
- package/dist/util/types.d.ts +9 -0
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
# @khanacademy/wonder-blocks-tooltip
|
|
2
2
|
|
|
3
|
+
## 4.2.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- b2bf6d3: Add a `variant` prop to `Tooltip` to support a new `strong` variant. The `strong` variant renders the tooltip with an inverse/knockout ("black") background and light ("white") text. It defaults to `subtle`, which is the existing tooltip styling.
|
|
8
|
+
|
|
3
9
|
## 4.1.85
|
|
4
10
|
|
|
5
11
|
### Patch Changes
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
2
|
import { color } from "@khanacademy/wonder-blocks-tokens";
|
|
3
3
|
import TooltipContent from "./tooltip-content";
|
|
4
|
-
import { PopperElementProps } from "../util/types";
|
|
4
|
+
import { PopperElementProps, TooltipVariant } from "../util/types";
|
|
5
5
|
export type Props = {
|
|
6
6
|
/** The unique identifier for this component. */
|
|
7
7
|
id: string;
|
|
@@ -10,6 +10,11 @@ export type Props = {
|
|
|
10
10
|
onActiveChanged: (active: boolean) => unknown;
|
|
11
11
|
/** Optional background color. */
|
|
12
12
|
backgroundColor?: keyof typeof color;
|
|
13
|
+
/**
|
|
14
|
+
* The visual style of the tooltip. When `strong`, the bubble uses a
|
|
15
|
+
* higher-emphasis inverse/knockout treatment. Defaults to `subtle`.
|
|
16
|
+
*/
|
|
17
|
+
variant?: TooltipVariant;
|
|
13
18
|
} & PopperElementProps;
|
|
14
19
|
type State = {
|
|
15
20
|
active: boolean;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
2
|
import { color } from "@khanacademy/wonder-blocks-tokens";
|
|
3
3
|
import type { StyleType } from "@khanacademy/wonder-blocks-core";
|
|
4
|
-
import type { getRefFn, Placement, Offset } from "../util/types";
|
|
4
|
+
import type { getRefFn, Placement, Offset, TooltipVariant } from "../util/types";
|
|
5
5
|
export type Props = {
|
|
6
6
|
/**
|
|
7
7
|
* Whether we should use the default background color or switch to a
|
|
@@ -11,6 +11,12 @@ export type Props = {
|
|
|
11
11
|
* @ignore
|
|
12
12
|
*/
|
|
13
13
|
color?: keyof typeof color;
|
|
14
|
+
/**
|
|
15
|
+
* The visual style of the tail. When `strong`, the tail matches the
|
|
16
|
+
* higher-emphasis inverse/knockout treatment of the strong tooltip bubble.
|
|
17
|
+
* Defaults to `subtle`.
|
|
18
|
+
*/
|
|
19
|
+
variant?: TooltipVariant;
|
|
14
20
|
/** The offset of the tail indicating where it should be positioned. */
|
|
15
21
|
offset?: Offset;
|
|
16
22
|
/** The placement of the tail with respect to the tooltip anchor. */
|
|
@@ -22,7 +22,7 @@ import type { Typography } from "@khanacademy/wonder-blocks-typography";
|
|
|
22
22
|
import type { AriaProps } from "@khanacademy/wonder-blocks-core";
|
|
23
23
|
import { color } from "@khanacademy/wonder-blocks-tokens";
|
|
24
24
|
import TooltipContent from "./tooltip-content";
|
|
25
|
-
import type { ContentStyle, Placement } from "../util/types";
|
|
25
|
+
import type { ContentStyle, Placement, TooltipVariant } from "../util/types";
|
|
26
26
|
type Props = AriaProps & Readonly<{
|
|
27
27
|
/**
|
|
28
28
|
* The content for anchoring the tooltip.
|
|
@@ -92,6 +92,18 @@ type Props = AriaProps & Readonly<{
|
|
|
92
92
|
* Test ID used for e2e testing.
|
|
93
93
|
*/
|
|
94
94
|
testId?: string;
|
|
95
|
+
/**
|
|
96
|
+
* The visual style of the tooltip.
|
|
97
|
+
*
|
|
98
|
+
* - `subtle` (default): the standard tooltip styling, used for most
|
|
99
|
+
* cases.
|
|
100
|
+
* - `strong`: a higher-emphasis tooltip that uses an inverse/knockout
|
|
101
|
+
* treatment. Its colors are defined with semantic tokens, so they
|
|
102
|
+
* adapt to the active theme.
|
|
103
|
+
*
|
|
104
|
+
* Defaults to `subtle`.
|
|
105
|
+
*/
|
|
106
|
+
variant?: TooltipVariant;
|
|
95
107
|
/**
|
|
96
108
|
* Optional custom styles for the tooltip content which are a subset of valid CSS styles.
|
|
97
109
|
*/
|
|
@@ -124,6 +136,7 @@ type State = Readonly<{
|
|
|
124
136
|
type DefaultProps = {
|
|
125
137
|
forceAnchorFocusivity: Props["forceAnchorFocusivity"];
|
|
126
138
|
placement: Props["placement"];
|
|
139
|
+
variant: Props["variant"];
|
|
127
140
|
};
|
|
128
141
|
/**
|
|
129
142
|
* Use a tooltip to help describe an on screen object.
|
package/dist/es/index.js
CHANGED
|
@@ -14,9 +14,9 @@ const TooltipAppearanceDelay=100;const TooltipDisappearanceDelay=75;
|
|
|
14
14
|
|
|
15
15
|
const TRACKER=new ActiveTracker;class TooltipAnchor extends React.Component{componentDidMount(){const anchorNode=ReactDOM.findDOMNode(this);if(anchorNode instanceof Text){throw new Error("TooltipAnchor must be applied to an Element. Text content is not supported.")}this._unsubscribeFromTracker=TRACKER.subscribe(this);this._anchorNode=anchorNode;this._updateFocusivity();if(anchorNode){anchorNode.addEventListener("focusin",this._handleFocusIn);anchorNode.addEventListener("focusout",this._handleFocusOut);anchorNode.addEventListener("mouseenter",this._handleMouseEnter);anchorNode.addEventListener("mouseleave",this._handleMouseLeave);this.props.anchorRef(this._anchorNode);}}componentDidUpdate(prevProps){if(prevProps.forceAnchorFocusivity!==this.props.forceAnchorFocusivity||prevProps.children!==this.props.children){this._updateFocusivity();}}componentWillUnmount(){if(this._unsubscribeFromTracker){this._unsubscribeFromTracker();}this._clearPendingAction();const anchorNode=this._anchorNode;if(anchorNode){anchorNode.removeEventListener("focusin",this._handleFocusIn);anchorNode.removeEventListener("focusout",this._handleFocusOut);anchorNode.removeEventListener("mouseenter",this._handleMouseEnter);anchorNode.removeEventListener("mouseleave",this._handleMouseLeave);}if(this.state.active){document.removeEventListener("keyup",this._handleKeyUp);}}_updateFocusivity(){const anchorNode=this._anchorNode;if(!anchorNode){return}const{forceAnchorFocusivity}=this.props;const currentTabIndex=anchorNode.getAttribute("tabindex");if(forceAnchorFocusivity&&!currentTabIndex){anchorNode.setAttribute("tabindex","0");this._weSetFocusivity=true;}else if(!forceAnchorFocusivity&¤tTabIndex){if(this._weSetFocusivity){anchorNode.removeAttribute("tabindex");this._weSetFocusivity=false;}}}_updateActiveState(hovered,focused){this._hovered=hovered;this._focused=focused;this._setActiveState(hovered||focused);}_clearPendingAction(){if(this._timeoutID){clearTimeout(this._timeoutID);this._timeoutID=null;}}_setActiveState(active,instant){if(this._stolenFromUs||active!==this.state.active||!this.state.active&&this._timeoutID){this._clearPendingAction();}else if(active===this.state.active){if(this._timeoutID){this._clearPendingAction();}return}instant=instant||active&&TRACKER.steal(this);if(instant){if(active){document.addEventListener("keyup",this._handleKeyUp);}else {document.removeEventListener("keyup",this._handleKeyUp);}this.setState({active});this.props.onActiveChanged(active);if(!this._stolenFromUs&&!active){TRACKER.giveup();}this._stolenFromUs=false;}else {const delay=active?TooltipAppearanceDelay:TooltipDisappearanceDelay;this._timeoutID=setTimeout(()=>{this._timeoutID=null;this._setActiveState(active,true);},delay);}}_renderAnchorableChildren(){const{children}=this.props;return typeof children==="string"?jsx(Text$1,{children:children}):children}render(){const{"aria-describedby":ariaDescribedBy}=this.props;const anchorableChildren=this._renderAnchorableChildren();return React.cloneElement(anchorableChildren,{"aria-describedby":ariaDescribedBy})}constructor(props){super(props),this.activeStateStolen=()=>{this._stolenFromUs=this.state.active||!!this._timeoutID;this._focused=false;this._setActiveState(false,true);},this._handleFocusIn=()=>{this._updateActiveState(this._hovered,true);},this._handleFocusOut=()=>{this._updateActiveState(this._hovered,false);},this._handleMouseEnter=()=>{this._updateActiveState(true,this._focused);},this._handleMouseLeave=()=>{this._updateActiveState(false,this._focused);},this._handleKeyUp=e=>{if(e.key==="Escape"&&this.state.active){e.preventDefault();e.stopPropagation();this._updateActiveState(false,false);}};this._focused=false;this._hovered=false;this.state={active:false};}}TooltipAnchor.defaultProps={forceAnchorFocusivity:true};
|
|
16
16
|
|
|
17
|
-
let tempIdCounter=0;class TooltipTail extends React.Component{_calculateDimensionsFromPlacement(){const{placement}=this.props;const trimlineOffset=.5;switch(placement){case"top":return {trimlinePoints:[`0,-${trimlineOffset}`,`${ARROW_WIDTH},-${trimlineOffset}`],points:["0,0",`${ARROW_WIDTH/2},${ARROW_HEIGHT}`,`${ARROW_WIDTH},0`],height:ARROW_HEIGHT,width:ARROW_WIDTH};case"right":return {trimlinePoints:[`${ARROW_HEIGHT+trimlineOffset},0`,`${ARROW_HEIGHT+trimlineOffset},${ARROW_WIDTH}`],points:[`${ARROW_HEIGHT},0`,`0,${ARROW_WIDTH/2}`,`${ARROW_HEIGHT},${ARROW_WIDTH}`],width:ARROW_HEIGHT,height:ARROW_WIDTH};case"bottom":return {trimlinePoints:[`0, ${ARROW_HEIGHT+trimlineOffset}`,`${ARROW_WIDTH},${ARROW_HEIGHT+trimlineOffset}`],points:[`0, ${ARROW_HEIGHT}`,`${ARROW_WIDTH/2},0`,`${ARROW_WIDTH},${ARROW_HEIGHT}`],width:ARROW_WIDTH,height:ARROW_HEIGHT};case"left":return {trimlinePoints:[`-${trimlineOffset},0`,`-${trimlineOffset},${ARROW_WIDTH}`],points:[`0,0`,`${ARROW_HEIGHT},${ARROW_WIDTH/2}`,`0,${ARROW_WIDTH}`],width:ARROW_HEIGHT,height:ARROW_WIDTH};default:throw new Error(`Unknown placement: ${placement}`)}}_getFilterPositioning(){const{placement}=this.props;switch(placement){case"top":return {y:"-50%",x:"-50%",offsetShadowX:0};case"bottom":return null;case"left":return {y:"-50%",x:"0%",offsetShadowX:1};case"right":return {y:"-50%",x:"-100%",offsetShadowX:-1};default:throw new Error(`Unknown placement: ${placement}`)}}_maybeRenderDropshadow(points){const position=this._getFilterPositioning();if(!position){return null}const{placement}=this.props;const{y,x,offsetShadowX}=position;const dropShadowFilterId=`tooltip-dropshadow-${placement}-${tempIdCounter++}`;return [jsxs("filter",{id:dropShadowFilterId,width:"200%",height:"200%",x:x,y:y,children:[jsx("feGaussianBlur",{in:"SourceAlpha",stdDeviation:3}),jsx("feComponentTransfer",{children:jsx("feFuncA",{type:"linear",slope:"0.3"})})]},"filter"),jsx("g",{transform:`translate(${offsetShadowX},5.5)`,children:jsx("polyline",{fill:semanticColor.core.shadow.transparent.mid,points:points.join(" "),stroke:semanticColor.core.shadow.transparent.mid,filter:`url(#${dropShadowFilterId})`})},"dropshadow")]}_getFullTailWidth(){return ARROW_WIDTH+2*MIN_DISTANCE_FROM_CORNERS}_getFullTailHeight(){return ARROW_HEIGHT+DISTANCE_FROM_ANCHOR}_getContainerStyle(){const{placement}=this.props;const fullTailWidth=this._getFullTailWidth();const fullTailHeight=this._getFullTailHeight();switch(placement){case"top":return {top:-1,width:fullTailWidth,height:fullTailHeight};case"right":return {left:1,width:fullTailHeight,height:fullTailWidth};case"bottom":return {top:1,width:fullTailWidth,height:fullTailHeight};case"left":return {left:-1,width:fullTailHeight,height:fullTailWidth};default:throw new Error(`Unknown placement: ${placement}`)}}_getArrowStyle(){const{placement}=this.props;switch(placement){case"top":return {marginLeft:MIN_DISTANCE_FROM_CORNERS,marginRight:MIN_DISTANCE_FROM_CORNERS,paddingBottom:DISTANCE_FROM_ANCHOR};case"right":return {marginTop:MIN_DISTANCE_FROM_CORNERS,marginBottom:MIN_DISTANCE_FROM_CORNERS,paddingLeft:DISTANCE_FROM_ANCHOR};case"bottom":return {marginLeft:MIN_DISTANCE_FROM_CORNERS,marginRight:MIN_DISTANCE_FROM_CORNERS,paddingTop:DISTANCE_FROM_ANCHOR};case"left":return {marginTop:MIN_DISTANCE_FROM_CORNERS,marginBottom:MIN_DISTANCE_FROM_CORNERS,paddingRight:DISTANCE_FROM_ANCHOR};default:throw new Error(`Unknown placement: ${placement}`)}}_renderArrow(){const{trimlinePoints,points,height,width}=this._calculateDimensionsFromPlacement();const{color:arrowColor,show}=this.props;const tailFill=arrowColor!==undefined&&color[arrowColor]?color[arrowColor]:semanticColor.core.background.base.default;if(!show){return jsx(View,{"aria-hidden":true,style:{inlineSize:height,flexBasis:height,flexShrink:0}})}return jsxs("svg",{className:css(styles$2.arrow),style:this._getArrowStyle(),width:width,height:height,"aria-hidden":true,children:[this._maybeRenderDropshadow(points),jsx("polyline",{fill:tailFill,stroke:tailFill,points:points.join(" ")}),jsx("polyline",{fill:tailFill,points:points.join(" "),stroke:
|
|
17
|
+
let tempIdCounter=0;class TooltipTail extends React.Component{_calculateDimensionsFromPlacement(){const{placement}=this.props;const trimlineOffset=.5;switch(placement){case"top":return {trimlinePoints:[`0,-${trimlineOffset}`,`${ARROW_WIDTH},-${trimlineOffset}`],points:["0,0",`${ARROW_WIDTH/2},${ARROW_HEIGHT}`,`${ARROW_WIDTH},0`],height:ARROW_HEIGHT,width:ARROW_WIDTH};case"right":return {trimlinePoints:[`${ARROW_HEIGHT+trimlineOffset},0`,`${ARROW_HEIGHT+trimlineOffset},${ARROW_WIDTH}`],points:[`${ARROW_HEIGHT},0`,`0,${ARROW_WIDTH/2}`,`${ARROW_HEIGHT},${ARROW_WIDTH}`],width:ARROW_HEIGHT,height:ARROW_WIDTH};case"bottom":return {trimlinePoints:[`0, ${ARROW_HEIGHT+trimlineOffset}`,`${ARROW_WIDTH},${ARROW_HEIGHT+trimlineOffset}`],points:[`0, ${ARROW_HEIGHT}`,`${ARROW_WIDTH/2},0`,`${ARROW_WIDTH},${ARROW_HEIGHT}`],width:ARROW_WIDTH,height:ARROW_HEIGHT};case"left":return {trimlinePoints:[`-${trimlineOffset},0`,`-${trimlineOffset},${ARROW_WIDTH}`],points:[`0,0`,`${ARROW_HEIGHT},${ARROW_WIDTH/2}`,`0,${ARROW_WIDTH}`],width:ARROW_HEIGHT,height:ARROW_WIDTH};default:throw new Error(`Unknown placement: ${placement}`)}}_getFilterPositioning(){const{placement}=this.props;switch(placement){case"top":return {y:"-50%",x:"-50%",offsetShadowX:0};case"bottom":return null;case"left":return {y:"-50%",x:"0%",offsetShadowX:1};case"right":return {y:"-50%",x:"-100%",offsetShadowX:-1};default:throw new Error(`Unknown placement: ${placement}`)}}_maybeRenderDropshadow(points){const position=this._getFilterPositioning();if(!position){return null}const{placement}=this.props;const{y,x,offsetShadowX}=position;const dropShadowFilterId=`tooltip-dropshadow-${placement}-${tempIdCounter++}`;return [jsxs("filter",{id:dropShadowFilterId,width:"200%",height:"200%",x:x,y:y,children:[jsx("feGaussianBlur",{in:"SourceAlpha",stdDeviation:3}),jsx("feComponentTransfer",{children:jsx("feFuncA",{type:"linear",slope:"0.3"})})]},"filter"),jsx("g",{transform:`translate(${offsetShadowX},5.5)`,children:jsx("polyline",{fill:semanticColor.core.shadow.transparent.mid,points:points.join(" "),stroke:semanticColor.core.shadow.transparent.mid,filter:`url(#${dropShadowFilterId})`})},"dropshadow")]}_getFullTailWidth(){return ARROW_WIDTH+2*MIN_DISTANCE_FROM_CORNERS}_getFullTailHeight(){return ARROW_HEIGHT+DISTANCE_FROM_ANCHOR}_getContainerStyle(){const{placement}=this.props;const fullTailWidth=this._getFullTailWidth();const fullTailHeight=this._getFullTailHeight();switch(placement){case"top":return {top:-1,width:fullTailWidth,height:fullTailHeight};case"right":return {left:1,width:fullTailHeight,height:fullTailWidth};case"bottom":return {top:1,width:fullTailWidth,height:fullTailHeight};case"left":return {left:-1,width:fullTailHeight,height:fullTailWidth};default:throw new Error(`Unknown placement: ${placement}`)}}_getArrowStyle(){const{placement}=this.props;switch(placement){case"top":return {marginLeft:MIN_DISTANCE_FROM_CORNERS,marginRight:MIN_DISTANCE_FROM_CORNERS,paddingBottom:DISTANCE_FROM_ANCHOR};case"right":return {marginTop:MIN_DISTANCE_FROM_CORNERS,marginBottom:MIN_DISTANCE_FROM_CORNERS,paddingLeft:DISTANCE_FROM_ANCHOR};case"bottom":return {marginLeft:MIN_DISTANCE_FROM_CORNERS,marginRight:MIN_DISTANCE_FROM_CORNERS,paddingTop:DISTANCE_FROM_ANCHOR};case"left":return {marginTop:MIN_DISTANCE_FROM_CORNERS,marginBottom:MIN_DISTANCE_FROM_CORNERS,paddingRight:DISTANCE_FROM_ANCHOR};default:throw new Error(`Unknown placement: ${placement}`)}}_renderArrow(){const{trimlinePoints,points,height,width}=this._calculateDimensionsFromPlacement();const{color:arrowColor,show,variant}=this.props;const isStrong=variant==="strong";const tailFill=arrowColor!==undefined&&color[arrowColor]?color[arrowColor]:isStrong?semanticColor.feedback.neutral.strong.background:semanticColor.core.background.base.default;const tailBorder=isStrong?semanticColor.feedback.neutral.strong.border:semanticColor.core.border.neutral.subtle;if(!show){return jsx(View,{"aria-hidden":true,style:{inlineSize:height,flexBasis:height,flexShrink:0}})}return jsxs("svg",{className:css(styles$2.arrow),style:this._getArrowStyle(),width:width,height:height,"aria-hidden":true,children:[this._maybeRenderDropshadow(points),jsx("polyline",{fill:tailFill,stroke:tailFill,points:points.join(" ")}),jsx("polyline",{fill:tailFill,points:points.join(" "),stroke:tailBorder}),jsx("polyline",{stroke:tailFill,points:trimlinePoints.join(" ")})]})}render(){const{offset,placement,updateRef}=this.props;return jsx(View,{style:[styles$2.tailContainer,{...offset},this._getContainerStyle()],"data-placement":placement,ref:updateRef,children:this._renderArrow()})}}TooltipTail.defaultProps={show:true};const DISTANCE_FROM_ANCHOR=8;const MIN_DISTANCE_FROM_CORNERS=8;const ARROW_WIDTH=24;const ARROW_HEIGHT=12;const styles$2=StyleSheet.create({tailContainer:{position:"relative",pointerEvents:"none"},arrow:{overflow:"visible"}});
|
|
18
18
|
|
|
19
|
-
class TooltipBubble extends React.Component{_setActiveState(active){this.setState({active});this.props.onActiveChanged(active);}render(){const{id,children,updateBubbleRef,placement,isReferenceHidden,style,updateTailRef,tailOffset,backgroundColor}=this.props;return jsxs(View,{id:id,role:"tooltip","data-placement":placement,onMouseEnter:this.handleMouseEnter,onMouseLeave:this.handleMouseLeave,ref:updateBubbleRef,style:[isReferenceHidden&&styles$1.hide,styles$1.bubble,styles$1[`content-${placement}`],style],children:[jsx(View,{style:[styles$1.content,backgroundColor&&{backgroundColor:color[backgroundColor]}],children:children}),jsx(TooltipTail,{updateRef:updateTailRef,placement:placement,offset:tailOffset,color:backgroundColor})]})}constructor(...args){super(...args),this.state={active:false},this.handleMouseEnter=()=>{this._setActiveState(true);},this.handleMouseLeave=()=>{this.props.onActiveChanged(false);};}}const styles$1=StyleSheet.create({bubble:{position:"absolute"},hide:{pointerEvents:"none",opacity:0,backgroundColor:semanticColor.core.transparent,color:semanticColor.core.transparent},"content-top":{flexDirection:"column"},"content-right":{flexDirection:"row-reverse"},"content-bottom":{flexDirection:"column-reverse"},"content-left":{flexDirection:"row"},content:{maxInlineSize:472,borderRadius:border.radius.radius_040,border:`solid 1px ${semanticColor.core.border.neutral.subtle}`,backgroundColor:semanticColor.core.background.base.default,boxShadow:boxShadow.mid,justifyContent:"center"}});
|
|
19
|
+
class TooltipBubble extends React.Component{_setActiveState(active){this.setState({active});this.props.onActiveChanged(active);}render(){const{id,children,updateBubbleRef,placement,isReferenceHidden,style,updateTailRef,tailOffset,backgroundColor,variant}=this.props;const isStrong=variant==="strong";return jsxs(View,{id:id,role:"tooltip","data-placement":placement,onMouseEnter:this.handleMouseEnter,onMouseLeave:this.handleMouseLeave,ref:updateBubbleRef,style:[isReferenceHidden&&styles$1.hide,styles$1.bubble,styles$1[`content-${placement}`],style],children:[jsx(View,{style:[styles$1.content,backgroundColor&&{backgroundColor:color[backgroundColor]},isStrong&&styles$1.contentStrong],children:children}),jsx(TooltipTail,{updateRef:updateTailRef,placement:placement,offset:tailOffset,color:backgroundColor,variant:variant})]})}constructor(...args){super(...args),this.state={active:false},this.handleMouseEnter=()=>{this._setActiveState(true);},this.handleMouseLeave=()=>{this.props.onActiveChanged(false);};}}const styles$1=StyleSheet.create({bubble:{position:"absolute"},hide:{pointerEvents:"none",opacity:0,backgroundColor:semanticColor.core.transparent,color:semanticColor.core.transparent},"content-top":{flexDirection:"column"},"content-right":{flexDirection:"row-reverse"},"content-bottom":{flexDirection:"column-reverse"},"content-left":{flexDirection:"row"},content:{maxInlineSize:472,borderRadius:border.radius.radius_040,border:`solid 1px ${semanticColor.core.border.neutral.subtle}`,backgroundColor:semanticColor.core.background.base.default,boxShadow:boxShadow.mid,justifyContent:"center"},contentStrong:{backgroundColor:semanticColor.feedback.neutral.strong.background,borderColor:semanticColor.feedback.neutral.strong.border,color:semanticColor.feedback.neutral.strong.text}});
|
|
20
20
|
|
|
21
21
|
class TooltipContent extends React.Component{_renderTitle(){const{title}=this.props;if(title){if(typeof title==="string"){return jsx(Heading,{size:"medium",children:title})}else {return title}}return null}_renderChildren(){const{children}=this.props;if(typeof children==="string"){return jsx(BodyText,{tag:"span",children:children})}else {return children}}render(){const title=this._renderTitle();const children=this._renderChildren();const containerStyle=title?styles.withTitle:styles.withoutTitle;return jsxs(View,{style:[containerStyle,this.props.contentStyle],testId:this.props.testId,children:[title,title?jsx(View,{children:children}):children]})}}const styles=StyleSheet.create({withoutTitle:{padding:`${sizing.size_100} ${sizing.size_160}`},withTitle:{padding:sizing.size_160,gap:sizing.size_040}});
|
|
22
22
|
|
|
@@ -26,6 +26,6 @@ class RefTracker{constructor(){this.updateRef=ref=>{if(ref){const domNode=ReactD
|
|
|
26
26
|
|
|
27
27
|
const filterPopperPlacement=placement=>{switch(placement){case"auto":case"auto-start":case"auto-end":case"top":case"top-start":case"top-end":return "top";case"bottom":case"bottom-start":case"bottom-end":return "bottom";case"right":case"right-start":case"right-end":return "right";case"left":case"left-start":case"left-end":return "left";default:throw new g(placement)}};function _modifyPosition({state}){const popperHeight=state.rects.popper.height+state.rects.reference.height;const minHeight=document.documentElement.clientHeight;if(minHeight<popperHeight&&state.modifiersData.hide){state.modifiersData.hide={...state.modifiersData.hide,isReferenceHidden:false};}}const smallViewportModifier={name:"smallViewport",enabled:true,phase:"main",fn:_modifyPosition};const VIEWPORT_PADDING=parseInt(remToPx(tokenValue(sizing.size_120)));class TooltipPopper extends React.Component{componentDidMount(){const{anchorElement,autoUpdate}=this.props;if(!anchorElement||!autoUpdate){return}this._observer=new MutationObserver(()=>{this._popperUpdate?.();});this._observer.observe(anchorElement,{attributes:true,childList:true,subtree:true});}componentWillUnmount(){this._observer?.disconnect();}_renderPositionedContent(popperProps){const{children}=this.props;const{isReady}=this.state;const placement=filterPopperPlacement(popperProps.placement)||this.props.placement;this._bubbleRefTracker.setCallback(popperProps.ref);this._tailRefTracker.setCallback(popperProps.arrowProps.ref);this._popperUpdate=popperProps.update;const bubbleProps={placement,style:{top:popperProps.style.top,left:popperProps.style.left,bottom:popperProps.style.bottom,right:popperProps.style.right,position:popperProps.style.position,transform:popperProps.style.transform,visibility:!isReady?"hidden":undefined},updateBubbleRef:this._bubbleRefTracker.updateRef,tailOffset:{bottom:popperProps.arrowProps.style.bottom,right:popperProps.arrowProps.style.right,top:popperProps.arrowProps.style.top,left:popperProps.arrowProps.style.left,transform:popperProps.arrowProps.style.transform},updateTailRef:this._tailRefTracker.updateRef,isReferenceHidden:popperProps.isReferenceHidden};return children(bubbleProps)}render(){const{anchorElement,placement,rootBoundary,viewportPadding}=this.props;const modifiers=[smallViewportModifier];if(rootBoundary==="viewport"){modifiers.push({name:"preventOverflow",options:{rootBoundary:"viewport",padding:viewportPadding}});}else {modifiers.push({name:"flip",options:{rootBoundary:"document"}});}return jsx(Popper,{referenceElement:anchorElement,strategy:"fixed",placement:placement,modifiers:modifiers,onFirstUpdate:this.handleFirstUpdate,children:props=>this._renderPositionedContent(props)})}constructor(props){super(props),this._bubbleRefTracker=new RefTracker,this._tailRefTracker=new RefTracker,this._observer=null,this._popperUpdate=null,this.handleFirstUpdate=()=>{this.setState({isReady:true});};this.state={isReady:false};}}TooltipPopper.defaultProps={rootBoundary:"viewport",viewportPadding:VIEWPORT_PADDING};
|
|
28
28
|
|
|
29
|
-
class Tooltip extends React.Component{static getDerivedStateFromProps(props,state){return {active:typeof props.opened==="boolean"?props.opened:state.active}}_updateAnchorElement(ref){if(ref&&ref!==this.state.anchorElement){this.setState({anchorElement:ref});}}_renderBubbleContent(){const{title,content,contentStyle,testId}=this.props;if(typeof content==="string"){return jsx(TooltipContent,{title:title,contentStyle:contentStyle,testId:testId?`${testId}-content`:undefined,children:content})}else if(title){return React.cloneElement(content,{title})}else {return content}}_renderPopper(ariaContentId){const{backgroundColor,placement}=this.props;return jsx(TooltipPopper,{anchorElement:this.state.anchorElement,placement:placement,autoUpdate:this.props.autoUpdate,viewportPadding:this.props.viewportPadding,children:props=>jsx(TooltipBubble,{id:ariaContentId,style:props.style,backgroundColor:backgroundColor,tailOffset:props.tailOffset,isReferenceHidden:props.isReferenceHidden,placement:props.placement,updateTailRef:props.updateTailRef,updateBubbleRef:props.updateBubbleRef,onActiveChanged:active=>this.setState({activeBubble:active}),children:this._renderBubbleContent()})})}_getHost(){const{anchorElement}=this.state;return maybeGetPortalMountedModalHostElement(anchorElement)||document.body}_renderTooltipAnchor(uniqueId){const{autoUpdate,children,forceAnchorFocusivity}=this.props;const{active,activeBubble}=this.state;const popperHost=this._getHost();const shouldAnchorExist=autoUpdate?this.state.anchorElement:true;const shouldBeVisible=popperHost&&(active||activeBubble)&&shouldAnchorExist;const ariaContentId=`${uniqueId}-aria-content`;return jsxs(React.Fragment,{children:[jsx(TooltipAnchor,{forceAnchorFocusivity:forceAnchorFocusivity,anchorRef:r=>this._updateAnchorElement(r),onActiveChanged:active=>this.setState({active}),"aria-describedby":shouldBeVisible?ariaContentId:undefined,children:children}),shouldBeVisible&&ReactDOM.createPortal(this._renderPopper(ariaContentId),popperHost)]})}render(){const{id}=this.props;return jsx(Id,{id:id,children:uniqueId=>this._renderTooltipAnchor(uniqueId)})}constructor(...args){super(...args),this.state={active:false,activeBubble:false};}}Tooltip.defaultProps={forceAnchorFocusivity:true,placement:"top"};
|
|
29
|
+
class Tooltip extends React.Component{static getDerivedStateFromProps(props,state){return {active:typeof props.opened==="boolean"?props.opened:state.active}}_updateAnchorElement(ref){if(ref&&ref!==this.state.anchorElement){this.setState({anchorElement:ref});}}_renderBubbleContent(){const{title,content,contentStyle,testId}=this.props;if(typeof content==="string"){return jsx(TooltipContent,{title:title,contentStyle:contentStyle,testId:testId?`${testId}-content`:undefined,children:content})}else if(title){return React.cloneElement(content,{title})}else {return content}}_renderPopper(ariaContentId){const{backgroundColor,placement,variant}=this.props;return jsx(TooltipPopper,{anchorElement:this.state.anchorElement,placement:placement,autoUpdate:this.props.autoUpdate,viewportPadding:this.props.viewportPadding,children:props=>jsx(TooltipBubble,{id:ariaContentId,style:props.style,backgroundColor:backgroundColor,variant:variant,tailOffset:props.tailOffset,isReferenceHidden:props.isReferenceHidden,placement:props.placement,updateTailRef:props.updateTailRef,updateBubbleRef:props.updateBubbleRef,onActiveChanged:active=>this.setState({activeBubble:active}),children:this._renderBubbleContent()})})}_getHost(){const{anchorElement}=this.state;return maybeGetPortalMountedModalHostElement(anchorElement)||document.body}_renderTooltipAnchor(uniqueId){const{autoUpdate,children,forceAnchorFocusivity}=this.props;const{active,activeBubble}=this.state;const popperHost=this._getHost();const shouldAnchorExist=autoUpdate?this.state.anchorElement:true;const shouldBeVisible=popperHost&&(active||activeBubble)&&shouldAnchorExist;const ariaContentId=`${uniqueId}-aria-content`;return jsxs(React.Fragment,{children:[jsx(TooltipAnchor,{forceAnchorFocusivity:forceAnchorFocusivity,anchorRef:r=>this._updateAnchorElement(r),onActiveChanged:active=>this.setState({active}),"aria-describedby":shouldBeVisible?ariaContentId:undefined,children:children}),shouldBeVisible&&ReactDOM.createPortal(this._renderPopper(ariaContentId),popperHost)]})}render(){const{id}=this.props;return jsx(Id,{id:id,children:uniqueId=>this._renderTooltipAnchor(uniqueId)})}constructor(...args){super(...args),this.state={active:false,activeBubble:false};}}Tooltip.defaultProps={forceAnchorFocusivity:true,placement:"top",variant:"subtle"};
|
|
30
30
|
|
|
31
31
|
export { TooltipContent, TooltipPopper, TooltipTail, Tooltip as default };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import type { Placement, PopperElementProps } from "./util/types";
|
|
1
|
+
import type { Placement, PopperElementProps, TooltipVariant } from "./util/types";
|
|
2
2
|
import Tooltip from "./components/tooltip";
|
|
3
3
|
import TooltipContent from "./components/tooltip-content";
|
|
4
4
|
import TooltipPopper from "./components/tooltip-popper";
|
|
5
5
|
import TooltipTail from "./components/tooltip-tail";
|
|
6
6
|
export { Tooltip as default, TooltipContent, TooltipPopper, TooltipTail };
|
|
7
|
-
export type { Placement, PopperElementProps };
|
|
7
|
+
export type { Placement, PopperElementProps, TooltipVariant };
|
package/dist/index.js
CHANGED
|
@@ -39,9 +39,9 @@ const TooltipAppearanceDelay=100;const TooltipDisappearanceDelay=75;
|
|
|
39
39
|
|
|
40
40
|
const TRACKER=new ActiveTracker;class TooltipAnchor extends React__namespace.Component{componentDidMount(){const anchorNode=ReactDOM__namespace.findDOMNode(this);if(anchorNode instanceof Text){throw new Error("TooltipAnchor must be applied to an Element. Text content is not supported.")}this._unsubscribeFromTracker=TRACKER.subscribe(this);this._anchorNode=anchorNode;this._updateFocusivity();if(anchorNode){anchorNode.addEventListener("focusin",this._handleFocusIn);anchorNode.addEventListener("focusout",this._handleFocusOut);anchorNode.addEventListener("mouseenter",this._handleMouseEnter);anchorNode.addEventListener("mouseleave",this._handleMouseLeave);this.props.anchorRef(this._anchorNode);}}componentDidUpdate(prevProps){if(prevProps.forceAnchorFocusivity!==this.props.forceAnchorFocusivity||prevProps.children!==this.props.children){this._updateFocusivity();}}componentWillUnmount(){if(this._unsubscribeFromTracker){this._unsubscribeFromTracker();}this._clearPendingAction();const anchorNode=this._anchorNode;if(anchorNode){anchorNode.removeEventListener("focusin",this._handleFocusIn);anchorNode.removeEventListener("focusout",this._handleFocusOut);anchorNode.removeEventListener("mouseenter",this._handleMouseEnter);anchorNode.removeEventListener("mouseleave",this._handleMouseLeave);}if(this.state.active){document.removeEventListener("keyup",this._handleKeyUp);}}_updateFocusivity(){const anchorNode=this._anchorNode;if(!anchorNode){return}const{forceAnchorFocusivity}=this.props;const currentTabIndex=anchorNode.getAttribute("tabindex");if(forceAnchorFocusivity&&!currentTabIndex){anchorNode.setAttribute("tabindex","0");this._weSetFocusivity=true;}else if(!forceAnchorFocusivity&¤tTabIndex){if(this._weSetFocusivity){anchorNode.removeAttribute("tabindex");this._weSetFocusivity=false;}}}_updateActiveState(hovered,focused){this._hovered=hovered;this._focused=focused;this._setActiveState(hovered||focused);}_clearPendingAction(){if(this._timeoutID){clearTimeout(this._timeoutID);this._timeoutID=null;}}_setActiveState(active,instant){if(this._stolenFromUs||active!==this.state.active||!this.state.active&&this._timeoutID){this._clearPendingAction();}else if(active===this.state.active){if(this._timeoutID){this._clearPendingAction();}return}instant=instant||active&&TRACKER.steal(this);if(instant){if(active){document.addEventListener("keyup",this._handleKeyUp);}else {document.removeEventListener("keyup",this._handleKeyUp);}this.setState({active});this.props.onActiveChanged(active);if(!this._stolenFromUs&&!active){TRACKER.giveup();}this._stolenFromUs=false;}else {const delay=active?TooltipAppearanceDelay:TooltipDisappearanceDelay;this._timeoutID=setTimeout(()=>{this._timeoutID=null;this._setActiveState(active,true);},delay);}}_renderAnchorableChildren(){const{children}=this.props;return typeof children==="string"?jsxRuntime.jsx(wonderBlocksCore.Text,{children:children}):children}render(){const{"aria-describedby":ariaDescribedBy}=this.props;const anchorableChildren=this._renderAnchorableChildren();return React__namespace.cloneElement(anchorableChildren,{"aria-describedby":ariaDescribedBy})}constructor(props){super(props),this.activeStateStolen=()=>{this._stolenFromUs=this.state.active||!!this._timeoutID;this._focused=false;this._setActiveState(false,true);},this._handleFocusIn=()=>{this._updateActiveState(this._hovered,true);},this._handleFocusOut=()=>{this._updateActiveState(this._hovered,false);},this._handleMouseEnter=()=>{this._updateActiveState(true,this._focused);},this._handleMouseLeave=()=>{this._updateActiveState(false,this._focused);},this._handleKeyUp=e=>{if(e.key==="Escape"&&this.state.active){e.preventDefault();e.stopPropagation();this._updateActiveState(false,false);}};this._focused=false;this._hovered=false;this.state={active:false};}}TooltipAnchor.defaultProps={forceAnchorFocusivity:true};
|
|
41
41
|
|
|
42
|
-
let tempIdCounter=0;class TooltipTail extends React__namespace.Component{_calculateDimensionsFromPlacement(){const{placement}=this.props;const trimlineOffset=.5;switch(placement){case"top":return {trimlinePoints:[`0,-${trimlineOffset}`,`${ARROW_WIDTH},-${trimlineOffset}`],points:["0,0",`${ARROW_WIDTH/2},${ARROW_HEIGHT}`,`${ARROW_WIDTH},0`],height:ARROW_HEIGHT,width:ARROW_WIDTH};case"right":return {trimlinePoints:[`${ARROW_HEIGHT+trimlineOffset},0`,`${ARROW_HEIGHT+trimlineOffset},${ARROW_WIDTH}`],points:[`${ARROW_HEIGHT},0`,`0,${ARROW_WIDTH/2}`,`${ARROW_HEIGHT},${ARROW_WIDTH}`],width:ARROW_HEIGHT,height:ARROW_WIDTH};case"bottom":return {trimlinePoints:[`0, ${ARROW_HEIGHT+trimlineOffset}`,`${ARROW_WIDTH},${ARROW_HEIGHT+trimlineOffset}`],points:[`0, ${ARROW_HEIGHT}`,`${ARROW_WIDTH/2},0`,`${ARROW_WIDTH},${ARROW_HEIGHT}`],width:ARROW_WIDTH,height:ARROW_HEIGHT};case"left":return {trimlinePoints:[`-${trimlineOffset},0`,`-${trimlineOffset},${ARROW_WIDTH}`],points:[`0,0`,`${ARROW_HEIGHT},${ARROW_WIDTH/2}`,`0,${ARROW_WIDTH}`],width:ARROW_HEIGHT,height:ARROW_WIDTH};default:throw new Error(`Unknown placement: ${placement}`)}}_getFilterPositioning(){const{placement}=this.props;switch(placement){case"top":return {y:"-50%",x:"-50%",offsetShadowX:0};case"bottom":return null;case"left":return {y:"-50%",x:"0%",offsetShadowX:1};case"right":return {y:"-50%",x:"-100%",offsetShadowX:-1};default:throw new Error(`Unknown placement: ${placement}`)}}_maybeRenderDropshadow(points){const position=this._getFilterPositioning();if(!position){return null}const{placement}=this.props;const{y,x,offsetShadowX}=position;const dropShadowFilterId=`tooltip-dropshadow-${placement}-${tempIdCounter++}`;return [jsxRuntime.jsxs("filter",{id:dropShadowFilterId,width:"200%",height:"200%",x:x,y:y,children:[jsxRuntime.jsx("feGaussianBlur",{in:"SourceAlpha",stdDeviation:3}),jsxRuntime.jsx("feComponentTransfer",{children:jsxRuntime.jsx("feFuncA",{type:"linear",slope:"0.3"})})]},"filter"),jsxRuntime.jsx("g",{transform:`translate(${offsetShadowX},5.5)`,children:jsxRuntime.jsx("polyline",{fill:wonderBlocksTokens.semanticColor.core.shadow.transparent.mid,points:points.join(" "),stroke:wonderBlocksTokens.semanticColor.core.shadow.transparent.mid,filter:`url(#${dropShadowFilterId})`})},"dropshadow")]}_getFullTailWidth(){return ARROW_WIDTH+2*MIN_DISTANCE_FROM_CORNERS}_getFullTailHeight(){return ARROW_HEIGHT+DISTANCE_FROM_ANCHOR}_getContainerStyle(){const{placement}=this.props;const fullTailWidth=this._getFullTailWidth();const fullTailHeight=this._getFullTailHeight();switch(placement){case"top":return {top:-1,width:fullTailWidth,height:fullTailHeight};case"right":return {left:1,width:fullTailHeight,height:fullTailWidth};case"bottom":return {top:1,width:fullTailWidth,height:fullTailHeight};case"left":return {left:-1,width:fullTailHeight,height:fullTailWidth};default:throw new Error(`Unknown placement: ${placement}`)}}_getArrowStyle(){const{placement}=this.props;switch(placement){case"top":return {marginLeft:MIN_DISTANCE_FROM_CORNERS,marginRight:MIN_DISTANCE_FROM_CORNERS,paddingBottom:DISTANCE_FROM_ANCHOR};case"right":return {marginTop:MIN_DISTANCE_FROM_CORNERS,marginBottom:MIN_DISTANCE_FROM_CORNERS,paddingLeft:DISTANCE_FROM_ANCHOR};case"bottom":return {marginLeft:MIN_DISTANCE_FROM_CORNERS,marginRight:MIN_DISTANCE_FROM_CORNERS,paddingTop:DISTANCE_FROM_ANCHOR};case"left":return {marginTop:MIN_DISTANCE_FROM_CORNERS,marginBottom:MIN_DISTANCE_FROM_CORNERS,paddingRight:DISTANCE_FROM_ANCHOR};default:throw new Error(`Unknown placement: ${placement}`)}}_renderArrow(){const{trimlinePoints,points,height,width}=this._calculateDimensionsFromPlacement();const{color:arrowColor,show}=this.props;const tailFill=arrowColor!==undefined&&wonderBlocksTokens.color[arrowColor]?wonderBlocksTokens.color[arrowColor]:wonderBlocksTokens.semanticColor.core.background.base.default;if(!show){return jsxRuntime.jsx(wonderBlocksCore.View,{"aria-hidden":true,style:{inlineSize:height,flexBasis:height,flexShrink:0}})}return jsxRuntime.jsxs("svg",{className:aphrodite.css(styles$2.arrow),style:this._getArrowStyle(),width:width,height:height,"aria-hidden":true,children:[this._maybeRenderDropshadow(points),jsxRuntime.jsx("polyline",{fill:tailFill,stroke:tailFill,points:points.join(" ")}),jsxRuntime.jsx("polyline",{fill:tailFill,points:points.join(" "),stroke:
|
|
42
|
+
let tempIdCounter=0;class TooltipTail extends React__namespace.Component{_calculateDimensionsFromPlacement(){const{placement}=this.props;const trimlineOffset=.5;switch(placement){case"top":return {trimlinePoints:[`0,-${trimlineOffset}`,`${ARROW_WIDTH},-${trimlineOffset}`],points:["0,0",`${ARROW_WIDTH/2},${ARROW_HEIGHT}`,`${ARROW_WIDTH},0`],height:ARROW_HEIGHT,width:ARROW_WIDTH};case"right":return {trimlinePoints:[`${ARROW_HEIGHT+trimlineOffset},0`,`${ARROW_HEIGHT+trimlineOffset},${ARROW_WIDTH}`],points:[`${ARROW_HEIGHT},0`,`0,${ARROW_WIDTH/2}`,`${ARROW_HEIGHT},${ARROW_WIDTH}`],width:ARROW_HEIGHT,height:ARROW_WIDTH};case"bottom":return {trimlinePoints:[`0, ${ARROW_HEIGHT+trimlineOffset}`,`${ARROW_WIDTH},${ARROW_HEIGHT+trimlineOffset}`],points:[`0, ${ARROW_HEIGHT}`,`${ARROW_WIDTH/2},0`,`${ARROW_WIDTH},${ARROW_HEIGHT}`],width:ARROW_WIDTH,height:ARROW_HEIGHT};case"left":return {trimlinePoints:[`-${trimlineOffset},0`,`-${trimlineOffset},${ARROW_WIDTH}`],points:[`0,0`,`${ARROW_HEIGHT},${ARROW_WIDTH/2}`,`0,${ARROW_WIDTH}`],width:ARROW_HEIGHT,height:ARROW_WIDTH};default:throw new Error(`Unknown placement: ${placement}`)}}_getFilterPositioning(){const{placement}=this.props;switch(placement){case"top":return {y:"-50%",x:"-50%",offsetShadowX:0};case"bottom":return null;case"left":return {y:"-50%",x:"0%",offsetShadowX:1};case"right":return {y:"-50%",x:"-100%",offsetShadowX:-1};default:throw new Error(`Unknown placement: ${placement}`)}}_maybeRenderDropshadow(points){const position=this._getFilterPositioning();if(!position){return null}const{placement}=this.props;const{y,x,offsetShadowX}=position;const dropShadowFilterId=`tooltip-dropshadow-${placement}-${tempIdCounter++}`;return [jsxRuntime.jsxs("filter",{id:dropShadowFilterId,width:"200%",height:"200%",x:x,y:y,children:[jsxRuntime.jsx("feGaussianBlur",{in:"SourceAlpha",stdDeviation:3}),jsxRuntime.jsx("feComponentTransfer",{children:jsxRuntime.jsx("feFuncA",{type:"linear",slope:"0.3"})})]},"filter"),jsxRuntime.jsx("g",{transform:`translate(${offsetShadowX},5.5)`,children:jsxRuntime.jsx("polyline",{fill:wonderBlocksTokens.semanticColor.core.shadow.transparent.mid,points:points.join(" "),stroke:wonderBlocksTokens.semanticColor.core.shadow.transparent.mid,filter:`url(#${dropShadowFilterId})`})},"dropshadow")]}_getFullTailWidth(){return ARROW_WIDTH+2*MIN_DISTANCE_FROM_CORNERS}_getFullTailHeight(){return ARROW_HEIGHT+DISTANCE_FROM_ANCHOR}_getContainerStyle(){const{placement}=this.props;const fullTailWidth=this._getFullTailWidth();const fullTailHeight=this._getFullTailHeight();switch(placement){case"top":return {top:-1,width:fullTailWidth,height:fullTailHeight};case"right":return {left:1,width:fullTailHeight,height:fullTailWidth};case"bottom":return {top:1,width:fullTailWidth,height:fullTailHeight};case"left":return {left:-1,width:fullTailHeight,height:fullTailWidth};default:throw new Error(`Unknown placement: ${placement}`)}}_getArrowStyle(){const{placement}=this.props;switch(placement){case"top":return {marginLeft:MIN_DISTANCE_FROM_CORNERS,marginRight:MIN_DISTANCE_FROM_CORNERS,paddingBottom:DISTANCE_FROM_ANCHOR};case"right":return {marginTop:MIN_DISTANCE_FROM_CORNERS,marginBottom:MIN_DISTANCE_FROM_CORNERS,paddingLeft:DISTANCE_FROM_ANCHOR};case"bottom":return {marginLeft:MIN_DISTANCE_FROM_CORNERS,marginRight:MIN_DISTANCE_FROM_CORNERS,paddingTop:DISTANCE_FROM_ANCHOR};case"left":return {marginTop:MIN_DISTANCE_FROM_CORNERS,marginBottom:MIN_DISTANCE_FROM_CORNERS,paddingRight:DISTANCE_FROM_ANCHOR};default:throw new Error(`Unknown placement: ${placement}`)}}_renderArrow(){const{trimlinePoints,points,height,width}=this._calculateDimensionsFromPlacement();const{color:arrowColor,show,variant}=this.props;const isStrong=variant==="strong";const tailFill=arrowColor!==undefined&&wonderBlocksTokens.color[arrowColor]?wonderBlocksTokens.color[arrowColor]:isStrong?wonderBlocksTokens.semanticColor.feedback.neutral.strong.background:wonderBlocksTokens.semanticColor.core.background.base.default;const tailBorder=isStrong?wonderBlocksTokens.semanticColor.feedback.neutral.strong.border:wonderBlocksTokens.semanticColor.core.border.neutral.subtle;if(!show){return jsxRuntime.jsx(wonderBlocksCore.View,{"aria-hidden":true,style:{inlineSize:height,flexBasis:height,flexShrink:0}})}return jsxRuntime.jsxs("svg",{className:aphrodite.css(styles$2.arrow),style:this._getArrowStyle(),width:width,height:height,"aria-hidden":true,children:[this._maybeRenderDropshadow(points),jsxRuntime.jsx("polyline",{fill:tailFill,stroke:tailFill,points:points.join(" ")}),jsxRuntime.jsx("polyline",{fill:tailFill,points:points.join(" "),stroke:tailBorder}),jsxRuntime.jsx("polyline",{stroke:tailFill,points:trimlinePoints.join(" ")})]})}render(){const{offset,placement,updateRef}=this.props;return jsxRuntime.jsx(wonderBlocksCore.View,{style:[styles$2.tailContainer,{...offset},this._getContainerStyle()],"data-placement":placement,ref:updateRef,children:this._renderArrow()})}}TooltipTail.defaultProps={show:true};const DISTANCE_FROM_ANCHOR=8;const MIN_DISTANCE_FROM_CORNERS=8;const ARROW_WIDTH=24;const ARROW_HEIGHT=12;const styles$2=aphrodite.StyleSheet.create({tailContainer:{position:"relative",pointerEvents:"none"},arrow:{overflow:"visible"}});
|
|
43
43
|
|
|
44
|
-
class TooltipBubble extends React__namespace.Component{_setActiveState(active){this.setState({active});this.props.onActiveChanged(active);}render(){const{id,children,updateBubbleRef,placement,isReferenceHidden,style,updateTailRef,tailOffset,backgroundColor}=this.props;return jsxRuntime.jsxs(wonderBlocksCore.View,{id:id,role:"tooltip","data-placement":placement,onMouseEnter:this.handleMouseEnter,onMouseLeave:this.handleMouseLeave,ref:updateBubbleRef,style:[isReferenceHidden&&styles$1.hide,styles$1.bubble,styles$1[`content-${placement}`],style],children:[jsxRuntime.jsx(wonderBlocksCore.View,{style:[styles$1.content,backgroundColor&&{backgroundColor:wonderBlocksTokens.color[backgroundColor]}],children:children}),jsxRuntime.jsx(TooltipTail,{updateRef:updateTailRef,placement:placement,offset:tailOffset,color:backgroundColor})]})}constructor(...args){super(...args),this.state={active:false},this.handleMouseEnter=()=>{this._setActiveState(true);},this.handleMouseLeave=()=>{this.props.onActiveChanged(false);};}}const styles$1=aphrodite.StyleSheet.create({bubble:{position:"absolute"},hide:{pointerEvents:"none",opacity:0,backgroundColor:wonderBlocksTokens.semanticColor.core.transparent,color:wonderBlocksTokens.semanticColor.core.transparent},"content-top":{flexDirection:"column"},"content-right":{flexDirection:"row-reverse"},"content-bottom":{flexDirection:"column-reverse"},"content-left":{flexDirection:"row"},content:{maxInlineSize:472,borderRadius:wonderBlocksTokens.border.radius.radius_040,border:`solid 1px ${wonderBlocksTokens.semanticColor.core.border.neutral.subtle}`,backgroundColor:wonderBlocksTokens.semanticColor.core.background.base.default,boxShadow:wonderBlocksTokens.boxShadow.mid,justifyContent:"center"}});
|
|
44
|
+
class TooltipBubble extends React__namespace.Component{_setActiveState(active){this.setState({active});this.props.onActiveChanged(active);}render(){const{id,children,updateBubbleRef,placement,isReferenceHidden,style,updateTailRef,tailOffset,backgroundColor,variant}=this.props;const isStrong=variant==="strong";return jsxRuntime.jsxs(wonderBlocksCore.View,{id:id,role:"tooltip","data-placement":placement,onMouseEnter:this.handleMouseEnter,onMouseLeave:this.handleMouseLeave,ref:updateBubbleRef,style:[isReferenceHidden&&styles$1.hide,styles$1.bubble,styles$1[`content-${placement}`],style],children:[jsxRuntime.jsx(wonderBlocksCore.View,{style:[styles$1.content,backgroundColor&&{backgroundColor:wonderBlocksTokens.color[backgroundColor]},isStrong&&styles$1.contentStrong],children:children}),jsxRuntime.jsx(TooltipTail,{updateRef:updateTailRef,placement:placement,offset:tailOffset,color:backgroundColor,variant:variant})]})}constructor(...args){super(...args),this.state={active:false},this.handleMouseEnter=()=>{this._setActiveState(true);},this.handleMouseLeave=()=>{this.props.onActiveChanged(false);};}}const styles$1=aphrodite.StyleSheet.create({bubble:{position:"absolute"},hide:{pointerEvents:"none",opacity:0,backgroundColor:wonderBlocksTokens.semanticColor.core.transparent,color:wonderBlocksTokens.semanticColor.core.transparent},"content-top":{flexDirection:"column"},"content-right":{flexDirection:"row-reverse"},"content-bottom":{flexDirection:"column-reverse"},"content-left":{flexDirection:"row"},content:{maxInlineSize:472,borderRadius:wonderBlocksTokens.border.radius.radius_040,border:`solid 1px ${wonderBlocksTokens.semanticColor.core.border.neutral.subtle}`,backgroundColor:wonderBlocksTokens.semanticColor.core.background.base.default,boxShadow:wonderBlocksTokens.boxShadow.mid,justifyContent:"center"},contentStrong:{backgroundColor:wonderBlocksTokens.semanticColor.feedback.neutral.strong.background,borderColor:wonderBlocksTokens.semanticColor.feedback.neutral.strong.border,color:wonderBlocksTokens.semanticColor.feedback.neutral.strong.text}});
|
|
45
45
|
|
|
46
46
|
class TooltipContent extends React__namespace.Component{_renderTitle(){const{title}=this.props;if(title){if(typeof title==="string"){return jsxRuntime.jsx(wonderBlocksTypography.Heading,{size:"medium",children:title})}else {return title}}return null}_renderChildren(){const{children}=this.props;if(typeof children==="string"){return jsxRuntime.jsx(wonderBlocksTypography.BodyText,{tag:"span",children:children})}else {return children}}render(){const title=this._renderTitle();const children=this._renderChildren();const containerStyle=title?styles.withTitle:styles.withoutTitle;return jsxRuntime.jsxs(wonderBlocksCore.View,{style:[containerStyle,this.props.contentStyle],testId:this.props.testId,children:[title,title?jsxRuntime.jsx(wonderBlocksCore.View,{children:children}):children]})}}const styles=aphrodite.StyleSheet.create({withoutTitle:{padding:`${wonderBlocksTokens.sizing.size_100} ${wonderBlocksTokens.sizing.size_160}`},withTitle:{padding:wonderBlocksTokens.sizing.size_160,gap:wonderBlocksTokens.sizing.size_040}});
|
|
47
47
|
|
|
@@ -51,7 +51,7 @@ class RefTracker{constructor(){this.updateRef=ref=>{if(ref){const domNode=ReactD
|
|
|
51
51
|
|
|
52
52
|
const filterPopperPlacement=placement=>{switch(placement){case"auto":case"auto-start":case"auto-end":case"top":case"top-start":case"top-end":return "top";case"bottom":case"bottom-start":case"bottom-end":return "bottom";case"right":case"right-start":case"right-end":return "right";case"left":case"left-start":case"left-end":return "left";default:throw new g(placement)}};function _modifyPosition({state}){const popperHeight=state.rects.popper.height+state.rects.reference.height;const minHeight=document.documentElement.clientHeight;if(minHeight<popperHeight&&state.modifiersData.hide){state.modifiersData.hide={...state.modifiersData.hide,isReferenceHidden:false};}}const smallViewportModifier={name:"smallViewport",enabled:true,phase:"main",fn:_modifyPosition};const VIEWPORT_PADDING=parseInt(wonderBlocksTokens.remToPx(wonderBlocksTokens.tokenValue(wonderBlocksTokens.sizing.size_120)));class TooltipPopper extends React__namespace.Component{componentDidMount(){const{anchorElement,autoUpdate}=this.props;if(!anchorElement||!autoUpdate){return}this._observer=new MutationObserver(()=>{this._popperUpdate?.();});this._observer.observe(anchorElement,{attributes:true,childList:true,subtree:true});}componentWillUnmount(){this._observer?.disconnect();}_renderPositionedContent(popperProps){const{children}=this.props;const{isReady}=this.state;const placement=filterPopperPlacement(popperProps.placement)||this.props.placement;this._bubbleRefTracker.setCallback(popperProps.ref);this._tailRefTracker.setCallback(popperProps.arrowProps.ref);this._popperUpdate=popperProps.update;const bubbleProps={placement,style:{top:popperProps.style.top,left:popperProps.style.left,bottom:popperProps.style.bottom,right:popperProps.style.right,position:popperProps.style.position,transform:popperProps.style.transform,visibility:!isReady?"hidden":undefined},updateBubbleRef:this._bubbleRefTracker.updateRef,tailOffset:{bottom:popperProps.arrowProps.style.bottom,right:popperProps.arrowProps.style.right,top:popperProps.arrowProps.style.top,left:popperProps.arrowProps.style.left,transform:popperProps.arrowProps.style.transform},updateTailRef:this._tailRefTracker.updateRef,isReferenceHidden:popperProps.isReferenceHidden};return children(bubbleProps)}render(){const{anchorElement,placement,rootBoundary,viewportPadding}=this.props;const modifiers=[smallViewportModifier];if(rootBoundary==="viewport"){modifiers.push({name:"preventOverflow",options:{rootBoundary:"viewport",padding:viewportPadding}});}else {modifiers.push({name:"flip",options:{rootBoundary:"document"}});}return jsxRuntime.jsx(reactPopper.Popper,{referenceElement:anchorElement,strategy:"fixed",placement:placement,modifiers:modifiers,onFirstUpdate:this.handleFirstUpdate,children:props=>this._renderPositionedContent(props)})}constructor(props){super(props),this._bubbleRefTracker=new RefTracker,this._tailRefTracker=new RefTracker,this._observer=null,this._popperUpdate=null,this.handleFirstUpdate=()=>{this.setState({isReady:true});};this.state={isReady:false};}}TooltipPopper.defaultProps={rootBoundary:"viewport",viewportPadding:VIEWPORT_PADDING};
|
|
53
53
|
|
|
54
|
-
class Tooltip extends React__namespace.Component{static getDerivedStateFromProps(props,state){return {active:typeof props.opened==="boolean"?props.opened:state.active}}_updateAnchorElement(ref){if(ref&&ref!==this.state.anchorElement){this.setState({anchorElement:ref});}}_renderBubbleContent(){const{title,content,contentStyle,testId}=this.props;if(typeof content==="string"){return jsxRuntime.jsx(TooltipContent,{title:title,contentStyle:contentStyle,testId:testId?`${testId}-content`:undefined,children:content})}else if(title){return React__namespace.cloneElement(content,{title})}else {return content}}_renderPopper(ariaContentId){const{backgroundColor,placement}=this.props;return jsxRuntime.jsx(TooltipPopper,{anchorElement:this.state.anchorElement,placement:placement,autoUpdate:this.props.autoUpdate,viewportPadding:this.props.viewportPadding,children:props=>jsxRuntime.jsx(TooltipBubble,{id:ariaContentId,style:props.style,backgroundColor:backgroundColor,tailOffset:props.tailOffset,isReferenceHidden:props.isReferenceHidden,placement:props.placement,updateTailRef:props.updateTailRef,updateBubbleRef:props.updateBubbleRef,onActiveChanged:active=>this.setState({activeBubble:active}),children:this._renderBubbleContent()})})}_getHost(){const{anchorElement}=this.state;return wonderBlocksModal.maybeGetPortalMountedModalHostElement(anchorElement)||document.body}_renderTooltipAnchor(uniqueId){const{autoUpdate,children,forceAnchorFocusivity}=this.props;const{active,activeBubble}=this.state;const popperHost=this._getHost();const shouldAnchorExist=autoUpdate?this.state.anchorElement:true;const shouldBeVisible=popperHost&&(active||activeBubble)&&shouldAnchorExist;const ariaContentId=`${uniqueId}-aria-content`;return jsxRuntime.jsxs(React__namespace.Fragment,{children:[jsxRuntime.jsx(TooltipAnchor,{forceAnchorFocusivity:forceAnchorFocusivity,anchorRef:r=>this._updateAnchorElement(r),onActiveChanged:active=>this.setState({active}),"aria-describedby":shouldBeVisible?ariaContentId:undefined,children:children}),shouldBeVisible&&ReactDOM__namespace.createPortal(this._renderPopper(ariaContentId),popperHost)]})}render(){const{id}=this.props;return jsxRuntime.jsx(wonderBlocksCore.Id,{id:id,children:uniqueId=>this._renderTooltipAnchor(uniqueId)})}constructor(...args){super(...args),this.state={active:false,activeBubble:false};}}Tooltip.defaultProps={forceAnchorFocusivity:true,placement:"top"};
|
|
54
|
+
class Tooltip extends React__namespace.Component{static getDerivedStateFromProps(props,state){return {active:typeof props.opened==="boolean"?props.opened:state.active}}_updateAnchorElement(ref){if(ref&&ref!==this.state.anchorElement){this.setState({anchorElement:ref});}}_renderBubbleContent(){const{title,content,contentStyle,testId}=this.props;if(typeof content==="string"){return jsxRuntime.jsx(TooltipContent,{title:title,contentStyle:contentStyle,testId:testId?`${testId}-content`:undefined,children:content})}else if(title){return React__namespace.cloneElement(content,{title})}else {return content}}_renderPopper(ariaContentId){const{backgroundColor,placement,variant}=this.props;return jsxRuntime.jsx(TooltipPopper,{anchorElement:this.state.anchorElement,placement:placement,autoUpdate:this.props.autoUpdate,viewportPadding:this.props.viewportPadding,children:props=>jsxRuntime.jsx(TooltipBubble,{id:ariaContentId,style:props.style,backgroundColor:backgroundColor,variant:variant,tailOffset:props.tailOffset,isReferenceHidden:props.isReferenceHidden,placement:props.placement,updateTailRef:props.updateTailRef,updateBubbleRef:props.updateBubbleRef,onActiveChanged:active=>this.setState({activeBubble:active}),children:this._renderBubbleContent()})})}_getHost(){const{anchorElement}=this.state;return wonderBlocksModal.maybeGetPortalMountedModalHostElement(anchorElement)||document.body}_renderTooltipAnchor(uniqueId){const{autoUpdate,children,forceAnchorFocusivity}=this.props;const{active,activeBubble}=this.state;const popperHost=this._getHost();const shouldAnchorExist=autoUpdate?this.state.anchorElement:true;const shouldBeVisible=popperHost&&(active||activeBubble)&&shouldAnchorExist;const ariaContentId=`${uniqueId}-aria-content`;return jsxRuntime.jsxs(React__namespace.Fragment,{children:[jsxRuntime.jsx(TooltipAnchor,{forceAnchorFocusivity:forceAnchorFocusivity,anchorRef:r=>this._updateAnchorElement(r),onActiveChanged:active=>this.setState({active}),"aria-describedby":shouldBeVisible?ariaContentId:undefined,children:children}),shouldBeVisible&&ReactDOM__namespace.createPortal(this._renderPopper(ariaContentId),popperHost)]})}render(){const{id}=this.props;return jsxRuntime.jsx(wonderBlocksCore.Id,{id:id,children:uniqueId=>this._renderTooltipAnchor(uniqueId)})}constructor(...args){super(...args),this.state={active:false,activeBubble:false};}}Tooltip.defaultProps={forceAnchorFocusivity:true,placement:"top",variant:"subtle"};
|
|
55
55
|
|
|
56
56
|
exports.TooltipContent = TooltipContent;
|
|
57
57
|
exports.TooltipPopper = TooltipPopper;
|
package/dist/util/types.d.ts
CHANGED
|
@@ -11,6 +11,15 @@ export type Offset = {
|
|
|
11
11
|
transform: CSSProperties["transform"];
|
|
12
12
|
};
|
|
13
13
|
export type Placement = "top" | "bottom" | "right" | "left";
|
|
14
|
+
/**
|
|
15
|
+
* The visual style of the tooltip.
|
|
16
|
+
*
|
|
17
|
+
* - `subtle`: the default, standard tooltip styling.
|
|
18
|
+
* - `strong`: a higher-emphasis tooltip that uses an inverse/knockout
|
|
19
|
+
* treatment. Its colors are defined with semantic tokens, so they adapt to
|
|
20
|
+
* the active theme.
|
|
21
|
+
*/
|
|
22
|
+
export type TooltipVariant = "subtle" | "strong";
|
|
14
23
|
/**
|
|
15
24
|
* Subset of CSS properties to allow overriding some of the default styles
|
|
16
25
|
*/
|
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"description": "",
|
|
4
4
|
"author": "Khan Academy",
|
|
5
5
|
"license": "MIT",
|
|
6
|
-
"version": "4.
|
|
6
|
+
"version": "4.2.0",
|
|
7
7
|
"publishConfig": {
|
|
8
8
|
"access": "public"
|
|
9
9
|
},
|
|
@@ -21,8 +21,8 @@
|
|
|
21
21
|
"types": "dist/index.d.ts",
|
|
22
22
|
"dependencies": {
|
|
23
23
|
"@khanacademy/wonder-blocks-core": "12.4.4",
|
|
24
|
-
"@khanacademy/wonder-blocks-modal": "8.7.10",
|
|
25
24
|
"@khanacademy/wonder-blocks-layout": "3.1.59",
|
|
25
|
+
"@khanacademy/wonder-blocks-modal": "8.7.10",
|
|
26
26
|
"@khanacademy/wonder-blocks-tokens": "17.2.0",
|
|
27
27
|
"@khanacademy/wonder-blocks-typography": "5.0.2"
|
|
28
28
|
},
|