1+ import React , { useEffect } from 'react' ;
2+ import MenuContext from './core/context' ;
3+ import Popup from './Popup' ;
4+
5+ import useStorage from './core/storage' ;
6+ import { calculatePopupPosition , getPopupPosition } from './core/position' ;
7+
8+ function Provider ( {
9+ children,
10+
11+ className = "" ,
12+ noStyle,
13+ theme = "default" ,
14+ animation = "slide-in" ,
15+
16+ inset,
17+ toggle,
18+ placement,
19+ shiftDistance = 10 ,
20+ trigger = "contextmenu" ,
21+ } ) {
22+ const { storage, updateStorage : renewStorage , getStorage, addElements, loopElements } = useStorage ( ) ;
23+
24+ const defaultSettings = {
25+ className,
26+ noStyle,
27+ theme,
28+ animation,
29+ inset,
30+ toggle,
31+ placement,
32+ shiftDistance,
33+ trigger,
34+ } ;
35+
36+ const updateStorage = ( key , updates = { } , ...path ) => {
37+ return renewStorage ( key , updates , ...path ) ;
38+ } ;
39+
40+ const openPopup = ( key , object ) => {
41+ setTimeout ( ( ) => {
42+ const { beforeConstruct = null , onConstruct = null , beforeShow = null , onShown = null , onOpen = null } = object ?. popup ?. events || { } ;
43+ if ( beforeConstruct && beforeConstruct ( object ) === false ) return ;
44+ updateStorage ( key , { enabled : true } , "modal" ) ;
45+ onConstruct && onConstruct ( object ) ;
46+ if ( beforeShow && beforeShow ( object ) === false ) return updateStorage ( key , { enabled : false } , "modal" ) ;
47+ setTimeout ( ( ) => {
48+ const { element } = object . popup ;
49+ const { position, trigger, options } = object . modal ;
50+
51+ const places = [ 'top-left' , 'top' , 'top-right' , 'right-top' , 'right' , 'right-bottom' , 'bottom-right' , 'bottom' , 'bottom-left' , 'left-bottom' , 'left' , 'left-top' ] ;
52+
53+ const { top, left } = options . placement && ( places . includes ( options . placement ) || ( options . inset && options . placement === "center" ) ) ? getPopupPosition ( trigger , element , options ) : calculatePopupPosition ( element , position . top , position . left ) ;
54+ updateStorage ( key , {
55+ position : {
56+ top,
57+ left,
58+ }
59+ } , "modal" ) ;
60+ setTimeout ( ( ) => {
61+ updateStorage ( key , { visible : true } , "modal" ) ;
62+ onShown && onShown ( object ) ;
63+ onOpen && onOpen ( object ) ;
64+ } , 5 ) ;
65+ } , 5 ) ;
66+ } , 5 ) ;
67+ } ;
68+
69+ const closePopup = ( key , object ) => {
70+ setTimeout ( ( ) => {
71+ const { beforeDestroy = null , onDestroy = null , beforeHide = null , onHidden = null , onClose = null } = object ?. popup ?. events || { } ;
72+ if ( beforeHide && beforeHide ( object ) === false ) return ;
73+ updateStorage ( key , { visible : false } , "modal" ) ;
74+ onHidden && onHidden ( object ) ;
75+ if ( beforeDestroy && beforeDestroy ( object ) === false ) return updateStorage ( key , { visible : true } , "modal" ) ;
76+ updateStorage ( key , { modal : { } } ) ;
77+ onDestroy && onDestroy ( object ) ;
78+ onClose && onClose ( object ) ;
79+ } , 5 ) ;
80+
81+ } ;
82+
83+ useEffect ( ( ) => {
84+ const handle = ( e ) => {
85+ Object . keys ( storage ) . forEach ( key => {
86+ const { enabled, visible, toggle } = storage ?. [ key ] ?. modal || { } ;
87+ if ( loopElements ( key , e . target ) && toggle ) return ;
88+ ( enabled || visible ) && closePopup ( key , storage [ key ] ) ;
89+ } ) ;
90+ } ;
91+ window . addEventListener ( "mousedown" , handle ) ;
92+ window . addEventListener ( "scroll" , handle ) ;
93+ window . addEventListener ( "resize" , handle ) ;
94+ return ( ) => {
95+ window . removeEventListener ( "mousedown" , handle ) ;
96+ window . removeEventListener ( "scroll" , handle ) ;
97+ window . removeEventListener ( "resize" , handle ) ;
98+ } ;
99+ } , [ storage ] ) ;
100+
101+ useEffect ( ( ) => {
102+ const styleSheet = `[data-oasismenu-styled=true],[data-oasismenu-styled=true] *{box-sizing: border-box}[data-oasismenu-styled=true]{background-color: var(--bg-popup);transition: 200ms;max-width: 100vw;max-height: 100vh;box-shadow: 5px 5px 10px 2.5px var(--shadow-popup);border-radius: 5px;border: 1px solid var(--border-popup);overflow: auto;padding-block: 5px}[data-oasismenu-styled=true]::-webkit-scrollbar{width: 10px;height: 10px}[data-oasismenu-styled=true]::-webkit-scrollbar-thumb{background: var(--scroll-popup);background-clip: padding-box;border: 3px solid transparent;border-radius: 10px;box-shadow: none;min-height: 40px;min-width: 40px}[data-oasismenu-styled=true]::-webkit-scrollbar-track,[data-oasismenu-styled=true]::-webkit-scrollbar-track:hover{background: none;border: none}[data-oasismenu-styled=true] .oasisbreak{min-height: 1px;max-height: 1px;height: 1px;background-color: var(--bg-break);width: 100%;margin-block: 5px}[data-oasismenu-styled=true] div:has(> .oasisitem){padding-inline: 5px}[data-oasismenu-styled=true] .oasisitem{background-color: var(--bg-popup);color: var(--color-item-primary);display: flex;flex-direction: row;flex-wrap: nowrap;align-items: center;width: 100%;border-radius: 5px;border: none;outline: none;font-size: 15px;transition: 200ms;gap: 15px;font-weight: 500;position: relative;padding: 5px 10px}[data-oasismenu-styled=true] .oasisitem:disabled{background-color: var(--bg-item-disabled);color: var(--color-item-disabled)}[data-oasismenu-styled=true] .oasisitem:not(:disabled).oasishovered{background-color: var(--bg-item-hover);cursor: pointer}[data-oasismenu-styled=true] .oasisicon,[data-oasismenu-styled=true] .oasisstatus{min-width: 20px;display: inline-flex;align-items: center;justify-content: center;flex-wrap: nowrap}[data-oasismenu-styled=true] .oasisicon,[data-oasismenu-styled=true] .oasisstatus,[data-oasismenu-styled=true] .oasiscontent{white-space: nowrap;word-wrap: initial;overflow: hidden;transition: inherit;text-overflow: ellipsis}[data-oasismenu-styled=true] .oasiscontent{flex: 1;text-align: left}[data-oasismenu-styled=true] .oasisstatus{font-size: 0.9em}[data-oasismenu-styled=true] .oasisitem:not(:disabled):not(.oasishovered) .oasisicon,[data-oasismenu-styled=true] .oasisitem:not(:disabled):not(.oasishovered) .oasisstatus{color: var(--color-item-secondary)}[data-oasismenu-styled=true] .oasisafter:not(:empty){margin-left: 30px;white-space: nowrap;font-size: 0.8em;transition: inherit}[data-oasismenu-styled=true] .oasisitem:not(:disabled):not(.oasishovered) .oasisafter{color: var(--color-item-tertiary)}[data-oasismenu-styled=true]:not(:has(.oasisiconhave)) .oasisicon,[data-oasismenu-styled=true]:not(:has(.oasisstatushave)) .oasisstatus,[data-oasismenu-styled=true]:not(:has(.oasisafterhave)) .oasisafter{display: none}[data-oasismenu-styled=true]:not(:has(.oasisiconhave):has(.oasisstatushave):has(.oasisafterhave)) .oasisitem{padding-inline: 15px}[data-oasismenu-styled=true][data-animation]:not([data-animation=""]):not([data-animation="none"]){visibility: hidden;opacity: 0}[data-oasismenu-styled=true][data-animation]:not([data-animation=""]):not([data-animation="none"])[data-visible=true]{visibility: visible;opacity: 1}[data-oasismenu-styled=true][data-animation=slide-in]{transform: translateY(-10px)}[data-oasismenu-styled=true][data-animation=slide-in][data-visible=true]{transform: translateY(0)}[data-oasismenu-styled=true][data-animation=flip-in]{transform: rotateX(-90deg);transform-origin: top}[data-oasismenu-styled=true][data-animation=flip-in][data-visible=true]{transform: rotateX(0)}[data-oasismenu-styled=true][data-animation=drop-in]{transform: translateY(-100%)}[data-oasismenu-styled=true][data-animation=drop-in][data-visible=true]{transform: translateY(0)}[data-oasismenu-styled=true][data-animation=bounce-in]{transform: translateY(-500px)}[data-oasismenu-styled=true][data-animation=bounce-in][data-visible=true]{animation: oasismenu-bounce-in 0.5s cubic-bezier(0.215, 0.610, 0.355, 1.000) both}@keyframes oasismenu-bounce-in{0%{transform: translateY(-500px);animation-timing-function: ease-in;opacity: 0}38%{transform: translateY(0px);animation-timing-function: ease-out;opacity: 1}55%{transform: translateY(-65px);animation-timing-function: ease-in}72%{transform: translateY(0px);animation-timing-function: ease-out}81%{transform: translateY(-28px);animation-timing-function: ease-in}90%{transform: translateY(0px);animation-timing-function: ease-out}95%{transform: translateY(-8px);animation-timing-function: ease-in}100%{transform: translateY(0px);animation-timing-function: ease-out}}` ;
103+ const style = document . createElement ( "style" ) ;
104+ style . dataset . oasismenuStyled = true ;
105+ style . innerHTML = styleSheet ;
106+ document . head . prepend ( style ) ;
107+ return ( ) => document . head . contains ( style ) && document . head . removeChild ( style ) ;
108+ } , [ ] ) ;
109+
110+ return (
111+ < MenuContext . Provider value = { { storage, getStorage, updateStorage, openPopup, closePopup, addElements, defaultSettings } } >
112+ { children }
113+ < Mapping />
114+ </ MenuContext . Provider >
115+ )
116+ } ;
117+
118+ export default Provider ;
119+
120+ function Mapping ( ) {
121+ return (
122+ < MenuContext . Consumer > { ( { storage } ) => Object . keys ( storage ) . map ( key => storage [ key ] ?. modal ?. enabled && < Popup key = { key } data = { storage [ key ] } name = { key } /> ) } </ MenuContext . Consumer >
123+ ) ;
124+ }
0 commit comments