@startlibs/components 1.0.4 → 1.1.1

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.
Files changed (115) hide show
  1. package/lib/BasicBulletList.mjs +9 -0
  2. package/lib/BasicBulletList.mjs.map +1 -0
  3. package/lib/Button.mjs +95 -0
  4. package/lib/Button.mjs.map +1 -0
  5. package/lib/ContextMenu.mjs +452 -0
  6. package/lib/ContextMenu.mjs.map +1 -0
  7. package/lib/Dialog.mjs +232 -0
  8. package/lib/Dialog.mjs.map +1 -0
  9. package/lib/Draggable.mjs +880 -0
  10. package/lib/Draggable.mjs.map +1 -0
  11. package/lib/ErrorBoundary.mjs +108 -0
  12. package/lib/ErrorBoundary.mjs.map +1 -0
  13. package/lib/FillLastLineResizer.mjs +238 -0
  14. package/lib/FillLastLineResizer.mjs.map +1 -0
  15. package/lib/Flip.mjs +604 -0
  16. package/lib/Flip.mjs.map +1 -0
  17. package/lib/FlipList.mjs +186 -0
  18. package/lib/FlipList.mjs.map +1 -0
  19. package/lib/FormatDate.mjs +25 -0
  20. package/lib/FormatDate.mjs.map +1 -0
  21. package/lib/Icon.mjs +50 -0
  22. package/lib/Icon.mjs.map +1 -0
  23. package/lib/Image.mjs +41 -0
  24. package/lib/Image.mjs.map +1 -0
  25. package/lib/Link.mjs +116 -0
  26. package/lib/Link.mjs.map +1 -0
  27. package/lib/Loading.mjs +10 -0
  28. package/lib/Loading.mjs.map +1 -0
  29. package/lib/LoadingDiv.mjs +83 -0
  30. package/lib/LoadingDiv.mjs.map +1 -0
  31. package/lib/Notifications.mjs +63 -0
  32. package/lib/Notifications.mjs.map +1 -0
  33. package/lib/PageLoader.mjs +42 -0
  34. package/lib/PageLoader.mjs.map +1 -0
  35. package/lib/PersistentNotifications.mjs +130 -0
  36. package/lib/PersistentNotifications.mjs.map +1 -0
  37. package/lib/Popup.mjs +49 -0
  38. package/lib/Popup.mjs.map +1 -0
  39. package/lib/SplitColumnsContainer.mjs +43 -0
  40. package/lib/SplitColumnsContainer.mjs.map +1 -0
  41. package/lib/SwitchDiv.mjs +61 -0
  42. package/lib/SwitchDiv.mjs.map +1 -0
  43. package/lib/TextButton.mjs +41 -0
  44. package/lib/TextButton.mjs.map +1 -0
  45. package/lib/ToastNotifications.mjs +215 -0
  46. package/lib/ToastNotifications.mjs.map +1 -0
  47. package/lib/Tooltip.mjs +160 -0
  48. package/lib/Tooltip.mjs.map +1 -0
  49. package/lib/TransitionDiv.mjs +56 -0
  50. package/lib/TransitionDiv.mjs.map +1 -0
  51. package/lib/WarningDialog.mjs +84 -0
  52. package/lib/WarningDialog.mjs.map +1 -0
  53. package/lib/alt/buttons/OnClickLoadingButton.mjs +36 -0
  54. package/lib/alt/buttons/OnClickLoadingButton.mjs.map +1 -0
  55. package/lib/alt/dialogs/DialogWithImageHeader.mjs +14 -0
  56. package/lib/alt/dialogs/DialogWithImageHeader.mjs.map +1 -0
  57. package/lib/alt/dialogs/DialogWithSidebar.mjs +106 -0
  58. package/lib/alt/dialogs/DialogWithSidebar.mjs.map +1 -0
  59. package/lib/alt/dialogs/MobileBubbleDialog.mjs +49 -0
  60. package/lib/alt/dialogs/MobileBubbleDialog.mjs.map +1 -0
  61. package/lib/containers/DialogContainer.mjs +413 -0
  62. package/lib/containers/DialogContainer.mjs.map +1 -0
  63. package/lib/containers/Reposition.mjs +35 -0
  64. package/lib/containers/Reposition.mjs.map +1 -0
  65. package/lib/containers/usePositioning.mjs +294 -0
  66. package/lib/containers/usePositioning.mjs.map +1 -0
  67. package/lib/index.cjs +5000 -0
  68. package/lib/index.cjs.map +1 -0
  69. package/lib/index.mjs +37 -0
  70. package/lib/index.mjs.map +1 -0
  71. package/lib/useDialogWithToggle.mjs +18 -0
  72. package/lib/useDialogWithToggle.mjs.map +1 -0
  73. package/lib/willUseIsSticky.mjs +123 -0
  74. package/lib/willUseIsSticky.mjs.map +1 -0
  75. package/package.json +47 -30
  76. package/src/BasicBulletList.jsx +21 -0
  77. package/src/Button.jsx +288 -0
  78. package/src/ContextMenu.jsx +621 -0
  79. package/src/Dialog.jsx +413 -0
  80. package/src/Draggable.jsx +801 -0
  81. package/src/ErrorBoundary.jsx +75 -0
  82. package/src/FillLastLineResizer.jsx +226 -0
  83. package/src/Flip.jsx +560 -0
  84. package/src/FlipList.jsx +136 -0
  85. package/src/FormatDate.jsx +9 -0
  86. package/src/Icon.jsx +49 -0
  87. package/src/Image.jsx +23 -0
  88. package/src/Link.jsx +68 -0
  89. package/src/Loading.jsx +29 -0
  90. package/src/LoadingDiv.jsx +75 -0
  91. package/src/Notifications.jsx +89 -0
  92. package/src/PageLoader.jsx +48 -0
  93. package/src/PersistentNotifications.jsx +182 -0
  94. package/src/Popup.jsx +49 -0
  95. package/src/SplitColumnsContainer.jsx +63 -0
  96. package/src/SwitchDiv.jsx +30 -0
  97. package/src/TextButton.jsx +34 -0
  98. package/src/ToastNotifications.jsx +298 -0
  99. package/src/Tooltip.jsx +158 -0
  100. package/src/TransitionDiv.jsx +34 -0
  101. package/src/WarningDialog.jsx +70 -0
  102. package/src/alt/buttons/OnClickLoadingButton.jsx +16 -0
  103. package/src/alt/dialogs/DialogWithImageHeader.jsx +42 -0
  104. package/src/alt/dialogs/DialogWithSidebar.jsx +135 -0
  105. package/src/alt/dialogs/MobileBubbleDialog.jsx +56 -0
  106. package/src/containers/DialogContainer.jsx +423 -0
  107. package/src/containers/Reposition.jsx +24 -0
  108. package/src/containers/usePositioning.jsx +305 -0
  109. package/src/index.js +35 -0
  110. package/src/useDialogWithToggle.jsx +12 -0
  111. package/src/willUseIsSticky.jsx +113 -0
  112. package/.babelrc +0 -27
  113. package/lib/index.js +0 -5429
  114. package/rollup.config.js +0 -52
  115. package/yarn-error.log +0 -3724
