UNPKG

@asphalt-react/popover

Version:

Popover

69 lines (53 loc) 11.3 kB
import React, { forwardRef, cloneElement, useState, useRef } from 'react'; import PropTypes from 'prop-types'; import { FloatingFocusManager, arrow, offset, flip, shift, useFloating, autoUpdate, useClick, useDismiss, useHover, useFocus, useInteractions, useMergeRefs, FloatingPortal, FloatingArrow } from '@floating-ui/react'; import { propsUtil } from '@asphalt-react/helper'; import cn from 'classnames'; import { sendStyles } from '@asphalt-react/context'; import { Text } from '@asphalt-react/typography'; function _extends() { return _extends = Object.assign ? Object.assign.bind() : function (n) { for (var e = 1; e < arguments.length; e++) { var t = arguments[e]; for (var r in t) ({}).hasOwnProperty.call(t, r) && (n[r] = t[r]); } return n; }, _extends.apply(null, arguments); } const FocusManager=({children,popoverContext,focusOrder=["content"],focusTrap=true,returnFocus=true,initialFocus=0,...props})=>{const{style,className,...rest}=props;return React.createElement(FloatingFocusManager,_extends({},rest,{context:popoverContext,order:focusOrder,modal:focusTrap,returnFocus:returnFocus,initialFocus:initialFocus}),children)};FocusManager.displayName="FocusManager";FocusManager.propTypes={children:PropTypes.node.isRequired,popoverContext:PropTypes.object.isRequired,focusOrder:PropTypes.arrayOf(PropTypes.oneOf(["reference","floating","content"])),focusTrap:PropTypes.bool,initialFocus:PropTypes.oneOfType([PropTypes.func,PropTypes.number,PropTypes.shape({current:PropTypes.any})]),returnFocus:PropTypes.bool};FocusManager.defaultProps={focusOrder:["content"],focusTrap:true,initialFocus:0,returnFocus:true}; const getOffset=offset=>{switch(offset){case"noOffset":return 0;case"lowOffset":return 4;case"highOffset":return 16;default:return 8}};const ARROW_SIZE=20;const shouldAdjustArrow=(target,placement)=>{return (target?.width<=ARROW_SIZE||target?.height<=ARROW_SIZE+1)&&/start|end/u.test(placement)}; const{resolvePropCollision}=propsUtil;const usePopover=({open,placement,outOfFlow,flip:flipPosition,shift:shiftContent,size,bezel,stretch,focusOrder,returnFocus,initialFocus,focusTrap,noOffset,lowOffset,mediumOffset,highOffset,onOpenChange,isHover=false,isClick=true,arrowRef,...props})=>{const{collision,value:offsetProp}=resolvePropCollision({noOffset,lowOffset,mediumOffset,highOffset},"mediumOffset");if(collision){console.warn(`usePopover: Multiple offsets detected, falling back to "${offsetProp}"`);}const arrowMiddleware=arrowRef!==null?arrow({element:arrowRef}):null;const middleware=[offset(({rects})=>{const alignmentAxis=shouldAdjustArrow(rects?.reference,placement)?{alignmentAxis:-ARROW_SIZE}:{};return {mainAxis:getOffset(offsetProp),...alignmentAxis}}),...(flipPosition?[flip()]:[]),...(shiftContent?[shift()]:[]),arrowMiddleware];const{x,y,refs,strategy,context}=useFloating({...props,open,placement,strategy:outOfFlow?"fixed":"absolute",onOpenChange,middleware,whileElementsMounted:autoUpdate});const click=isClick?useClick(context):null;const dismiss=useDismiss(context);const hover=isHover?useHover(context,{delay:{open:300,close:100}}):null;const focus=isHover?useFocus(context):null;const{getReferenceProps,getFloatingProps}=useInteractions([click,dismiss,hover,focus]);return {getTargetProps:()=>{return {ref:refs.setReference,...getReferenceProps()}},getPopoverProps:()=>{return {open,size,bezel,ref:refs.setFloating,stretch,anchorStyles:{position:strategy,top:y??0,left:x??0},...getFloatingProps()}},getFocusProps:()=>{return {initialFocus,focusOrder,focusTrap,popoverContext:context,returnFocus}}}}; function styleInject(css, ref) { if ( ref === void 0 ) ref = {}; var insertAt = ref.insertAt; if (!css || typeof document === 'undefined') { return; } var head = document.head || document.getElementsByTagName('head')[0]; var style = document.createElement('style'); style.type = 'text/css'; if (insertAt === 'top') { if (head.firstChild) { head.insertBefore(style, head.firstChild); } else { head.appendChild(style); } } else { head.appendChild(style); } if (style.styleSheet) { style.styleSheet.cssText = css; } else { style.appendChild(document.createTextNode(css)); } } var css_248z$1 = ".Popover__awIQi {\n --surface-color: var(--static-surface-primary, #ffffff);\n --box-shadow: var(--shadow-low, 0px 2px 20px 0px #1e2c6a14);\n --border-radius: var(--roundness-container-S, 0.5rem);\n\n box-shadow: var(--box-shadow);\n background-color: var(--surface-color);\n border-radius: var(--border-radius);\n z-index: 1;\n box-sizing: border-box;\n padding: 1rem;\n border: none;\n width: -moz-fit-content;\n width: fit-content;\n margin: 0;\n}\n\n.s__ryY9V {\n padding: 0.75rem;\n}\n\n.m__lHN6x {\n padding: 1rem;\n}\n\n.l__Ab7cI {\n padding: 1.5rem;\n}\n\n.bezelless__iNfYC {\n padding: 0;\n}\n\n.stretch__vFs7x {\n width: 100%;\n}\n\n.inverse__ljPEh {\n --surface-color: var(--static-surface-inverse, #383942);\n --text-color: var(--content-on-inverse, #ffffff);\n color: var(--text-color);\n}\n"; var styles$1 = {"Popover":"Popover__awIQi","s":"s__ryY9V","m":"m__lHN6x","l":"l__Ab7cI","bezelless":"bezelless__iNfYC","stretch":"stretch__vFs7x","inverse":"inverse__ljPEh"}; var stylesheet$1=".Popover__awIQi {\n --surface-color: var(--static-surface-primary, #ffffff);\n --box-shadow: var(--shadow-low, 0px 2px 20px 0px #1e2c6a14);\n --border-radius: var(--roundness-container-S, 0.5rem);\n\n box-shadow: var(--box-shadow);\n background-color: var(--surface-color);\n border-radius: var(--border-radius);\n z-index: 1;\n box-sizing: border-box;\n padding: 1rem;\n border: none;\n width: -moz-fit-content;\n width: fit-content;\n margin: 0;\n}\n\n.s__ryY9V {\n padding: 0.75rem;\n}\n\n.m__lHN6x {\n padding: 1rem;\n}\n\n.l__Ab7cI {\n padding: 1.5rem;\n}\n\n.bezelless__iNfYC {\n padding: 0;\n}\n\n.stretch__vFs7x {\n width: 100%;\n}\n\n.inverse__ljPEh {\n --surface-color: var(--static-surface-inverse, #383942);\n --text-color: var(--content-on-inverse, #ffffff);\n color: var(--text-color);\n}\n"; styleInject(css_248z$1); const BasePopover=forwardRef(({children,open=false,size="m",bezel=true,stretch=false,anchorStyles,inverse=false,...props},ref)=>{sendStyles(stylesheet$1);const{style,className,...rest}=props;const classes=cn(styles$1.Popover,styles$1[size],{[styles$1.bezelless]:!bezel,[styles$1.stretch]:stretch,[styles$1.inverse]:inverse});return open?React.createElement("dialog",_extends({},rest,{ref:ref,open:open,className:classes,style:anchorStyles}),children):null});BasePopover.displayName="BasePopover";BasePopover.propTypes={children:PropTypes.node.isRequired,open:PropTypes.bool,size:PropTypes.oneOf(["s","m","l"]),bezel:PropTypes.bool,stretch:PropTypes.bool,anchorStyles:PropTypes.object,inverse:PropTypes.bool};BasePopover.defaultProps={open:false,size:"m",bezel:true,stretch:false}; const Popover=forwardRef(({children,target,open=false,onOpenChange=null,placement="bottom",size="m",bezel=true,focusOrder=["content"],initialFocus=0,outOfFlow=false,flip=true,shift=true,focusTrap=true,lowOffset=false,mediumOffset=false,highOffset=false,noOffset=false,stretch=false,returnFocus=true,...props},ref)=>{const{style,className,...rest}=props;const{getTargetProps,getPopoverProps,getFocusProps}=usePopover({open,placement,outOfFlow,size,bezel,flip,focusOrder,returnFocus,initialFocus,focusTrap,noOffset,lowOffset,mediumOffset,highOffset,onOpenChange,shift,stretch,ref});const popoverRef=useMergeRefs([ref,getPopoverProps().ref]);return React.createElement(React.Fragment,null,cloneElement(target,{...getTargetProps()}),open&&React.createElement(FocusManager,getFocusProps(),React.createElement(BasePopover,_extends({},rest,getPopoverProps(),{ref:popoverRef}),children)))});Popover.displayName="Popover";Popover.propTypes={children:PropTypes.node.isRequired,target:PropTypes.node.isRequired,open:PropTypes.bool,size:PropTypes.oneOf(["s","m","l"]),bezel:PropTypes.bool,placement:PropTypes.oneOf(["auto","auto-start","auto-end","top","top-start","top-end","bottom","bottom-start","bottom-end","right","right-start","right-end","left","left-start","left-end"]),focusOrder:PropTypes.arrayOf(PropTypes.oneOf(["reference","floating","content"])),initialFocus:PropTypes.oneOfType([PropTypes.func,PropTypes.number,PropTypes.shape({current:PropTypes.any})]),outOfFlow:PropTypes.bool,flip:PropTypes.bool,stretch:PropTypes.bool,shift:PropTypes.bool,focusTrap:PropTypes.bool,lowOffset:PropTypes.bool,mediumOffset:PropTypes.bool,highOffset:PropTypes.bool,noOffset:PropTypes.bool,onOpenChange:PropTypes.func,returnFocus:PropTypes.bool};Popover.defaultProps={open:false,size:"m",bezel:true,placement:"bottom",focusOrder:["content"],initialFocus:0,outOfFlow:false,flip:true,shift:true,stretch:false,focusTrap:true,lowOffset:false,mediumOffset:false,highOffset:false,noOffset:false,onOpenChange:null,returnFocus:true}; var css_248z = ".content__-k6SR {\n --surface-color: var(--static-surface-inverse, #383942);\n --text-color: var(--content-on-inverse, #ffffff);\n color: var(--text-color);\n}\n\n.arrow__rJMmF {\n --surface-color: var(--static-surface-inverse, #383942);\n fill: var(--surface-color);\n width: 16px;\n height: 16px;\n}\n"; var styles = {"content":"content__-k6SR","arrow":"arrow__rJMmF"}; var stylesheet=".content__-k6SR {\n --surface-color: var(--static-surface-inverse, #383942);\n --text-color: var(--content-on-inverse, #ffffff);\n color: var(--text-color);\n}\n\n.arrow__rJMmF {\n --surface-color: var(--static-surface-inverse, #383942);\n fill: var(--surface-color);\n width: 16px;\n height: 16px;\n}\n"; styleInject(css_248z); const Tooltip=({target,children,arrow=true,placement="bottom",size="m",title,bezel=true,...props})=>{sendStyles(stylesheet);const{style,className,...rest}=props;const[open,setOpen]=useState(false);const arrowRef=useRef(null);const{getTargetProps,getPopoverProps,getFocusProps}=usePopover({open,onOpenChange:setOpen,isHover:true,isClick:false,arrowRef:arrow?arrowRef:null,placement,flip:true,shift:true,bezel});const{popoverContext}=getFocusProps();const classes=cn(styles.Tooltip,styles[size]);const arrowOffset=/bottom|top/u.test(placement)&&/start|end/u.test(placement)?{staticOffset:`${ARROW_SIZE}px`}:{};return React.createElement(React.Fragment,null,cloneElement(target,{...getTargetProps()}),open&&React.createElement(FloatingPortal,null,React.createElement(BasePopover,_extends({},rest,{inverse:true},getPopoverProps(),{role:"tooltip"}),React.createElement("span",{className:classes},title?React.createElement(Text,{div:true,onBrand:true,bold:true,size:size},title):null,React.createElement(Text,{span:true,size:size,onBrand:true},children)),arrow?React.createElement(FloatingArrow,_extends({ref:arrowRef,context:popoverContext,className:styles.arrow,tipRadius:2},arrowOffset)):null)))};Tooltip.displayName="Tooltip";Tooltip.propTypes={children:PropTypes.node.isRequired,target:PropTypes.node.isRequired,title:PropTypes.string,size:PropTypes.oneOf(["s","m","l"]),arrow:PropTypes.bool,placement:PropTypes.oneOf(["top","top-start","top-end","bottom","bottom-start","bottom-end","right","right-start","right-end","left","left-start","left-end"]),bezel:PropTypes.bool}; export { BasePopover, FocusManager, Popover, Tooltip, usePopover };