UNPKG

react-ally

Version:

Accessible react components

2 lines (1 loc) 15.4 kB
import ReactDOM from"react-dom";import{disableBodyScroll,enableBodyScroll}from"body-scroll-lock";import React,{useState,createContext,forwardRef,useContext,useEffect,useRef,Fragment}from"react";import{number,bool,oneOf,string,object,element,func}from"prop-types";function _defineProperty(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function _extends(){return(_extends=Object.assign||function(e){for(var t=1;arguments.length>t;t++){var n=arguments[t];for(var r in n)Object.prototype.hasOwnProperty.call(n,r)&&(e[r]=n[r])}return e}).apply(this,arguments)}function _objectSpread(e){for(var t=1;arguments.length>t;t++){var n=null!=arguments[t]?arguments[t]:{},r=Object.keys(n);"function"==typeof Object.getOwnPropertySymbols&&(r=r.concat(Object.getOwnPropertySymbols(n).filter(function(e){return Object.getOwnPropertyDescriptor(n,e).enumerable}))),r.forEach(function(t){_defineProperty(e,t,n[t])})}return e}function _objectWithoutPropertiesLoose(e,t){if(null==e)return{};var n,r,o={},a=Object.keys(e);for(r=0;a.length>r;r++)0>t.indexOf(n=a[r])&&(o[n]=e[n]);return o}function _objectWithoutProperties(e,t){if(null==e)return{};var n,r,o=_objectWithoutPropertiesLoose(e,t);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);for(r=0;a.length>r;r++)0>t.indexOf(n=a[r])&&Object.prototype.propertyIsEnumerable.call(e,n)&&(o[n]=e[n])}return o}function _slicedToArray(e,t){return _arrayWithHoles(e)||_iterableToArrayLimit(e,t)||_nonIterableRest()}function _arrayWithHoles(e){if(Array.isArray(e))return e}function _iterableToArrayLimit(e,t){var n=[],r=!0,o=!1,a=void 0;try{for(var i,c=e[Symbol.iterator]();!(r=(i=c.next()).done)&&(n.push(i.value),!t||n.length!==t);r=!0);}catch(e){o=!0,a=e}finally{try{r||null==c.return||c.return()}finally{if(o)throw a}}return n}function _nonIterableRest(){throw new TypeError("Invalid attempt to destructure non-iterable instance")}var TAB=9,ESC=27,PAGEUP=33,PAGEDOWN=34,END=35,HOME=36,LEFT=37,UP=38,RIGHT=39,DOWN=40,ArrowTrap=function(e){var t=e.children,n=e.toolbarItem,r=void 0!==n&&n,o=_slicedToArray(useState(),2),a=o[0],i=o[1];return React.createElement(React.Fragment,null,React.Children.map(t,function(e,n){return React.cloneElement(e,{focused:n===a,setFocusOnKeyDown:function(e){return function(e,n){var o=e.keyCode,a=React.Children.count(t),c=function(){return i((n+1)%a)},u=function(){return i((n+a-1)%a)};switch(o){case HOME:r||i(0);break;case END:r||i(a-1);break;case LEFT:r&&0>=n||u();break;case RIGHT:r&&n+1!==a||c();break;case UP:u();break;case DOWN:c()}}(e,n)}})}))},Level=createContext(1),Heading=forwardRef(function(e,t){var n=useContext(Level);return React.createElement("h".concat(n),_extends({ref:t},e))}),Section=function(e){var t=e.children,n=e.levelOverride,r=useContext(Level);return React.createElement(Level.Provider,{value:Math.min(n||r+1,6)},React.createElement(React.Fragment,null,t))};Section.propTypes={levelOverride:number};var logAndThrow=function(e){void 0!==console&&"function"==typeof console.error&&console.error(e);try{throw Error(e)}catch(e){}},logError=function(){var e=arguments.length>0&&void 0!==arguments[0]&&arguments[0],t=arguments.length>1?arguments[1]:void 0;return useEffect(function(){e&&logAndThrow(t)},[])},initialState=function(e,t){switch(e){case"none":return{0:!1};case"first":return{0:!0};case"all":for(var n={},r=t.length,o=0;r>o;o++)n[o]=!0;return n;default:throw Error()}},Group=function(e){var t=e.children,n=e.multi,r=void 0===n||n,o=e.headingLevel,a=e.initialOpen,i=void 0===a?"first":a,c=_objectWithoutProperties(e,["children","multi","headingLevel","initialOpen"]),u=_slicedToArray(useState(initialState(i,t)),2),l=u[0],s=u[1],d=r?function(e){return s(function(t){return Object.assign({},t,_defineProperty({},e,!l[e]))})}:function(e){return s(_defineProperty({},e,!0))};return logError(!r&&"all"===i,'Cannot use multi={false} with initialOpen="all"'),React.createElement(Section,{levelOverride:o},React.createElement("div",c,React.createElement(ArrowTrap,null,React.Children.map(t,function(e,t){return React.cloneElement(e,{accordionId:"".concat(c.id,"-").concat(t,"th"),isOpen:!!l[t],multi:r,onClickHeader:function(){return d(t)}})}))))};Group.propTypes={headingLevel:number,id:string.isRequired,initialOpen:oneOf(["none","first","all"]),multi:bool};var Accordion=function(e){var t=e.children,n=_objectWithoutProperties(e,["children"]);return React.createElement(React.Fragment,null,React.Children.map(t,function(e){return React.cloneElement(e,_objectSpread({},n))}))},buttonId=function(e){return"".concat(e,"-button")},contentId=function(e){return"".concat(e,"-content")},Header=forwardRef(function(e,t){var n=e.accordionId,r=e.buttonProps,o=e.multi,a=e.focused,i=e.isOpen,c=e.onClickHeader,u=e.setFocusOnKeyDown,l=_objectWithoutProperties(e,["accordionId","buttonProps","multi","focused","isOpen","onClickHeader","setFocusOnKeyDown"]),s=useRef(null);return useEffect(function(){a&&s.current.focus()},[a]),React.createElement(Heading,_extends({ref:t},l),React.createElement("button",_extends({},r,{ref:s,"aria-disabled":i&&!o,"aria-expanded":i,"aria-controls":contentId(n),id:buttonId(n),onClick:c,onKeyDown:u}),l.children))});Header.propTypes={buttonProps:object};var Panel=forwardRef(function(e,t){var n=e.accordionId,r=e.isOpen,o=_objectWithoutProperties(e,["accordionId","multi","focused","isOpen","onClickHeader","setFocusOnKeyDown"]);return React.createElement("div",_extends({ref:t},o,{"aria-hidden":!r,"aria-labelledby":buttonId(n),id:contentId(n)}),r&&o.children)});Group.displayName="AccordionGroup",Accordion.displayName="Accordion",Header.displayName="AccordionHeader",Panel.displayName="AccordionPanel";var Breadcrumb=function(e){var t=e.listProps,n=void 0===t?{}:t,r=e.children,o=_objectWithoutProperties(e,["listProps","children"]),a=React.Children.count(r);return React.createElement("nav",_extends({},o,{"aria-label":"Breadcrumb"}),React.createElement("ol",n,React.Children.map(r,function(e,t){return a-1===t?React.cloneElement(e,{"aria-current":"page"}):e})))};Breadcrumb.propTypes={listProps:object};var BreadcrumbLink=function(e){var t=e.listItemProps,n=void 0===t?{}:t,r=_objectWithoutProperties(e,["listItemProps"]);return React.createElement("li",n,React.createElement("a",r))};BreadcrumbLink.propTypes={href:string.isRequired,listItemProps:object},BreadcrumbLink.displayName="BreadcrumbLink";var Overlay=function(e){var t=e.backgroundColor,n=_objectWithoutProperties(e,["backgroundColor"]);return React.createElement("div",_extends({style:{position:"fixed",top:0,left:0,right:0,bottom:0,backgroundColor:t}},n))},FocusTrap=function(e){var t=e.children,n=e.firstTabbableElementRef,r=e.initialFocusElementRef,o=e.lastTabbableElementRef,a=e.wrapperRef;useEffect(function(){setTimeout(function(){(r||n).current.focus()},0)},[]);useEffect(function(){var e=function(e){e.keyCode===TAB&&(o?e.shiftKey&&(e.preventDefault(),o.current.focus()):e.preventDefault())};return n.current.addEventListener("keydown",e),function(){n.current.removeEventListener("keydown",e)}},[]);useEffect(function(){var e=function(e){e.shiftKey||e.keyCode!==TAB||(e.preventDefault(),n.current.focus())};if(o)return o.current.addEventListener("keydown",e),function(){o.current.removeEventListener("keydown",e)}},[]);return useEffect(function(){var e=function(e){e.keyCode===TAB&&e.shiftKey&&a.current===document.activeElement&&(e.preventDefault(),o.current?o.current.focus():n.current.focus())};return a.current.addEventListener("keydown",e),function(){a.current.removeEventListener("keydown",e)}},[]),React.createElement(React.Fragment,null,t&&React.cloneElement(t,{tabIndex:-1}))};FocusTrap.propTypes={children:element.isRequired,firstTabbableElementRef:object.isRequired,initialFocusElementRef:object,lastTabbableElementRef:object,wrapperRef:object.isRequired};var DialogContext=createContext({}),Dialog=forwardRef(function(e,t){var n=e.close,r=void 0===n?function(){}:n,o=e.closeOnOverlayClick,a=void 0===o||o,i=e.firstTabbableElementRef,c=e.isOpen,u=void 0!==c&&c,l=e.initialFocusElementRef,s=e.lastTabbableElementRef,d=e.onClick,f=void 0===d?function(){}:d,b=e.onKeyDown,p=void 0===b?function(){}:b,m=e.overlayBackgroundColor,v=void 0===m?"rgba(255,255,255,0.75)":m,y=e.overlayProps,R=void 0===y?{}:y,E=e.returnFocus,h=void 0===E?{current:document.activeElement}:E,x=_objectWithoutProperties(e,["close","closeOnOverlayClick","firstTabbableElementRef","isOpen","initialFocusElementRef","lastTabbableElementRef","onClick","onKeyDown","overlayBackgroundColor","overlayProps","returnFocus"]);if(!t)throw Error("Dialog requires a ref");useEffect(function(){if(u&&h.current===document.body)throw Error("Dialog did not receive a valid element to send focus after closing. This usually occurs because the dialog initialized open, but wasn't passed a returnFocus ref.")},[u]);var g=useRef(document.createElement("div")),C=_slicedToArray(useState(""),2),T=C[0],w=C[1],P=_slicedToArray(useState(""),2),O=P[0],k=P[1];useEffect(function(){if(u){for(var e=h.current;e.parentElement!==e.ownerDocument.body&&e!==e.ownerDocument.body;)e=e.parentElement;return e.setAttribute("aria-hidden",!0),function(){e.setAttribute("aria-hidden",!1)}}});useEffect(function(){return disableBodyScroll(t.current),function(){enableBodyScroll(t.current)}},[u]),useEffect(function(){if(u)return function(){g.current.parentNode&&g.current.parentNode.removeChild(g.current)}},[u]),useEffect(function(){if(u){var e=h.current;return function(){e.focus()}}},[u]);var D=React.createElement(Overlay,_extends({backgroundColor:v},R,{onClick:function(e){a&&r(e)}}),React.createElement(DialogContext.Provider,{value:{contentId:x.id,setDescribedby:k,setLabelledby:w}},React.createElement(FocusTrap,{firstTabbableElementRef:i,initialFocusElementRef:l,lastTabbableElementRef:s,wrapperRef:t},React.createElement("div",_extends({ref:t,"aria-describedby":O,"aria-labelledby":T,"aria-modal":!0,id:"test-dialog",role:"dialog"},x,{onClick:function(e){e.stopPropagation(),f(e)},onKeyDown:function(e){e.keyCode===ESC&&r(e),p(e)}})))));return u?(document.body.appendChild(g.current),ReactDOM.createPortal(D,g.current)):null});Dialog.displayName="Dialog",Dialog.propTypes={close:func.isRequired,closeOnOverlayClick:bool,firstTabbableElementRef:object.isRequired,id:string.isRequired,isOpen:bool.isRequired,initialFocusElementRef:object,lastTabbableElementRef:object,onClick:func,onKeyDown:func,overlayBackgroundColor:string,overlayProps:object,returnFocus:object};var Description=function(e){var t=useRef(!1);useEffect(function(){t.current=!0},[]);var n=useContext(DialogContext),r=n.setDescribedby,o="".concat(n.contentId,"-description");return t.current||r(o),React.createElement("div",_extends({},e,{id:o}))},Label=function(e){var t=useRef(!1);useEffect(function(){t.current=!0},[]);var n=useContext(DialogContext),r=n.setLabelledby,o="".concat(n.contentId,"-label");return t.current||r(o),React.createElement("h2",_extends({},e,{id:o}))},Helpers=function(){var e=arguments.length>0&&void 0!==arguments[0]&&arguments[0],t=useRef(null),n=useRef(null),r=useRef(null),o=_slicedToArray(useState(e),2),a=o[1];return{close:function(){return a(!1)},firstTabbableElementRef:t,isOpen:o[0],lastTabbableElementRef:n,open:function(){return a(!0)},ref:r}},SpinButtonContext=createContext({}),SpinButton=React.forwardRef(function(e,t){var n=e.bigStep,r=void 0===n?1:n,o=e.onChange,a=e.onKeyDown,i=void 0===a?function(){}:a,c=e.onMouseDown,u=void 0===c?function(){}:c,l=e.tabIndex,s=void 0===l?0:l,d=_objectWithoutProperties(e,["bigStep","onChange","onKeyDown","onMouseDown","tabIndex"]),f=d["aria-valuemax"],b=d["aria-valuemin"];return React.createElement(SpinButtonContext.Provider,{value:{current:d["aria-valuenow"],maxValue:f,minValue:b,onChange:o}},React.createElement("div",_extends({},d,{ref:t,role:"spinbutton",onKeyDown:function(e){switch(e.keyCode){case PAGEUP:o(function(e){return Math.min(e+r,f)});break;case PAGEDOWN:o(function(e){return Math.max(e-r,b)});break;case END:o(f);break;case HOME:o(b);break;case UP:o(function(e){return Math.min(e+1,f)});break;case DOWN:o(function(e){return Math.max(e-1,b)})}i(e)},onMouseDown:function(e){t.current.focus(),u(e)},tabIndex:s})))});SpinButton.displayName="SpinButton",SpinButton.propTypes={"aria-valuemax":number.isRequired,"aria-valuemin":number.isRequired,"aria-valuenow":number.isRequired,"aria-valuetext":string,bigStep:number,onChange:func.isRequired};var Button=function(e){return React.createElement("button",_extends({},e,{"aria-hidden":!0,onMouseDown:function(e){e.preventDefault()},tabIndex:-1}))},UpButton=function(e){var t=useContext(SpinButtonContext),n=t.onChange,r=t.maxValue;return React.createElement(Button,_extends({},e,{disabled:t.current===r,onClick:function(){n(function(e){return Math.min(e+1,r)})}}))},DownButton=function(e){var t=useContext(SpinButtonContext),n=t.onChange,r=t.minValue;return React.createElement(Button,_extends({},e,{disabled:t.current===r,onClick:function(){n(function(e){return Math.max(e-1,r)})}}))},TabsContext=createContext({}),Tabs=function(e){var t=e.initialActiveIndex,n=void 0===t?0:t,r=e.id,o=_objectWithoutProperties(e,["initialActiveIndex","id"]),a=_slicedToArray(useState(n),2);return React.createElement(TabsContext.Provider,{value:{activeIndex:a[0],tabsId:r,setActiveIndex:a[1]}},React.createElement("div",_extends({id:r},o)))};Tabs.propTypes={id:string.isRequired};var TabList=function(e){var t=e.manual,n=void 0!==t&&t,r=_objectWithoutProperties(e,["manual"]),o=_slicedToArray(useState(-1),2),a=o[0],i=o[1];return React.createElement("div",_extends({},r,{role:"tablist"}),React.Children.map(r.children,function(e,t){return React.cloneElement(e,{count:r.children.length,focusIndex:a,index:t,manual:n,setFocusIndex:i})}))};TabList.propTypes={manual:bool};var tabId=function(e,t){return"".concat(e,"-").concat(t,"th-tab")},panelId=function(e,t){return"".concat(e,"-").concat(t,"th-panel")},Tab=function(e){var t=e.count,n=e.focusIndex,r=e.index,o=e.manual,a=e.setFocusIndex,i=_objectWithoutProperties(e,["count","focusIndex","index","manual","setFocusIndex"]),c=useRef(null),u=useRef(!1),l=useContext(TabsContext),s=l.activeIndex,d=l.setActiveIndex,f=l.tabsId,b=r===s,p=n>-1?r===n:r===s;return useEffect(function(){u.current&&p&&c.current.focus()},[s,n]),useEffect(function(){u.current=!0},[]),React.createElement("button",_extends({ref:c,id:tabId(f,r)},i,{"aria-controls":panelId(f,r),"aria-selected":b,onClick:function(){return d(r)},onKeyDown:function(e){var n=o?a:d,i=function(){return n((r+1)%t)},c=function(){return n((r-1+t)%t)};switch(e.keyCode){case 35:n(t-1);break;case 36:n(0);break;case 37:case 38:c();break;case 39:case 40:i()}},role:"tab",tabIndex:b-1}))},TabPanels=function(e){return React.createElement(Fragment,null,React.Children.map(e.children,function(e,t){return React.cloneElement(e,{index:t})}))},TabPanel=function(e){var t=e.index,n=_objectWithoutProperties(e,["index"]),r=useContext(TabsContext),o=r.tabsId,a=t===r.activeIndex;return React.createElement("div",_extends({id:panelId(o,t)},n,{"aria-labelledby":tabId(o,t),"aria-selected":a,role:"tabpanel",style:{display:a?"inherit":"none"},tabIndex:0}))};export{Group as AccordionGroup,Accordion,Header as AccordionHeader,Panel as AccordionPanel,Breadcrumb,BreadcrumbLink,ArrowTrap,Dialog,Label as DialogLabel,Description as DialogDescription,Helpers as DialogHelpers,FocusTrap,Heading,Section,SpinButton,UpButton,DownButton,Tabs,TabList,Tab,TabPanels,TabPanel};