@myzbox/react-overlay 1.0.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.
@@ -0,0 +1,26 @@
1
+ (function(b,u){typeof exports=="object"&&typeof module<"u"?u(exports,require("react"),require("react-dom")):typeof define=="function"&&define.amd?define(["exports","react","react-dom"],u):(b=typeof globalThis<"u"?globalThis:b||self,u(b.ReactOverlay={},b.React,b.ReactDOM))})(this,(function(b,u,me){"use strict";function pe(r){return r&&r.__esModule&&Object.prototype.hasOwnProperty.call(r,"default")?r.default:r}var Y={exports:{}},M={};/**
2
+ * @license React
3
+ * react-jsx-runtime.production.js
4
+ *
5
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
6
+ *
7
+ * This source code is licensed under the MIT license found in the
8
+ * LICENSE file in the root directory of this source tree.
9
+ */var oe;function be(){if(oe)return M;oe=1;var r=Symbol.for("react.transitional.element"),a=Symbol.for("react.fragment");function s(l,n,t){var o=null;if(t!==void 0&&(o=""+t),n.key!==void 0&&(o=""+n.key),"key"in n){t={};for(var c in n)c!=="key"&&(t[c]=n[c])}else t=n;return n=t.ref,{$$typeof:r,type:l,key:o,ref:n!==void 0?n:null,props:t}}return M.Fragment=a,M.jsx=s,M.jsxs=s,M}var I={};/**
10
+ * @license React
11
+ * react-jsx-runtime.development.js
12
+ *
13
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
14
+ *
15
+ * This source code is licensed under the MIT license found in the
16
+ * LICENSE file in the root directory of this source tree.
17
+ */var ne;function he(){return ne||(ne=1,process.env.NODE_ENV!=="production"&&(function(){function r(e){if(e==null)return null;if(typeof e=="function")return e.$$typeof===O?null:e.displayName||e.name||null;if(typeof e=="string")return e;switch(e){case C:return"Fragment";case B:return"Profiler";case K:return"StrictMode";case L:return"Suspense";case Q:return"SuspenseList";case w:return"Activity"}if(typeof e=="object")switch(typeof e.tag=="number"&&console.error("Received an unexpected object in getComponentNameFromType(). This is likely a bug in React. Please file an issue."),e.$$typeof){case Z:return"Portal";case W:return e.displayName||"Context";case F:return(e._context.displayName||"Context")+".Consumer";case U:var d=e.render;return e=e.displayName,e||(e=d.displayName||d.name||"",e=e!==""?"ForwardRef("+e+")":"ForwardRef"),e;case ee:return d=e.displayName||null,d!==null?d:r(e.type)||"Memo";case D:d=e._payload,e=e._init;try{return r(e(d))}catch{}}return null}function a(e){return""+e}function s(e){try{a(e);var d=!1}catch{d=!0}if(d){d=console;var p=d.error,h=typeof Symbol=="function"&&Symbol.toStringTag&&e[Symbol.toStringTag]||e.constructor.name||"Object";return p.call(d,"The provided key is an unsupported type %s. This value must be coerced to a string before using it here.",h),a(e)}}function l(e){if(e===C)return"<>";if(typeof e=="object"&&e!==null&&e.$$typeof===D)return"<...>";try{var d=r(e);return d?"<"+d+">":"<...>"}catch{return"<...>"}}function n(){var e=S.A;return e===null?null:e.getOwner()}function t(){return Error("react-stack-top-frame")}function o(e){if(P.call(e,"key")){var d=Object.getOwnPropertyDescriptor(e,"key").get;if(d&&d.isReactWarning)return!1}return e.key!==void 0}function c(e,d){function p(){ie||(ie=!0,console.error("%s: `key` is not a prop. Trying to access it will result in `undefined` being returned. If you need to access the same value within the child component, you should pass it as a different prop. (https://react.dev/link/special-props)",d))}p.isReactWarning=!0,Object.defineProperty(e,"key",{get:p,configurable:!0})}function i(){var e=r(this.type);return ue[e]||(ue[e]=!0,console.error("Accessing element.ref was removed in React 19. ref is now a regular prop. It will be removed from the JSX Element type in a future release.")),e=this.props.ref,e!==void 0?e:null}function _(e,d,p,h,X,te){var v=p.ref;return e={$$typeof:$,type:e,key:d,props:p,_owner:h},(v!==void 0?v:null)!==null?Object.defineProperty(e,"ref",{enumerable:!1,get:i}):Object.defineProperty(e,"ref",{enumerable:!1,value:null}),e._store={},Object.defineProperty(e._store,"validated",{configurable:!1,enumerable:!1,writable:!0,value:0}),Object.defineProperty(e,"_debugInfo",{configurable:!1,enumerable:!1,writable:!0,value:null}),Object.defineProperty(e,"_debugStack",{configurable:!1,enumerable:!1,writable:!0,value:X}),Object.defineProperty(e,"_debugTask",{configurable:!1,enumerable:!1,writable:!0,value:te}),Object.freeze&&(Object.freeze(e.props),Object.freeze(e)),e}function m(e,d,p,h,X,te){var v=d.children;if(v!==void 0)if(h)if(z(v)){for(h=0;h<v.length;h++)g(v[h]);Object.freeze&&Object.freeze(v)}else console.error("React.jsx: Static children should always be an array. You are likely explicitly calling React.jsxs or React.jsxDEV. Use the Babel transform instead.");else g(v);if(P.call(d,"key")){v=r(e);var A=Object.keys(d).filter(function(Ce){return Ce!=="key"});h=0<A.length?"{key: someKey, "+A.join(": ..., ")+": ...}":"{key: someKey}",_e[v+h]||(A=0<A.length?"{"+A.join(": ..., ")+": ...}":"{}",console.error(`A props object containing a "key" prop is being spread into JSX:
18
+ let props = %s;
19
+ <%s {...props} />
20
+ React keys must be passed directly to JSX without using spread:
21
+ let props = %s;
22
+ <%s key={someKey} {...props} />`,h,v,A,v),_e[v+h]=!0)}if(v=null,p!==void 0&&(s(p),v=""+p),o(d)&&(s(d.key),v=""+d.key),"key"in d){p={};for(var re in d)re!=="key"&&(p[re]=d[re])}else p=d;return v&&c(p,typeof e=="function"?e.displayName||e.name||"Unknown":e),_(e,v,p,n(),X,te)}function g(e){E(e)?e._store&&(e._store.validated=1):typeof e=="object"&&e!==null&&e.$$typeof===D&&(e._payload.status==="fulfilled"?E(e._payload.value)&&e._payload.value._store&&(e._payload.value._store.validated=1):e._store&&(e._store.validated=1))}function E(e){return typeof e=="object"&&e!==null&&e.$$typeof===$}var k=u,$=Symbol.for("react.transitional.element"),Z=Symbol.for("react.portal"),C=Symbol.for("react.fragment"),K=Symbol.for("react.strict_mode"),B=Symbol.for("react.profiler"),F=Symbol.for("react.consumer"),W=Symbol.for("react.context"),U=Symbol.for("react.forward_ref"),L=Symbol.for("react.suspense"),Q=Symbol.for("react.suspense_list"),ee=Symbol.for("react.memo"),D=Symbol.for("react.lazy"),w=Symbol.for("react.activity"),O=Symbol.for("react.client.reference"),S=k.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE,P=Object.prototype.hasOwnProperty,z=Array.isArray,N=console.createTask?console.createTask:function(){return null};k={react_stack_bottom_frame:function(e){return e()}};var ie,ue={},de=k.react_stack_bottom_frame.bind(k,t)(),fe=N(l(t)),_e={};I.Fragment=C,I.jsx=function(e,d,p){var h=1e4>S.recentlyCreatedOwnerStacks++;return m(e,d,p,!1,h?Error("react-stack-top-frame"):de,h?N(l(e)):fe)},I.jsxs=function(e,d,p){var h=1e4>S.recentlyCreatedOwnerStacks++;return m(e,d,p,!0,h?Error("react-stack-top-frame"):de,h?N(l(e)):fe)}})()),I}var se;function ve(){return se||(se=1,process.env.NODE_ENV==="production"?Y.exports=be():Y.exports=he()),Y.exports}var f=ve();const y={overlay:"_overlay_1pta6_15",open:"_open_1pta6_32",center:"_center_1pta6_38",top:"_top_1pta6_44",bottom:"_bottom_1pta6_50",left:"_left_1pta6_55",right:"_right_1pta6_61","top-left":"_top-left_1pta6_66","top-right":"_top-right_1pta6_73","bottom-left":"_bottom-left_1pta6_80","bottom-right":"_bottom-right_1pta6_87",modal:"_modal_1pta6_94",openScale:"_openScale_1pta6_1",sm:"_sm_1pta6_124",md:"_md_1pta6_128",lg:"_lg_1pta6_132",xl:"_xl_1pta6_136",full:"_full_1pta6_140",auto:"_auto_1pta6_147","slide-up":"_slide-up_1pta6_164","slide-down":"_slide-down_1pta6_172",fade:"_fade_1pta6_180","slide-left":"_slide-left_1pta6_188","slide-right":"_slide-right_1pta6_196","drawer-slide-right":"_drawer-slide-right_1pta6_205","drawer-slide-left":"_drawer-slide-left_1pta6_213","drawer-slide-up":"_drawer-slide-up_1pta6_221","drawer-slide-down":"_drawer-slide-down_1pta6_229",header:"_header_1pta6_237",draggable:"_draggable_1pta6_247",title:"_title_1pta6_252",closeButton:"_closeButton_1pta6_259",content:"_content_1pta6_294",footer:"_footer_1pta6_305"};var J={exports:{}};/*!
23
+ Copyright (c) 2018 Jed Watson.
24
+ Licensed under the MIT License (MIT), see
25
+ http://jedwatson.github.io/classnames
26
+ */var ae;function ge(){return ae||(ae=1,(function(r){(function(){var a={}.hasOwnProperty;function s(){for(var t="",o=0;o<arguments.length;o++){var c=arguments[o];c&&(t=n(t,l(c)))}return t}function l(t){if(typeof t=="string"||typeof t=="number")return t;if(typeof t!="object")return"";if(Array.isArray(t))return s.apply(null,t);if(t.toString!==Object.prototype.toString&&!t.toString.toString().includes("[native code]"))return t.toString();var o="";for(var c in t)a.call(t,c)&&t[c]&&(o=n(o,c));return o}function n(t,o){return o?t?t+" "+o:t+o:t}r.exports?(s.default=s,r.exports=s):window.classNames=s})()})(J)),J.exports}var we=ge();const T=pe(we),q=(r,a)=>{const s=u.useRef(!1),l=u.useRef({x:0,y:0}),n=u.useRef({x:0,y:0}),t=u.useCallback(i=>{if(!s.current||!a.current)return;const _=i.clientX-l.current.x,m=i.clientY-l.current.y,g=n.current.x+_,E=n.current.y+m;a.current.style.transform=`translate(${g}px, ${E}px)`},[a]),o=u.useCallback(i=>{if(!s.current)return;s.current=!1;const _=i.clientX-l.current.x,m=i.clientY-l.current.y;n.current={x:n.current.x+_,y:n.current.y+m},a.current&&(a.current.style.transition=""),document.body.style.userSelect="",window.removeEventListener("mousemove",t),window.removeEventListener("mouseup",o)},[a,t]),c=i=>{!r||!a.current||(s.current=!0,l.current={x:i.clientX,y:i.clientY},a.current.style.transition="none",document.body.style.userSelect="none",window.addEventListener("mousemove",t),window.addEventListener("mouseup",o))};return u.useEffect(()=>()=>{window.removeEventListener("mousemove",t),window.removeEventListener("mouseup",o)},[t,o]),u.useEffect(()=>{r||(n.current={x:0,y:0})},[r]),{onMouseDown:c}},V=({isOpen:r,onClose:a,children:s,title:l,footer:n,closeOnOverlayClick:t=!0,closeOnEsc:o=!0,overlayClassName:c,className:i,overlayStyle:_,style:m,position:g="center",size:E="md",animation:k="zoom",initialFocusRef:$,zIndex:Z,draggable:C=!1,hideHeader:K=!1})=>{const[B,F]=u.useState(!1),[W,U]=u.useState(!1),L=u.useRef(null),{onMouseDown:Q}=q(C,L);if(u.useEffect(()=>{if(r)F(!0),requestAnimationFrame(()=>U(!0));else{U(!1);const w=setTimeout(()=>{F(!1)},200);return()=>clearTimeout(w)}},[r]),u.useEffect(()=>{if(!r||!o)return;const w=O=>{O.key==="Escape"&&a()};return document.addEventListener("keydown",w),()=>document.removeEventListener("keydown",w)},[r,o,a]),u.useEffect(()=>{if(!r||!B)return;const w=L.current;if(!w)return;$!=null&&$.current?$.current.focus():w.focus();const O=w.querySelectorAll('a[href], button:not([disabled]), textarea:not([disabled]), input:not([disabled]), select:not([disabled]), [tabindex]:not([tabindex="-1"])'),S=O[0],P=O[O.length-1],z=N=>{N.key==="Tab"&&(N.shiftKey?document.activeElement===S&&(N.preventDefault(),P==null||P.focus()):document.activeElement===P&&(N.preventDefault(),S==null||S.focus()))};return w.addEventListener("keydown",z),()=>w.removeEventListener("keydown",z)},[r,B,$]),u.useEffect(()=>{if(r){const w=window.getComputedStyle(document.body).overflow;return document.body.style.overflow="hidden",()=>{document.body.style.overflow=w}}},[r]),!B)return null;const ee=w=>{w.target===w.currentTarget&&t&&a()},D=f.jsx("div",{className:T(y.overlay,{[y.open]:W},y[g],c),style:{zIndex:Z,..._},onClick:ee,role:"presentation",children:f.jsxs("div",{ref:L,className:T(y.modal,{[y.open]:W},y[E],y[k],i),style:m,role:"dialog","aria-modal":"true","aria-labelledby":l?"modal-title":void 0,tabIndex:-1,children:[!K&&f.jsxs("div",{className:T(y.header,{[y.draggable]:C}),onMouseDown:Q,style:{cursor:C?"move":"default"},children:[l?f.jsx("h2",{id:"modal-title",className:y.title,children:l}):f.jsx("div",{}),f.jsx("button",{type:"button",className:y.closeButton,onClick:a,"aria-label":"Close modal",children:f.jsxs("svg",{width:"20",height:"20",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2.5",strokeLinecap:"round",strokeLinejoin:"round",children:[f.jsx("line",{x1:"18",y1:"6",x2:"6",y2:"18"}),f.jsx("line",{x1:"6",y1:"6",x2:"18",y2:"18"})]})})]}),f.jsx("div",{className:y.content,children:s}),n&&f.jsx("div",{className:y.footer,children:n})]})});return me.createPortal(D,document.body)},x={drawer:"_drawer_43ihb_24",left:"_left_43ihb_29",right:"_right_43ihb_30",top:"_top_43ihb_35",bottom:"_bottom_43ihb_36",alertModal:"_alertModal_43ihb_41",alertContent:"_alertContent_43ihb_47",icon:"_icon_43ihb_53",success:"_success_43ihb_64",error:"_error_43ihb_69",warning:"_warning_43ihb_74",info:"_info_43ihb_79",okButton:"_okButton_43ihb_84",confirmButton:"_confirmButton_43ihb_85",cancelButton:"_cancelButton_43ihb_107",destructive:"_destructive_43ihb_126"},Ee=({type:r="info",message:a,onOk:s,okText:l="OK",onClose:n,className:t,showHeader:o=!0,...c})=>{const i=()=>{s==null||s(),n()};return f.jsx(V,{...c,onClose:n,className:T(x.alertModal,x[r],t),size:"sm",hideHeader:!o,footer:f.jsx("button",{className:x.okButton,onClick:i,children:l}),children:f.jsxs("div",{className:x.alertContent,children:[f.jsxs("div",{className:x.icon,children:[r==="success"&&"✓",r==="error"&&"✕",r==="warning"&&"!",r==="info"&&"i"]}),f.jsx("div",{className:x.message,children:a})]})})},ye=({message:r,onConfirm:a,onCancel:s,confirmText:l="Confirm",cancelText:n="Cancel",isDestructive:t=!1,onClose:o,className:c,...i})=>{const[_,m]=u.useState(!1),g=async()=>{try{m(!0),await a(),o()}catch(k){console.error(k)}finally{m(!1)}},E=()=>{s==null||s(),o()};return f.jsx(V,{...i,onClose:o,className:T(x.confirmModal,c),size:"sm",closeOnOverlayClick:!_,closeOnEsc:!_,footer:f.jsxs(f.Fragment,{children:[f.jsx("button",{className:x.cancelButton,onClick:E,disabled:_,children:n}),f.jsx("button",{className:T(x.confirmButton,{[x.destructive]:t}),onClick:g,disabled:_,children:_?"Processing...":l})]}),children:f.jsx("div",{className:x.confirmContent,children:r})})},xe=({placement:r="right",className:a,...s})=>{let l="right",n="slide-right";switch(r){case"left":l="left",n="drawer-slide-left";break;case"top":l="top",n="drawer-slide-down";break;case"bottom":l="bottom",n="drawer-slide-up";break;case"right":default:l="right",n="drawer-slide-right";break}return f.jsx(V,{...s,position:l,animation:n,className:T(x.drawer,x[r],a)})},R={wrapper:"_wrapper_1bfkp_1",trigger:"_trigger_1bfkp_6",popover:"_popover_1bfkp_11",fadeIn:"_fadeIn_1bfkp_1",draggable:"_draggable_1bfkp_24",bottom:"_bottom_1bfkp_30",top:"_top_1bfkp_37",left:"_left_1bfkp_44",right:"_right_1bfkp_51"},le=(r,a)=>{u.useEffect(()=>{const s=l=>{!r.current||r.current.contains(l.target)||a(l)};return document.addEventListener("mousedown",s),document.addEventListener("touchstart",s),()=>{document.removeEventListener("mousedown",s),document.removeEventListener("touchstart",s)}},[r,a])},Te=({children:r,content:a,position:s="bottom",className:l,style:n,width:t,draggable:o=!1})=>{const[c,i]=u.useState(!1),_=u.useRef(null),m=u.useRef(null);le(_,()=>{c&&i(!1)});const{onMouseDown:g}=q(o,m),E=()=>i(!c);return f.jsxs("div",{className:R.wrapper,ref:_,children:[f.jsx("div",{onClick:E,className:R.trigger,children:r}),c&&f.jsx("div",{ref:m,className:T(R.popover,R[s],l,{[R.draggable]:o}),style:{width:t,...n},role:"dialog","aria-modal":"false",onMouseDown:g,children:typeof a=="function"?a(()=>i(!1)):a})]})},j={toast:"_toast_15h14_1",slideIn:"_slideIn_15h14_1",draggable:"_draggable_15h14_19",exiting:"_exiting_15h14_24",success:"_success_15h14_31",error:"_error_15h14_35",warning:"_warning_15h14_39",info:"_info_15h14_43",icon:"_icon_15h14_47",content:"_content_15h14_78",closeBtn:"_closeBtn_15h14_85",container:"_container_15h14_119","top-left":"_top-left_15h14_129","top-right":"_top-right_15h14_135","top-center":"_top-center_15h14_141","bottom-left":"_bottom-left_15h14_148","bottom-right":"_bottom-right_15h14_155","bottom-center":"_bottom-center_15h14_162",slideInBottom:"_slideInBottom_15h14_1"},ce=({id:r,type:a,content:s,duration:l=3e3,onDismiss:n,draggable:t=!1})=>{const[o,c]=u.useState(!1),i=u.useRef(null),{onMouseDown:_}=q(t,i),m=u.useCallback(()=>{c(!0),setTimeout(()=>{n(r)},300)},[r,n]);return u.useEffect(()=>{if(l>0){const g=setTimeout(()=>{m()},l);return()=>clearTimeout(g)}},[l,m]),f.jsxs("div",{ref:i,className:T(j.toast,j[a],{[j.exiting]:o,[j.draggable]:t}),role:"alert",onMouseDown:_,children:[f.jsxs("div",{className:j.icon,children:[a==="success"&&"✓",a==="error"&&"✕",a==="warning"&&"!",a==="info"&&"i"]}),f.jsx("div",{className:j.content,children:s}),f.jsx("button",{className:j.closeBtn,onClick:m,children:"×"})]})},G=u.createContext(void 0);let ke=0;const je=({children:r})=>{const[a,s]=u.useState([]),l=u.useCallback(({closeOthers:c,delay:i=0,..._})=>{const m=`toast-${ke++}`,g={..._,duration:_.duration,delay:i,id:m},E=()=>{s(k=>c?[g]:[...k,g])};i>0?setTimeout(E,i):E()},[]),n=u.useCallback(c=>{s(i=>i.filter(_=>_.id!==c))},[]),t=u.useCallback(()=>{s([])},[]),o=a.reduce((c,i)=>{const _=i.position||"top-right";return c[_]||(c[_]=[]),c[_].push(i),c},{});return f.jsxs(G.Provider,{value:{addToast:l,removeToast:n,clearAll:t},children:[r,Object.keys(o).map(c=>f.jsx("div",{className:T(j.container,j[c]),children:o[c].map(i=>f.jsx(ce,{...i,onDismiss:n},i.id))},c))]})},$e=()=>{const r=u.useContext(G);if(!r)throw new Error("useToast must be used within a ToastProvider");const{addToast:a,removeToast:s,clearAll:l}=r,n=(t,o,c={})=>{let i,_={...c};if(typeof o=="object"&&o!==null&&"content"in o&&!u.isValidElement(o)){const m=o;i=m.content,_={..._,...m}}else i=o;a({type:t,content:i,duration:3e3,position:"top-right",..._})};return{success:(t,o)=>n("success",t,o),error:(t,o)=>n("error",t,o),warning:(t,o)=>n("warning",t,o),info:(t,o)=>n("info",t,o),dismiss:t=>s(t),dismissAll:()=>l()}},H={wrapper:"_wrapper_1dham_1",tooltip:"_tooltip_1dham_6",fadeIn:"_fadeIn_1dham_1",top:"_top_1dham_22",bottom:"_bottom_1dham_28",left:"_left_1dham_34",right:"_right_1dham_40"},Se=({children:r,content:a,position:s="top",delay:l=200,width:n,height:t,className:o,style:c})=>{const[i,_]=u.useState(!1),m=u.useRef(null),g=()=>{m.current=setTimeout(()=>{_(!0)},l)},E=()=>{m.current&&clearTimeout(m.current),_(!1)};return f.jsxs("div",{className:H.wrapper,onMouseEnter:g,onMouseLeave:E,onFocus:g,onBlur:E,children:[r,i&&f.jsx("div",{className:T(H.tooltip,H[s],o),style:{width:n,height:t,...c},role:"tooltip",children:a})]})},Ne=(r=!1)=>{const[a,s]=u.useState(r),l=u.useCallback(()=>s(!0),[]),n=u.useCallback(()=>s(!1),[]),t=u.useCallback(()=>s(o=>!o),[]);return{isOpen:a,open:l,close:n,toggle:t}};b.AlertModal=Ee,b.ConfirmModal=ye,b.Drawer=xe,b.Modal=V,b.Popover=Te,b.Toast=ce,b.ToastContext=G,b.ToastProvider=je,b.Tooltip=Se,b.useClickOutside=le,b.useDraggable=q,b.useModal=Ne,b.useToast=$e,Object.defineProperty(b,Symbol.toStringTag,{value:"Module"})}));
package/package.json ADDED
@@ -0,0 +1,85 @@
1
+ {
2
+ "name": "@myzbox/react-overlay",
3
+ "version": "1.0.0",
4
+ "description": "A comprehensive React overlay library with modals, drawers, tooltips, popovers, and toast notifications",
5
+ "keywords": [
6
+ "react",
7
+ "modal",
8
+ "dialog",
9
+ "overlay",
10
+ "drawer",
11
+ "tooltip",
12
+ "popover",
13
+ "toast",
14
+ "notification",
15
+ "alert",
16
+ "confirm",
17
+ "draggable",
18
+ "accessible",
19
+ "typescript",
20
+ "react-component",
21
+ "ui",
22
+ "myzbox"
23
+ ],
24
+ "author": "myzbox",
25
+ "license": "MIT",
26
+ "repository": {
27
+ "type": "git",
28
+ "url": "https://github.com/myzbox/react-overlay"
29
+ },
30
+ "bugs": {
31
+ "url": "https://github.com/myzbox/react-overlay/issues"
32
+ },
33
+ "homepage": "https://github.com/myzbox/react-overlay#readme",
34
+ "type": "module",
35
+ "main": "./dist/react-overlay.umd.js",
36
+ "module": "./dist/react-overlay.es.js",
37
+ "types": "./dist/index.d.ts",
38
+ "exports": {
39
+ ".": {
40
+ "types": "./dist/index.d.ts",
41
+ "import": "./dist/react-overlay.es.js",
42
+ "require": "./dist/react-overlay.umd.js"
43
+ },
44
+ "./dist/react-overlay.css": "./dist/react-overlay.css",
45
+ "./dist/style.css": "./dist/react-overlay.css",
46
+ "./types/*": "./src/types/*",
47
+ "./styles/*": "./src/styles/*",
48
+ "./hooks/*": "./src/hooks/*"
49
+ },
50
+ "files": [
51
+ "dist",
52
+ "src"
53
+ ],
54
+ "scripts": {
55
+ "dev": "vite",
56
+ "build": "tsc -b && vite build",
57
+ "lint": "eslint .",
58
+ "preview": "vite preview"
59
+ },
60
+ "peerDependencies": {
61
+ "react": "^19.0.0",
62
+ "react-dom": "^19.0.0"
63
+ },
64
+ "dependencies": {
65
+ "classnames": "^2.5.1"
66
+ },
67
+ "devDependencies": {
68
+ "@eslint/js": "^9.17.0",
69
+ "@testing-library/user-event": "^14.6.1",
70
+ "@types/node": "^22.10.2",
71
+ "@types/react": "^19.0.2",
72
+ "@types/react-dom": "^19.0.2",
73
+ "@vitejs/plugin-react": "^4.3.4",
74
+ "eslint": "^9.17.0",
75
+ "eslint-plugin-react-hooks": "^5.0.0",
76
+ "eslint-plugin-react-refresh": "^0.4.16",
77
+ "globals": "^15.14.0",
78
+ "react": "^19.0.0",
79
+ "react-dom": "^19.0.0",
80
+ "typescript": "~5.6.2",
81
+ "typescript-eslint": "^8.18.1",
82
+ "vite": "^6.0.5",
83
+ "vite-plugin-dts": "^4.4.0"
84
+ }
85
+ }
@@ -0,0 +1,11 @@
1
+ export * from './modal/Modal';
2
+ export type { ModalProps, ModalSize, ModalPosition, ModalAnimation } from '../types/Modal';
3
+ export * from './modal-variants/AlertModal';
4
+ export * from './modal-variants/ConfirmModal';
5
+ export * from './modal-variants/Drawer';
6
+ export * from './popover/Popover';
7
+ export type { PopoverProps } from '../types/Popover';
8
+ export * from './toast';
9
+ export type { ToastPosition, ToastOptions } from '../types/Toast';
10
+ export * from './tooltip/Tooltip';
11
+ export type { TooltipProps } from '../types/Tooltip';
@@ -0,0 +1,186 @@
1
+ import React, { useEffect, useRef, useState } from 'react';
2
+ import { createPortal } from 'react-dom';
3
+ import styles from '../../styles/Modal.module.css';
4
+ import type { ModalProps } from '../../types/Modal';
5
+ import classNames from 'classnames';
6
+ import { useDraggable } from '../../hooks/useDraggable';
7
+
8
+ export const Modal: React.FC<ModalProps> = ({
9
+ isOpen,
10
+ onClose,
11
+ children,
12
+ title,
13
+ footer,
14
+ closeOnOverlayClick = true,
15
+ closeOnEsc = true,
16
+ overlayClassName,
17
+ className,
18
+ overlayStyle,
19
+ style,
20
+ position = 'center',
21
+ size = 'md',
22
+ animation = 'zoom',
23
+ initialFocusRef,
24
+
25
+ zIndex,
26
+ draggable = false,
27
+ hideHeader = false,
28
+ }) => {
29
+ const [mounted, setMounted] = useState(false);
30
+ const [visible, setVisible] = useState(false);
31
+ const modalRef = useRef<HTMLDivElement>(null);
32
+ const { onMouseDown } = useDraggable(draggable, modalRef as React.RefObject<HTMLElement>);
33
+
34
+ // Handle mounting for transition
35
+ useEffect(() => {
36
+ if (isOpen) {
37
+ setMounted(true);
38
+ // Small delay to allow CSS transition to initiate
39
+ requestAnimationFrame(() => setVisible(true));
40
+ } else {
41
+ setVisible(false);
42
+ const timer = setTimeout(() => {
43
+ setMounted(false);
44
+ }, 200); // Match CSS transition duration
45
+ return () => clearTimeout(timer);
46
+ }
47
+ }, [isOpen]);
48
+
49
+ // Handle ESC key
50
+ useEffect(() => {
51
+ if (!isOpen || !closeOnEsc) return;
52
+
53
+ const handleKeyDown = (event: KeyboardEvent) => {
54
+ if (event.key === 'Escape') {
55
+ onClose();
56
+ }
57
+ };
58
+
59
+ document.addEventListener('keydown', handleKeyDown);
60
+ return () => document.removeEventListener('keydown', handleKeyDown);
61
+ }, [isOpen, closeOnEsc, onClose]);
62
+
63
+ // Handle Focus Trap (Senior level implementation: simple but effective manual trap for minimal deps)
64
+ useEffect(() => {
65
+ if (!isOpen || !mounted) return;
66
+
67
+ const modalElement = modalRef.current;
68
+ if (!modalElement) return;
69
+
70
+ // Focus initial element or modal itself
71
+ if (initialFocusRef?.current) {
72
+ initialFocusRef.current.focus();
73
+ } else {
74
+ modalElement.focus();
75
+ }
76
+
77
+ const focusableElements = modalElement.querySelectorAll(
78
+ 'a[href], button:not([disabled]), textarea:not([disabled]), input:not([disabled]), select:not([disabled]), [tabindex]:not([tabindex="-1"])'
79
+ );
80
+ const firstElement = focusableElements[0] as HTMLElement;
81
+ const lastElement = focusableElements[focusableElements.length - 1] as HTMLElement;
82
+
83
+ const handleTab = (e: KeyboardEvent) => {
84
+ if (e.key === 'Tab') {
85
+ if (e.shiftKey) {
86
+ if (document.activeElement === firstElement) {
87
+ e.preventDefault();
88
+ lastElement?.focus();
89
+ }
90
+ } else {
91
+ if (document.activeElement === lastElement) {
92
+ e.preventDefault();
93
+ firstElement?.focus();
94
+ }
95
+ }
96
+ }
97
+ };
98
+
99
+ modalElement.addEventListener('keydown', handleTab);
100
+ return () => modalElement.removeEventListener('keydown', handleTab);
101
+ }, [isOpen, mounted, initialFocusRef]);
102
+
103
+ // Lock Body Scroll
104
+ useEffect(() => {
105
+ if (isOpen) {
106
+ const originalStyle = window.getComputedStyle(document.body).overflow;
107
+ document.body.style.overflow = 'hidden';
108
+ return () => {
109
+ document.body.style.overflow = originalStyle;
110
+ };
111
+ }
112
+ }, [isOpen]);
113
+
114
+ if (!mounted) return null;
115
+
116
+ const handleOverlayClick = (e: React.MouseEvent<HTMLDivElement>) => {
117
+ if (e.target === e.currentTarget && closeOnOverlayClick) {
118
+ onClose();
119
+ }
120
+ };
121
+
122
+ const portalContent = (
123
+ <div
124
+ className={classNames(styles.overlay, { [styles.open]: visible }, styles[position], overlayClassName)}
125
+ style={{ zIndex, ...overlayStyle }}
126
+ onClick={handleOverlayClick}
127
+ role="presentation"
128
+ >
129
+ <div
130
+ ref={modalRef}
131
+ className={classNames(
132
+ styles.modal,
133
+ { [styles.open]: visible },
134
+ styles[size],
135
+ styles[animation],
136
+ className
137
+ )}
138
+ style={style}
139
+ role="dialog"
140
+ aria-modal="true"
141
+ aria-labelledby={title ? 'modal-title' : undefined}
142
+ tabIndex={-1}
143
+ >
144
+ {!hideHeader && (
145
+ <div
146
+ className={classNames(styles.header, { [styles.draggable]: draggable })}
147
+ onMouseDown={onMouseDown}
148
+ style={{ cursor: draggable ? 'move' : 'default' }}
149
+ >
150
+ {title ? (
151
+ <h2 id="modal-title" className={styles.title}>{title}</h2>
152
+ ) : (
153
+ <div /> // Placeholder to keep button on right
154
+ )}
155
+ <button
156
+ type="button"
157
+ className={styles.closeButton}
158
+ onClick={onClose}
159
+ aria-label="Close modal"
160
+ >
161
+ <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round">
162
+ <line x1="18" y1="6" x2="6" y2="18"></line>
163
+ <line x1="6" y1="6" x2="18" y2="18"></line>
164
+ </svg>
165
+ </button>
166
+ </div>
167
+ )}
168
+
169
+ <div className={styles.content}>
170
+ {children}
171
+ </div>
172
+
173
+ {footer && (
174
+ <div className={styles.footer}>
175
+ {footer}
176
+ </div>
177
+ )}
178
+ </div>
179
+ </div>
180
+ );
181
+
182
+ return createPortal(portalContent, document.body);
183
+ };
184
+
185
+ export default Modal;
186
+
@@ -0,0 +1,56 @@
1
+ import React from 'react';
2
+ import { Modal } from '../modal/Modal';
3
+ import type { ModalProps } from '../../types/Modal';
4
+ import styles from '../../styles/ModalVariants.module.css';
5
+ import classNames from 'classnames';
6
+
7
+ export type AlertType = 'success' | 'error' | 'warning' | 'info';
8
+
9
+ export interface AlertModalProps extends Omit<ModalProps, 'children' | 'footer'> {
10
+ type?: AlertType;
11
+ message: React.ReactNode;
12
+ onOk?: () => void;
13
+ okText?: string;
14
+ showHeader?: boolean;
15
+ }
16
+
17
+ export const AlertModal: React.FC<AlertModalProps> = ({
18
+ type = 'info',
19
+ message,
20
+ onOk,
21
+ okText = 'OK',
22
+ onClose,
23
+ className,
24
+ showHeader = true,
25
+ ...props
26
+ }) => {
27
+ const handleOk = () => {
28
+ onOk?.();
29
+ onClose();
30
+ };
31
+
32
+ return (
33
+ <Modal
34
+ {...props}
35
+ onClose={onClose}
36
+ className={classNames(styles.alertModal, styles[type], className)}
37
+ size="sm"
38
+ hideHeader={!showHeader}
39
+ footer={
40
+ <button className={styles.okButton} onClick={handleOk}>
41
+ {okText}
42
+ </button>
43
+ }
44
+ >
45
+ <div className={styles.alertContent}>
46
+ <div className={styles.icon}>
47
+ {type === 'success' && '✓'}
48
+ {type === 'error' && '✕'}
49
+ {type === 'warning' && '!'}
50
+ {type === 'info' && 'i'}
51
+ </div>
52
+ <div className={styles.message}>{message}</div>
53
+ </div>
54
+ </Modal>
55
+ );
56
+ };
@@ -0,0 +1,78 @@
1
+ import React, { useState } from 'react';
2
+ import { Modal } from '../modal/Modal';
3
+ import type { ModalProps } from '../../types/Modal';
4
+ import styles from '../../styles/ModalVariants.module.css';
5
+ import classNames from 'classnames';
6
+
7
+ export interface ConfirmModalProps extends Omit<ModalProps, 'children' | 'footer'> {
8
+ message: React.ReactNode;
9
+ onConfirm: () => void | Promise<void>;
10
+ onCancel?: () => void;
11
+ confirmText?: string;
12
+ cancelText?: string;
13
+ isDestructive?: boolean;
14
+ }
15
+
16
+ export const ConfirmModal: React.FC<ConfirmModalProps> = ({
17
+ message,
18
+ onConfirm,
19
+ onCancel,
20
+ confirmText = 'Confirm',
21
+ cancelText = 'Cancel',
22
+ isDestructive = false,
23
+ onClose,
24
+ className,
25
+ ...props
26
+ }) => {
27
+ const [loading, setLoading] = useState(false);
28
+
29
+ const handleConfirm = async () => {
30
+ try {
31
+ setLoading(true);
32
+ await onConfirm();
33
+ onClose();
34
+ } catch (e) {
35
+ console.error(e);
36
+ } finally {
37
+ setLoading(false);
38
+ }
39
+ };
40
+
41
+ const handleCancel = () => {
42
+ onCancel?.();
43
+ onClose();
44
+ };
45
+
46
+ return (
47
+ <Modal
48
+ {...props}
49
+ onClose={onClose}
50
+ className={classNames(styles.confirmModal, className)}
51
+ size="sm"
52
+ closeOnOverlayClick={!loading}
53
+ closeOnEsc={!loading}
54
+ footer={
55
+ <>
56
+ <button
57
+ className={styles.cancelButton}
58
+ onClick={handleCancel}
59
+ disabled={loading}
60
+ >
61
+ {cancelText}
62
+ </button>
63
+ <button
64
+ className={classNames(styles.confirmButton, { [styles.destructive]: isDestructive })}
65
+ onClick={handleConfirm}
66
+ disabled={loading}
67
+ >
68
+ {loading ? 'Processing...' : confirmText}
69
+ </button>
70
+ </>
71
+ }
72
+ >
73
+ <div className={styles.confirmContent}>
74
+ {message}
75
+ </div>
76
+ </Modal>
77
+ );
78
+ };
@@ -0,0 +1,51 @@
1
+ import React from 'react';
2
+ import { Modal } from '../modal/Modal';
3
+ import type { ModalProps, ModalPosition } from '../../types/Modal';
4
+ import classNames from 'classnames';
5
+ import styles from '../../styles/ModalVariants.module.css'; // We might want shared drawer styles here or in Modal.module.css
6
+
7
+ export interface DrawerProps extends ModalProps {
8
+ placement?: 'left' | 'right' | 'top' | 'bottom';
9
+ }
10
+
11
+ export const Drawer: React.FC<DrawerProps> = ({
12
+ placement = 'right',
13
+ className,
14
+ ...props
15
+ }) => {
16
+
17
+ // Map placement to Modal position and animation
18
+ let position: ModalPosition = 'right';
19
+ let animation: ModalProps['animation'] = 'slide-right';
20
+
21
+ switch (placement) {
22
+ case 'left':
23
+ position = 'left';
24
+ animation = 'drawer-slide-left' as ModalProps['animation'];
25
+ break;
26
+ case 'top':
27
+ position = 'top';
28
+ animation = 'drawer-slide-down' as ModalProps['animation'];
29
+ break;
30
+ case 'bottom':
31
+ position = 'bottom';
32
+ animation = 'drawer-slide-up' as ModalProps['animation'];
33
+ break;
34
+ case 'right':
35
+ default:
36
+ position = 'right';
37
+ animation = 'drawer-slide-right' as ModalProps['animation'];
38
+ break;
39
+ }
40
+
41
+ return (
42
+ <Modal
43
+ {...props}
44
+ position={position}
45
+ animation={animation}
46
+ className={classNames(styles.drawer, styles[placement], className)}
47
+ // Drawers usually are full height (for left/right) or full width (for top/bottom)
48
+ // But we can let styles handle that based on position
49
+ />
50
+ );
51
+ };
@@ -0,0 +1,54 @@
1
+ import React, { useState, useRef } from 'react';
2
+ import type { PopoverProps } from '../../types/Popover';
3
+ import styles from '../../styles/Popover.module.css';
4
+ import classNames from 'classnames';
5
+ import { useClickOutside } from '../../hooks/useClickOutside';
6
+ import { useDraggable } from '../../hooks/useDraggable';
7
+
8
+ export const Popover: React.FC<PopoverProps> = ({
9
+ children,
10
+ content,
11
+ position = 'bottom',
12
+ className,
13
+ style,
14
+ width,
15
+ draggable = false,
16
+ }) => {
17
+ const [isOpen, setIsOpen] = useState(false);
18
+ const containerRef = useRef<HTMLDivElement>(null);
19
+ const contentRef = useRef<HTMLDivElement>(null);
20
+
21
+ useClickOutside(containerRef as React.RefObject<HTMLElement>, () => {
22
+ if (isOpen) setIsOpen(false);
23
+ });
24
+
25
+ const { onMouseDown } = useDraggable(draggable, contentRef as React.RefObject<HTMLElement>);
26
+
27
+ const toggleOpen = () => setIsOpen(!isOpen);
28
+
29
+ return (
30
+ <div className={styles.wrapper} ref={containerRef}>
31
+ <div onClick={toggleOpen} className={styles.trigger}>
32
+ {children}
33
+ </div>
34
+
35
+ {isOpen && (
36
+ <div
37
+ ref={contentRef}
38
+ className={classNames(
39
+ styles.popover,
40
+ styles[position],
41
+ className,
42
+ { [styles.draggable]: draggable }
43
+ )}
44
+ style={{ width, ...style }}
45
+ role="dialog"
46
+ aria-modal="false"
47
+ onMouseDown={onMouseDown}
48
+ >
49
+ {typeof content === 'function' ? content(() => setIsOpen(false)) : content}
50
+ </div>
51
+ )}
52
+ </div>
53
+ );
54
+ };
@@ -0,0 +1,61 @@
1
+ import React, { useEffect, useState } from 'react';
2
+ import type { ToastProps } from '../../types/Toast';
3
+ import styles from '../../styles/Toast.module.css';
4
+ import classNames from 'classnames';
5
+ import { useDraggable } from '../../hooks/useDraggable';
6
+ import { useRef } from 'react';
7
+
8
+ export const Toast: React.FC<ToastProps> = ({
9
+ id,
10
+ type,
11
+ content,
12
+ duration = 3000,
13
+ onDismiss,
14
+ draggable = false,
15
+ }) => {
16
+ const [isExiting, setIsExiting] = useState(false);
17
+ const toastRef = useRef<HTMLDivElement>(null);
18
+ const { onMouseDown } = useDraggable(draggable, toastRef as React.RefObject<HTMLElement>);
19
+
20
+ const handleDismiss = React.useCallback(() => {
21
+ setIsExiting(true);
22
+ // Wait for animation to finish before removing from DOM
23
+ setTimeout(() => {
24
+ onDismiss(id);
25
+ }, 300); // 300ms matches CSS animation
26
+ }, [id, onDismiss]);
27
+
28
+ useEffect(() => {
29
+ if (duration > 0) {
30
+ const timer = setTimeout(() => {
31
+ handleDismiss();
32
+ }, duration);
33
+ return () => clearTimeout(timer);
34
+ }
35
+ }, [duration, handleDismiss]);
36
+
37
+ return (
38
+ <div
39
+ ref={toastRef}
40
+ className={classNames(
41
+ styles.toast,
42
+ styles[type],
43
+ {
44
+ [styles.exiting]: isExiting,
45
+ [styles.draggable]: draggable
46
+ }
47
+ )}
48
+ role="alert"
49
+ onMouseDown={onMouseDown}
50
+ >
51
+ <div className={styles.icon}>
52
+ {type === 'success' && '✓'}
53
+ {type === 'error' && '✕'}
54
+ {type === 'warning' && '!'}
55
+ {type === 'info' && 'i'}
56
+ </div>
57
+ <div className={styles.content}>{content}</div>
58
+ <button className={styles.closeBtn} onClick={handleDismiss}>×</button>
59
+ </div>
60
+ );
61
+ };