@@ -0,0 +1,135 @@
1
+ import React, {useRef} from 'react'
2
+ import styled from 'styled-components'
3
+ import {media, callIfFunction, getColor, twoWayAnimation, oneWayAnimation} from '@startlibs/utils'
4
+ import {useToggle, usePopupToggle, useLazyConstant} from '@startlibs/core'
5
+ import {CloseIcon, Dialog} from '../../Dialog'
6
+ import {willUseFlipSwitch} from '../../Flip'
7
+ import {Loading} from '../../Loading'
8
+ import {DialogContainer} from '../../containers/DialogContainer'
9
+
10
+ export const SideMenu = styled.div`
11
+ background-color: ${getColor('lightBluishGray')};
12
+ border-top-left-radius: 18px;
13
+ border-bottom-left-radius: 18px;
14
+ width: 19rem;
15
+ flex-shrink: 0;
16
+ padding: 1.25rem 0;
17
+ ${props => !props.visibleOnDesktop && 'display:none;'}
18
+ ${media.mobile`
19
+ display:block;
20
+ position:fixed;
21
+ top:0;
22
+ left:0;
23
+ bottom:0;
24
+ z-index:1000;
25
+ transition: 0.25s transform ease-out;
26
+ background-color: ${getColor('gray240')};
27
+ box-shadow: -5px 0 20px 5px rgba(0,0,0,0.4);
28
+ border-radius: 0;
29
+ ${props => !props.isOpen && `
30
+ transform: translateX(-100%);
31
+ box-shadow: none;
32
+ `}
33
+ `}
34
+ `
35
+ export const DialogTabMenu = styled.ul`
36
+ cursor: default;
37
+ overflow: auto;
38
+ text-align: left;
39
+ color: inherit;
40
+ user-select: none;
41
+ li {
42
+ position: relative;
43
+ padding: 1rem 2rem 1rem 4.1rem;
44
+ font-size: 14px;
45
+ ${media.mobile`
46
+ font-size: 16px;
47
+ `}
48
+ :hover {
49
+ background-color: ${getColor('bluishGray')};
50
+ cursor: pointer;
51
+ }
52
+ &.selected {
53
+ color: ${getColor('main')};
54
+ background: rgba(0, 132, 214, 0.125);
55
+ pointer-events: none;
56
+ }
57
+ }
58
+ .icon{
59
+ position: absolute;
60
+ left: 1rem;
61
+ top: 50%;
62
+ transform: translateY(-50%);
63
+ width: 3rem;
64
+ text-align: center;
65
+ line-height: 0;
66
+ font-size: 26px;
67
+ }
68
+ ${props => props.menuTabIsOpen && `
69
+ li {
70
+ padding: 1.25rem 2rem 1.25rem 3.25rem;
71
+ border-radius: 6px
72
+ }
73
+ .icon{
74
+ left: 0;
75
+ }
76
+ `}
77
+ `
78
+
79
+ const [fadeIn, relativeFadeout] = twoWayAnimation('opacity: 0;','opacity: 0;', 'opacity: 1;')
80
+ const absoluteOnLeave = oneWayAnimation('position: absolute;', 'position: absolute;')
81
+ const fadeOut = [absoluteOnLeave,relativeFadeout]
82
+
83
+ export const MENU_TAB = '$menu$'
84
+
85
+ // ponytail: sem footer — padrão do dialog com barra lateral; fecha pelo X ou por fora
86
+ export const DialogWithSidebarPresentation = ({className, tabIsOpen, menuIsOpen, sidebar, Content, openTab, children, title, ...props}) => {
87
+ const tab = useToggle(tabIsOpen)
88
+ const sideMenu = usePopupToggle(menuIsOpen)
89
+ const ref = useRef()
90
+ const switchAnimation = useLazyConstant(willUseFlipSwitch)
91
+
92
+ const menuTabIsOpen = tab.isOpen === MENU_TAB
93
+
94
+ return <DialogContainer {...props} ref={ref}>{({confirmCloseDialog}) =>
95
+ <div className={className + " JS-has-wc"} ref={ref}>
96
+ {
97
+ props.closeDialog &&
98
+ <CloseIcon onClick={confirmCloseDialog}/>
99
+ }
100
+ <SideMenu isOpen={sideMenu.isOpen} visibleOnDesktop>
101
+ {sidebar(tab)}
102
+ </SideMenu>
103
+
104
+ <div className={(menuTabIsOpen ? "menuTabIsOpen " : "") + "main-container"}>
105
+ <h2>{
106
+ menuTabIsOpen ? "Configurações gerais" : <><span className="icon icon-menu mobile-only" onClick={sideMenu.open}/>
107
+ {title(tab)}</>}</h2>
108
+ <React.Suspense fallback={<TabContent key="loading" switchAnimation={switchAnimation} tab={tab} children={<Loading absolute/>} />}>
109
+ <TabContent key={tab.isOpen} switchAnimation={switchAnimation} tab={tab} Content={Content} children={children} />
110
+ </React.Suspense>
111
+ </div>
112
+ </div>
113
+ }</DialogContainer>
114
+ }
115
+
116
+ const TabContent = ({Content,children,tab,switchAnimation}) => {
117
+ const contentRef = useRef()
118
+ switchAnimation(contentRef,fadeIn,fadeOut)
119
+
120
+ return <div ref={contentRef} className="dialog-content">
121
+ {callIfFunction(Content,tab)}
122
+ {callIfFunction(children,tab)}
123
+ </div>
124
+ }
125
+
126
+ export const DialogWithSidebar = styled((props) => <Dialog fullscreenOnMobile as={DialogWithSidebarPresentation} {...props} />)`
127
+ max-width: 59rem;
128
+ display: flex;
129
+ padding: 0;
130
+ flex-direction: row;
131
+ ${media.mobile`
132
+ min-height: 100%;
133
+ overflow: hidden;
134
+ `}
135
+ `
@@ -0,0 +1,56 @@
1
+ import React, {useRef} from 'react'
2
+
3
+ import {callIfFunction, getColor} from '@startlibs/utils'
4
+ import styled from 'styled-components'
5
+
6
+ import {DialogErrorContent} from '../../Dialog'
7
+ import {DialogContainer} from '../../containers/DialogContainer'
8
+
9
+ const MobileBubbleDialogFooter = styled.div `
10
+ background-color: ${getColor('gray240')};
11
+ min-height: 5rem;
12
+ color: ${getColor('main')};
13
+ font-size: 13px;
14
+ font-weight: 500;
15
+ text-transform: uppercase;
16
+ padding: 1rem;
17
+ user-select: none;
18
+ display: flex;
19
+ align-items: center;
20
+ justify-content: center;
21
+ cursor: pointer;
22
+ border-bottom-left-radius: 10px;
23
+ border-bottom-right-radius: 10px;
24
+ :active {
25
+ background: rgb(232,232,232);
26
+ }
27
+ `
28
+
29
+ export const MobileBubbleDialog = styled(({className, footer, Content, children, title, illustration, icon, auxiliarActions, ...props}) => {
30
+ const ref = useRef()
31
+ return <DialogContainer {...props} closeOnClick ref={ref} DialogErrorContent={DialogErrorContent} title={title}>{({confirmCloseDialog}) =>
32
+ <>
33
+ <div className={className} ref={ref}>
34
+ {
35
+ children && callIfFunction(children)
36
+ }
37
+ <MobileBubbleDialogFooter onClick={confirmCloseDialog}>
38
+ Fechar
39
+ </MobileBubbleDialogFooter>
40
+ </div>
41
+ </>
42
+ }</DialogContainer>
43
+ })`
44
+ border-radius: 10px;
45
+ background-color: white;
46
+ width: 100%;
47
+ margin: auto;
48
+ position: relative;
49
+ align-self: baseline;
50
+ overflow: hidden;
51
+ @supports (flex-wrap: wrap) {
52
+ display: flex;
53
+ flex-direction: column;
54
+ }
55
+ `
56
+
@@ -0,0 +1,423 @@
1
+ import React, {forwardRef, useEffect, useRef} from 'react'
2
+ import styled from 'styled-components'
3
+ import _ from 'lodash/fp'
4
+ import {Fill, Slot, useEnsureRef, useRefState, willUseLayers} from '@startlibs/core'
5
+ import {callIfFunction, identityOpacityFrames, media, oneWayAnimation, twoWayAnimation} from '@startlibs/utils'
6
+ import {ErrorBoundary} from '../ErrorBoundary'
7
+ import {useFlipAnimation} from '../Flip'
8
+
9
+ export const dialogFadeInDown = oneWayAnimation('opacity: 0; transform: translateY(50px);', 'opacity: 1; transform: translateY(0);')
10
+ const [dialogModalIn, dialogModalOut] = twoWayAnimation('opacity: 0;', 'opacity: 1;')
11
+ export {dialogModalIn, dialogModalOut}
12
+
13
+ export const Modal = styled.div`
14
+ position: fixed;
15
+ top:0;
16
+ bottom:0;
17
+ left:0;
18
+ right:0;
19
+ will-change: opacity;
20
+ background-color: rgba(0,0,0,0.7);
21
+ ${props => props.$blurBackdrop && `backdrop-filter: blur(10px);`}
22
+ `
23
+
24
+ export const ModalContainer = styled.div`
25
+ position: fixed;
26
+ top:0;
27
+ bottom:0;
28
+ left:0;
29
+ right:0;
30
+ /* Barra invisível mantendo a LARGURA NATIVA da calha (scrollbar-color não
31
+ colapsa a largura como scrollbar-width:none / ::-webkit-scrollbar faziam).
32
+ Essa calha compensa a remoção da scrollbar do body ao abrir a dialog (body
33
+ vira overflow:hidden e o viewport alarga ~15px no fim da animação); sem ela,
34
+ a dialog centralizada dá um pulinho lateral. Aqui: sem barra visível e sem salto. */
35
+ scrollbar-color: transparent transparent;
36
+ `
37
+
38
+ const DialogCenter = styled.div`
39
+ @supports (position:sticky) {
40
+ will-change: transform, opacity;
41
+ &.JS-wc-off {
42
+ will-change: auto;
43
+ }
44
+ }
45
+ display: -webkit-box;
46
+ display: -ms-flexbox;
47
+ display: flex;
48
+ overflow-y: ${props => props.withScroll ? "scroll" : "hidden"};
49
+ overflow-x:hidden;
50
+ /* idem ModalContainer: calha nativa reservada (sem salto), track invisível — mas o
51
+ polegar é visível, senão dialog mais alta que a tela rola sem nenhuma pista.
52
+ Claro porque a calha fica sobre o backdrop escuro. Sem overflow, o Chromium não
53
+ desenha polegar → não sobra track fantasma. */
54
+ scrollbar-color: rgba(255,255,255,0.6) transparent;
55
+ position:fixed;
56
+ top:0;
57
+ bottom:0;
58
+ left:0;
59
+ right:0;
60
+ align-items: center;
61
+ ${media.desktop`
62
+ padding: ${props => props.fullscreen ? '1rem' : '3rem 0.5rem;'};
63
+ `}
64
+ ${media.mobile`
65
+ padding: ${props => props.fullscreenOnMobile ? '0' : '0.5rem'};
66
+ `}
67
+ `
68
+
69
+ const PopupSlot = styled(Slot)`
70
+ position: absolute;
71
+ left:0;
72
+ top: 0;
73
+ width: 100%;
74
+ `
75
+ const RelativeSlot = styled(Slot)`
76
+ width:100%;
77
+ position: absolute;
78
+ `
79
+ const editableSelector = 'textarea,input,[contenteditable]'
80
+ const focusableSelector = 'button,a,'+editableSelector
81
+
82
+ const shouldPreventResolvers = []
83
+ let shouldPreventDialogPop = Promise.resolve()
84
+ export const preventDialogPop = (cb) => {
85
+ shouldPreventDialogPop = shouldPreventDialogPop.then(() => {
86
+ new Promise((res) => {
87
+ shouldPreventResolvers.push(res)
88
+ cb()
89
+ })
90
+ })
91
+ }
92
+ export const waitForPop = () => {
93
+ return shouldPreventDialogPop
94
+ }
95
+ const preventDialogPopState = (e) => {
96
+ const resolver = shouldPreventResolvers.shift()
97
+ if (resolver) {
98
+ e.stopImmediatePropagation()
99
+ resolver()
100
+ }
101
+ }
102
+
103
+ window.addEventListener("popstate",preventDialogPopState)
104
+
105
+ const fixPosition = (query, fn) => {
106
+ const elements = document.querySelectorAll(query)
107
+ if (elements.length) {
108
+ Array.prototype.slice.call(elements).forEach(fn)
109
+ }
110
+ }
111
+
112
+ let openLayers = 0
113
+ let fakedScrollTop = 0
114
+ export const isFakeScrolled = () => openLayers>0 && fakedScrollTop > 0
115
+ export const removeWindowScroll = (opening, keepDisabledScroll) => {
116
+ if (opening) {
117
+ openLayers = Math.max(openLayers+1,1)
118
+ if (openLayers>1) {
119
+ return
120
+ }
121
+ } else {
122
+ openLayers = Math.min(openLayers-1,0)
123
+ if (openLayers<0) {
124
+ return
125
+ }
126
+ }
127
+ const finishReset = (marginRight, scrollTop) => {
128
+ // body.style.paddingRight = marginRight + 'px'
129
+ window.scroll(window.scrollLeft || window.scrollX || 0, scrollTop)
130
+ if (keepDisabledScroll) { return }
131
+ fixPosition('.navbar,.JS-fix-dialog-scroll', n => {
132
+ if (opening) {
133
+ const {right} = window.getComputedStyle(n)
134
+ n.style.right = (parseInt(right) + marginRight) + 'px'
135
+ } else {
136
+ n.style.right = null
137
+ }
138
+ })
139
+ fixPosition('.JS-fix-dialog-scroll-transform', n => {
140
+ if (marginRight) {
141
+ n.style.transform = window.getComputedStyle(n).transform + ` translateX(-${marginRight / 2}px)`
142
+ } else {
143
+ n.style.transform = null
144
+ }
145
+ })
146
+
147
+ fixPosition('.JS-on-open-dialog', n => {
148
+ if (opening) {
149
+ n.classList.add('JS-dialog-open')
150
+ } else {
151
+ n.classList.remove('JS-dialog-open')
152
+ }
153
+ })
154
+ window.requestAnimationFrame(()=>window.dispatchEvent(new CustomEvent('resize', {detail: {dialog: true}})))
155
+ }
156
+
157
+ const body = document.body
158
+ let bodyWidth = body.offsetWidth
159
+ if (opening) {
160
+ const scrollTop = window.pageYOffset
161
+ fakedScrollTop = scrollTop
162
+ if (keepDisabledScroll) {
163
+ body.style.overflowY = 'scroll'
164
+ } else {
165
+ body.style.overflow = 'hidden'
166
+ }
167
+ const marginRight = (body.offsetWidth - bodyWidth)
168
+
169
+ body.style.position = 'fixed'
170
+ body.style.top = -scrollTop + "px"
171
+ body.style.left = '0'
172
+ if (!keepDisabledScroll) {
173
+ body.style.right = marginRight + 'px'
174
+ } else {
175
+ body.style.right = '0'
176
+ }
177
+
178
+ finishReset(marginRight,scrollTop)
179
+ } else {
180
+ fakedScrollTop = 0
181
+ // setTimeout(() => {
182
+ const scrollTop = parseInt(body.style.top)
183
+ body.style.overflow = ''
184
+
185
+ body.style.position = ''
186
+ body.style.top = ''
187
+ body.style.left = ''
188
+ body.style.right = ''
189
+ finishReset(0,-scrollTop)
190
+ // },250)
191
+ }
192
+ }
193
+
194
+ const {href: originalHref, hash: originalHash} = window.location
195
+
196
+ const [useDialogLayers, getDialogLayersSize] = willUseLayers(React.useLayoutEffect)
197
+ export {useDialogLayers, getDialogLayersSize}
198
+
199
+ export const DialogContainer = forwardRef((
200
+ {
201
+ children, title, closeDialog, closeOnClick, dontRefocus, onConfirm, prev, next, form, noFormWrap, withFormWrap, placement = 'Dialog',
202
+ DialogErrorContent, customHref, animation = dialogFadeInDown, animationTiming, animationLeaveEffect, animationEnterEffect, skipKeys,fullscreenOnMobile, fullscreen, confirmChanges = false, skipDom, modalRef, skipCheckFocus,
203
+ autoComplete, blurBackdrop
204
+ },
205
+ forwardedRef) => {
206
+
207
+ const dialogRef = useEnsureRef(forwardedRef)
208
+ const prevActiveElement = useRefState(document.activeElement)
209
+ const modal = useEnsureRef(modalRef)
210
+ const modalScroll = useRef()
211
+ const mouseDownInModal = useRefState()
212
+ const previousHash = useRefState(window.location.hash)
213
+
214
+ const [layerIndex, {isTop, getSize}] = useDialogLayers({
215
+ onFirst: () => {
216
+ waitForPop().then(() => {
217
+ if (customHref && encodeURI(customHref) === window.location.hash) {
218
+ window.history.replaceState(history.state, '', window.location.pathname)
219
+ window.history.pushState({dialog: true}, '', customHref)
220
+ } else {
221
+ window.history.pushState({dialog: true}, '', window.location.href + (customHref || ""))
222
+ }
223
+ })
224
+ // clearPreventDialogPop()
225
+ if (document.body.scrollHeight === document.body.clientHeight) {
226
+ modal.current.style.overflowY = "scroll"
227
+ if (modalScroll.current) {
228
+ modalScroll.current.style.overflowY = "scroll"
229
+ }
230
+ }
231
+ // resetNavbar(true)
232
+ },
233
+ onRemove: (layers) => {
234
+ if (
235
+ (layers === 0 && _.get('dialog', window.history.state) === true) ||
236
+ (customHref && window.location.hash === encodeURI(customHref))
237
+ ) {
238
+ preventDialogPop(() => {
239
+ window.history.back()
240
+ })
241
+ }
242
+ },
243
+ onEmpty: () => {
244
+ if (document.body.scrollHeight > document.body.clientHeight) {
245
+ if (modalScroll.current) {
246
+ modalScroll.current.style.overflowY = "hidden"
247
+ }
248
+ } else if (modal.current) {
249
+ modal.current.style.overflowY = "scroll"
250
+ }
251
+ removeWindowScroll(false)
252
+ }
253
+ })
254
+
255
+ const confirmCloseDialog = () => {
256
+ if (form && form.getHasChanged() && confirmChanges) {
257
+ form.confirm()
258
+ .then(closeDialog)
259
+ } else {
260
+ callIfFunction(closeDialog)
261
+ }
262
+ }
263
+ /*
264
+ useEffect(() => () => {
265
+ setTimeout(() => {
266
+ if (window.location.hash === previousHash.get() && _.get('dialog', window.history.state) === true) {
267
+ console.log("???")
268
+ }
269
+ }, 100)
270
+ }, [])*/
271
+
272
+ const pastFirstTime = React.useRef()
273
+
274
+
275
+ useEffect(() => {
276
+ if (customHref && window.location.hash !== customHref) {
277
+ // if (pastFirstTime.current) {
278
+ waitForPop().then(() => {
279
+ if (window.location.hash) {
280
+ window.history.replaceState({dialog: true}, '', customHref)
281
+ } else {
282
+ window.history.pushState({dialog: true}, '', customHref)
283
+ }
284
+ })
285
+ // } else {
286
+ // window.history.pushState({dialog: true}, '', customHref)
287
+ // pastFirstTime.current = true
288
+ // }
289
+ previousHash.set(encodeURI(customHref))
290
+ }
291
+ }, [customHref])
292
+
293
+ useEffect(() => {
294
+ window.addEventListener('keydown', checkKey)
295
+ window.addEventListener('focusin', checkFocus)
296
+ window.addEventListener('popstate', checkHistory)
297
+ return () => {
298
+ window.removeEventListener('keydown', checkKey)
299
+ window.removeEventListener('focusin', checkFocus)
300
+ window.removeEventListener('popstate', checkHistory)
301
+ if (!dontRefocus && document.body.contains(prevActiveElement.get()) && prevActiveElement.get().matches(focusableSelector)) {
302
+ setTimeout(() => prevActiveElement.get().focus(), 0)
303
+ }
304
+ }
305
+ }, [])
306
+
307
+
308
+ const checkHistory = (e) => {
309
+ if (!isTop()/* || (previousHash.get() !== window.location.hash && !customHref)*/) return
310
+ if (_.get('willOpenDialog', window.history.state) === true) { return }
311
+ if (_.get('dialog', window.history.state) !== true) {
312
+ window.history.pushState({dialog: true}, '', window.location.href)
313
+ }
314
+ confirmCloseDialog()
315
+ }
316
+
317
+ const checkFocus = (e) => {
318
+ if (!isTop() || skipCheckFocus) return
319
+ if (modal.current && !e.target.closest('.' + modal.current.className.split(' ').join('.'))) {
320
+ const nextTab = modal.current.querySelector('[tabindex]')
321
+ if (nextTab)
322
+ nextTab.focus()
323
+ }
324
+ }
325
+
326
+ const checkKey = (e) => {
327
+ if (!isTop() || skipKeys || e.target?.className?.indexOf("JS-dialog-skip-evts")>=0 || e.target?.closest(".JS-dialog-skip-evts")) return
328
+ if (modal.current && e.shiftKey && e.keyCode === 9 && modal.current.querySelector('[tabindex]') === document.activeElement) {
329
+ e.preventDefault()
330
+ const prevTab = [].slice.call(modal.current.querySelectorAll('[tabindex]')).reduce((acc, el) => el.tabIndex >= acc.tabIndex ? el : acc, {tabIndex: -1})
331
+ if (prevTab && prevTab.focus)
332
+ prevTab.focus()
333
+ }
334
+ if (e.key === 'Escape') {
335
+ e.preventDefault()
336
+ confirmCloseDialog()
337
+ } else if (e.key === 'Enter') {
338
+ if (e.target) {
339
+ const target = e.target.closest(focusableSelector + ',div')
340
+ if (target && (target.tagName.toLowerCase() !== 'input' || target.form || target.type.toLowerCase() === 'file') && target.matches(focusableSelector))
341
+ return
342
+ }
343
+ e.preventDefault()
344
+ e.stopPropagation()
345
+ if (onConfirm) {
346
+ onConfirm()
347
+ } else if (form) {
348
+ form.submitForm()
349
+ }
350
+ } else if (!e.target.matches(editableSelector)) {
351
+ if (e.key === 'ArrowLeft' && prev) {
352
+ prev()
353
+ } else if (e.key === 'ArrowRight' && next) {
354
+ next()
355
+ }
356
+ }
357
+ }
358
+
359
+ const checkClick = (e) => {
360
+ if (closeOnClick && e.target === e.currentTarget && mouseDownInModal.get()) {
361
+ confirmCloseDialog()
362
+ }
363
+ mouseDownInModal.set(false)
364
+ }
365
+ const mouseDown = (e) => {
366
+ mouseDownInModal.set(e.target === e.currentTarget)
367
+ }
368
+
369
+ const usingForm = withFormWrap || (form && !noFormWrap)
370
+
371
+ useFlipAnimation(dialogRef, animation, true, {
372
+ nestedFlip: !skipDom,
373
+ onEnterEnd: () => {
374
+ if (getSize() === 1) {
375
+ removeWindowScroll(true);
376
+ }
377
+ if (document.body.scrollHeight === document.body.clientHeight) {
378
+ modal.current.style.overflowY = "none"
379
+ }
380
+ if (modalScroll.current) {
381
+ modalScroll.current.style.overflowY = "scroll"
382
+ }
383
+ },
384
+ leaveEffect:animationLeaveEffect,
385
+ enterEffect:animationEnterEffect,
386
+ timing:animationTiming
387
+ })
388
+ useFlipAnimation(modal, !skipDom && identityOpacityFrames, true,{timing:animationTiming})
389
+
390
+ const content = <>
391
+ <PopupSlot name={'Popup-' + layerIndex}/>
392
+ <ErrorBoundary title={callIfFunction(title)} closeDialog={closeDialog} Error={DialogErrorContent}>
393
+ {callIfFunction(children, {confirmCloseDialog}, dialogRef, fullscreenOnMobile)}
394
+ </ErrorBoundary>
395
+ </>
396
+
397
+ return (
398
+ <Fill name={placement}>
399
+ {
400
+ skipDom
401
+ ? content
402
+ : <>
403
+ <AnimatedModal timing={animationTiming} blurBackdrop={blurBackdrop}/>
404
+ <ModalContainer ref={modal}>
405
+
406
+ <DialogCenter autoComplete={autoComplete} className="JS-has-wc" withScroll={getSize()>1} fullscreen={fullscreen} fullscreenOnMobile={fullscreenOnMobile} ref={modalScroll} as={usingForm ? 'form' : undefined} onClick={checkClick}
407
+ onSubmit={usingForm ? () => {} : undefined}
408
+ onMouseDown={mouseDown}>
409
+ {content}
410
+ </DialogCenter>
411
+ </ModalContainer>
412
+ </>
413
+ }
414
+ </Fill>
415
+ )
416
+ })
417
+
418
+ export const AnimatedModal = React.forwardRef(({timing,nestedFlip,enterEffect,leaveEffect,modalIn = dialogModalIn, modalOut = dialogModalOut, blurBackdrop,...props},ref) => {
419
+ const modal = useEnsureRef(ref)
420
+ useFlipAnimation(modal, modalIn, modalOut,{timing,nestedFlip,leaveEffect,enterEffect})
421
+ return <Modal {...props} $blurBackdrop={blurBackdrop} ref={modal}/>
422
+ })
423
+
@@ -0,0 +1,24 @@
1
+ import React, {useMemo} from 'react'
2
+ import {Fill, useEnsureRef} from '@startlibs/core'
3
+ import {usePositioning} from './usePositioning'
4
+ import styled from 'styled-components'
5
+ import {getDialogLayersSize} from './DialogContainer'
6
+
7
+ const AbsoluteContainer = styled.div`
8
+ position: absolute;
9
+ `
10
+
11
+ export const Reposition = React.forwardRef(({slot = 'Popup', className, onClick, children, ...rest},ref) => {
12
+ const popupRef = useEnsureRef(ref)
13
+ const [positionInfo, placeholder] = usePositioning(popupRef, rest)
14
+
15
+ const slotName = useMemo(() => getDialogLayersSize() === 0 ? slot : (slot + '-' + (getDialogLayersSize() - 1)), [])
16
+ return <>
17
+ {placeholder}
18
+ <Fill name={slotName}>
19
+ <AbsoluteContainer {...positionInfo} onClick={onClick} className={className} ref={popupRef}>
20
+ {children}
21
+ </AbsoluteContainer>
22
+ </Fill>
23
+ </>
24
+ })