@rpcbase/client 0.210.0 → 0.211.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.
- package/package.json +1 -1
- package/ui/ExpandableFloatView/index.tsx +123 -0
- package/ui/Modal/Modal.js +72 -76
- package/ui/Modal/ModalForm/AlertBanner.js +3 -4
- package/ui/Modal/ModalForm/index.js +146 -151
- package/ui/View/index.tsx +17 -0
- package/ui/View/index.web.js +35 -32
- package/ui/helpers/withSuspense/index.js +4 -5
- package/ui/ExpandableFloatView/index.js +0 -120
- package/ui/View/index.js +0 -12
package/package.json
CHANGED
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import {motion} from "framer-motion"
|
|
2
|
+
import {useRef, useState, useImperativeHandle} from "react"
|
|
3
|
+
|
|
4
|
+
import {SPRING_DEFAULT} from "../springs"
|
|
5
|
+
import useBackdrop from "./useBackdrop"
|
|
6
|
+
|
|
7
|
+
import "./exp.scss"
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
export const ExpandableFloatView = ({
|
|
11
|
+
ref,
|
|
12
|
+
renderContent,
|
|
13
|
+
containerRef,
|
|
14
|
+
onGetExpandedRect,
|
|
15
|
+
targetRef,
|
|
16
|
+
...props
|
|
17
|
+
}) => {
|
|
18
|
+
const parentRef = useRef(null)
|
|
19
|
+
const wrapperRef = useRef(null)
|
|
20
|
+
|
|
21
|
+
const isOpen = useRef(false)
|
|
22
|
+
const openedFromRect = useRef(null)
|
|
23
|
+
|
|
24
|
+
const [animatedVals, setAnimatedVals] = useState({})
|
|
25
|
+
|
|
26
|
+
// eslint-disable-next-line no-use-before-define
|
|
27
|
+
const {showBackdrop} = useBackdrop(isOpen, onToggle)
|
|
28
|
+
|
|
29
|
+
const getRect = () => {
|
|
30
|
+
const boundingRect = containerRef.current.getBoundingClientRect()
|
|
31
|
+
const rect = {
|
|
32
|
+
left: `${boundingRect.x}px`,
|
|
33
|
+
top: `${boundingRect.top}px`,
|
|
34
|
+
right: `${window.innerWidth - boundingRect.right}px`,
|
|
35
|
+
bottom: `${window.innerHeight - boundingRect.bottom}px`,
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
return rect
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const onOpen = () => {
|
|
42
|
+
showBackdrop()
|
|
43
|
+
|
|
44
|
+
isOpen.current = true
|
|
45
|
+
|
|
46
|
+
const expandedRect = onGetExpandedRect()
|
|
47
|
+
|
|
48
|
+
requestAnimationFrame(() => {
|
|
49
|
+
// set the wrapper ref to current + fixed
|
|
50
|
+
const rect = getRect()
|
|
51
|
+
openedFromRect.current = rect
|
|
52
|
+
|
|
53
|
+
const newStyle = {
|
|
54
|
+
position: "fixed",
|
|
55
|
+
top: rect.top,
|
|
56
|
+
right: rect.right,
|
|
57
|
+
bottom: rect.bottom,
|
|
58
|
+
left: rect.left,
|
|
59
|
+
zIndex: 1000,
|
|
60
|
+
}
|
|
61
|
+
Object.keys(newStyle).forEach((k) => {
|
|
62
|
+
wrapperRef.current.style[k] = newStyle[k]
|
|
63
|
+
})
|
|
64
|
+
|
|
65
|
+
document.body.appendChild(wrapperRef.current)
|
|
66
|
+
|
|
67
|
+
setAnimatedVals(expandedRect)
|
|
68
|
+
})
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const onClose = () => {
|
|
72
|
+
isOpen.current = false
|
|
73
|
+
|
|
74
|
+
const returnToRect = openedFromRect.current
|
|
75
|
+
setAnimatedVals({
|
|
76
|
+
top: returnToRect.top,
|
|
77
|
+
right: returnToRect.right,
|
|
78
|
+
bottom: returnToRect.bottom,
|
|
79
|
+
left: returnToRect.left,
|
|
80
|
+
zIndex: "unset",
|
|
81
|
+
})
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const onToggle = () => {
|
|
85
|
+
if (isOpen.current) {
|
|
86
|
+
onClose()
|
|
87
|
+
} else {
|
|
88
|
+
onOpen()
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
useImperativeHandle(ref, () => {
|
|
93
|
+
return {
|
|
94
|
+
open: () => {
|
|
95
|
+
onOpen()
|
|
96
|
+
},
|
|
97
|
+
toggle: () => {
|
|
98
|
+
onToggle()
|
|
99
|
+
},
|
|
100
|
+
close: () => onClose(),
|
|
101
|
+
}
|
|
102
|
+
})
|
|
103
|
+
|
|
104
|
+
return (
|
|
105
|
+
<>
|
|
106
|
+
<div ref={parentRef}>
|
|
107
|
+
<motion.div
|
|
108
|
+
ref={wrapperRef}
|
|
109
|
+
style={{
|
|
110
|
+
inset: "unset",
|
|
111
|
+
// zIndex: 10000,
|
|
112
|
+
// ...styleProps,
|
|
113
|
+
}}
|
|
114
|
+
transition={SPRING_DEFAULT}
|
|
115
|
+
animate={{...animatedVals}}
|
|
116
|
+
onClick={(e) => e.stopPropagation()}
|
|
117
|
+
>
|
|
118
|
+
{renderContent({})}
|
|
119
|
+
</motion.div>
|
|
120
|
+
</div>
|
|
121
|
+
</>
|
|
122
|
+
)
|
|
123
|
+
}
|
package/ui/Modal/Modal.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/* @flow */
|
|
2
|
-
import {
|
|
2
|
+
import {useEffect, useRef} from "react"
|
|
3
3
|
import _debounce from "lodash/debounce"
|
|
4
4
|
import BSModal from "react-bootstrap/Modal"
|
|
5
5
|
|
|
@@ -9,85 +9,81 @@ import "./modal.scss"
|
|
|
9
9
|
const MAX_RETRIES = 256
|
|
10
10
|
const DELAY_MS = 20
|
|
11
11
|
|
|
12
|
-
const Modal =
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
attemptsCount++
|
|
38
|
-
if (attemptsCount > MAX_RETRIES) {
|
|
39
|
-
throw new Error("unable to initialize after max attempts")
|
|
40
|
-
}
|
|
41
|
-
setTimeout(setup, DELAY_MS)
|
|
42
|
-
return
|
|
12
|
+
const Modal = ({
|
|
13
|
+
ref: _ref,
|
|
14
|
+
show = true,
|
|
15
|
+
dark = false,
|
|
16
|
+
scrollable = true,
|
|
17
|
+
animation = true,
|
|
18
|
+
className = "",
|
|
19
|
+
children,
|
|
20
|
+
...props
|
|
21
|
+
}) => {
|
|
22
|
+
const internalRef = useRef(null)
|
|
23
|
+
const ref = _ref || internalRef
|
|
24
|
+
|
|
25
|
+
useEffect(() => {
|
|
26
|
+
if (!show) return
|
|
27
|
+
|
|
28
|
+
let attemptsCount = 0
|
|
29
|
+
let ro
|
|
30
|
+
|
|
31
|
+
const setup = () => {
|
|
32
|
+
const bodyEl = ref.current?.dialog?.querySelector(".modal-body")
|
|
33
|
+
if (!bodyEl) {
|
|
34
|
+
attemptsCount++
|
|
35
|
+
if (attemptsCount > MAX_RETRIES) {
|
|
36
|
+
throw new Error("unable to initialize after max attempts")
|
|
43
37
|
}
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
const contentEl = ref.current?.dialog?.querySelector(".modal-content")
|
|
47
|
-
// TODO: this shouldn't happen, but it only happens with modalforms... investigate
|
|
48
|
-
// additionally we're eventually going to ditch the react-bootstrap modals and use our own so don't waste so much time on this
|
|
49
|
-
if (!contentEl) return
|
|
50
|
-
|
|
51
|
-
if (bodyEl.scrollHeight > bodyEl.clientHeight) {
|
|
52
|
-
if (!contentEl.classList.contains("has-scroller")) {
|
|
53
|
-
contentEl.classList.add("has-scroller")
|
|
54
|
-
}
|
|
55
|
-
} else {
|
|
56
|
-
if (contentEl.classList.contains("has-scroller")) {
|
|
57
|
-
contentEl.classList.remove("has-scroller")
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
}, DELAY_MS)
|
|
61
|
-
|
|
62
|
-
ro = new ResizeObserver((entries) => {
|
|
63
|
-
checkApplyScroller()
|
|
64
|
-
})
|
|
65
|
-
|
|
66
|
-
ro.observe(bodyEl)
|
|
38
|
+
setTimeout(setup, DELAY_MS)
|
|
39
|
+
return
|
|
67
40
|
}
|
|
68
41
|
|
|
69
|
-
|
|
42
|
+
const checkApplyScroller = _debounce(() => {
|
|
43
|
+
const contentEl = ref.current?.dialog?.querySelector(".modal-content")
|
|
44
|
+
// TODO: this shouldn't happen, but it only happens with modalforms... investigate
|
|
45
|
+
// additionally we're eventually going to ditch the react-bootstrap modals and use our own so don't waste so much time on this
|
|
46
|
+
if (!contentEl) return
|
|
70
47
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
48
|
+
if (bodyEl.scrollHeight > bodyEl.clientHeight) {
|
|
49
|
+
if (!contentEl.classList.contains("has-scroller")) {
|
|
50
|
+
contentEl.classList.add("has-scroller")
|
|
51
|
+
}
|
|
52
|
+
} else {
|
|
53
|
+
if (contentEl.classList.contains("has-scroller")) {
|
|
54
|
+
contentEl.classList.remove("has-scroller")
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}, DELAY_MS)
|
|
58
|
+
|
|
59
|
+
ro = new ResizeObserver((entries) => {
|
|
60
|
+
checkApplyScroller()
|
|
61
|
+
})
|
|
62
|
+
|
|
63
|
+
ro.observe(bodyEl)
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
setup()
|
|
67
|
+
|
|
68
|
+
return () => {
|
|
69
|
+
ro?.disconnect()
|
|
70
|
+
}
|
|
71
|
+
}, [show])
|
|
72
|
+
|
|
73
|
+
return (
|
|
74
|
+
<BSModal
|
|
75
|
+
className={cx({"is-dark": dark}, className)}
|
|
76
|
+
centered={true}
|
|
77
|
+
scrollable={scrollable}
|
|
78
|
+
animation={animation}
|
|
79
|
+
ref={ref}
|
|
80
|
+
show={show}
|
|
81
|
+
{...props}
|
|
82
|
+
>
|
|
83
|
+
{children}
|
|
84
|
+
</BSModal>
|
|
85
|
+
)
|
|
86
|
+
}
|
|
91
87
|
|
|
92
88
|
Modal.Dialog = BSModal.Dialog
|
|
93
89
|
Modal.Header = BSModal.Header
|
|
@@ -1,10 +1,9 @@
|
|
|
1
|
-
|
|
2
|
-
import {forwardRef, useImperativeHandle, useState, useEffect} from "react"
|
|
1
|
+
import {useImperativeHandle, useState, useEffect} from "react"
|
|
3
2
|
import {useFormContext} from "react-hook-form"
|
|
4
3
|
import Alert from "react-bootstrap/Alert"
|
|
5
4
|
|
|
6
5
|
|
|
7
|
-
const AlertBanner =
|
|
6
|
+
const AlertBanner = ({ref, modalRef, ...props}) => {
|
|
8
7
|
const {formState} = useFormContext()
|
|
9
8
|
|
|
10
9
|
const [alertContent, setAlertContent] = useState(null)
|
|
@@ -76,7 +75,7 @@ const AlertBanner = forwardRef(({modalRef, ...props}, ref) => {
|
|
|
76
75
|
{alertContent}
|
|
77
76
|
</div>
|
|
78
77
|
)
|
|
79
|
-
}
|
|
78
|
+
}
|
|
80
79
|
|
|
81
80
|
AlertBanner.displayName = "AlertBanner"
|
|
82
81
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/* @flow */
|
|
2
2
|
import debug from "debug"
|
|
3
|
-
import {
|
|
3
|
+
import {useImperativeHandle, useEffect, useState, useRef} from "react"
|
|
4
4
|
import {FormProvider} from "react-hook-form"
|
|
5
5
|
|
|
6
6
|
import ActivityIndicator from "../../ActivityIndicator"
|
|
@@ -15,9 +15,7 @@ import "./modal-form.scss"
|
|
|
15
15
|
|
|
16
16
|
const log = debug("form")
|
|
17
17
|
|
|
18
|
-
|
|
19
18
|
const ExtLink = ({to}) => {
|
|
20
|
-
|
|
21
19
|
return (
|
|
22
20
|
<a target="_blank" rel="noopener noreferrer" href={to}>
|
|
23
21
|
{to}
|
|
@@ -25,169 +23,166 @@ const ExtLink = ({to}) => {
|
|
|
25
23
|
)
|
|
26
24
|
}
|
|
27
25
|
|
|
28
|
-
|
|
29
26
|
const SHOW_LOADER_DELAY = 120 // ms
|
|
30
27
|
|
|
31
28
|
// Bootstrap Modal integrated with hook form
|
|
32
|
-
const ModalForm =
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
29
|
+
const ModalForm = ({
|
|
30
|
+
ref: _ref,
|
|
31
|
+
title,
|
|
32
|
+
icon,
|
|
33
|
+
footerLink,
|
|
34
|
+
submitTitle,
|
|
35
|
+
submittingTitle,
|
|
36
|
+
show,
|
|
37
|
+
onHide,
|
|
38
|
+
isLiveSubmit = false,
|
|
39
|
+
form,
|
|
40
|
+
onSubmit,
|
|
41
|
+
children,
|
|
42
|
+
...props
|
|
43
|
+
}) => {
|
|
44
|
+
const {formState, watch, handleSubmit} = form
|
|
45
|
+
|
|
46
|
+
const internalRef = useRef(null)
|
|
47
|
+
const ref = _ref || internalRef
|
|
48
|
+
|
|
49
|
+
const alertBannerRef = useRef(null)
|
|
50
|
+
|
|
51
|
+
// timeoutRef: only start displaying isBusy after a short delay
|
|
52
|
+
// to avoid screen flashing when the data loads fast enough
|
|
53
|
+
const timeoutRef = useRef(null)
|
|
54
|
+
const [isBusy, setIsBusy] = useState(false)
|
|
55
|
+
|
|
56
|
+
useImperativeHandle(ref, () => ({
|
|
57
|
+
...ref.current,
|
|
58
|
+
forceClose: () => {
|
|
59
|
+
onHide()
|
|
47
60
|
},
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
const timeoutRef = useRef(null)
|
|
60
|
-
const [isBusy, setIsBusy] = useState(false)
|
|
61
|
-
|
|
62
|
-
useImperativeHandle(ref, () => ({
|
|
63
|
-
...ref.current,
|
|
64
|
-
forceClose: () => {
|
|
65
|
-
onHide()
|
|
66
|
-
},
|
|
67
|
-
}))
|
|
68
|
-
|
|
69
|
-
useEffect(() => {
|
|
70
|
-
// busy when either submitting or loading (async getDefaultValues)
|
|
71
|
-
if (formState.isSubmitting || formState.isLoading) {
|
|
72
|
-
timeoutRef.current = setTimeout(() => {
|
|
73
|
-
setIsBusy(true)
|
|
74
|
-
}, SHOW_LOADER_DELAY)
|
|
75
|
-
} else {
|
|
76
|
-
if (timeoutRef.current) {
|
|
77
|
-
window.clearTimeout(timeoutRef.current)
|
|
78
|
-
}
|
|
79
|
-
setIsBusy(false)
|
|
61
|
+
}))
|
|
62
|
+
|
|
63
|
+
useEffect(() => {
|
|
64
|
+
// busy when either submitting or loading (async getDefaultValues)
|
|
65
|
+
if (formState.isSubmitting || formState.isLoading) {
|
|
66
|
+
timeoutRef.current = setTimeout(() => {
|
|
67
|
+
setIsBusy(true)
|
|
68
|
+
}, SHOW_LOADER_DELAY)
|
|
69
|
+
} else {
|
|
70
|
+
if (timeoutRef.current) {
|
|
71
|
+
window.clearTimeout(timeoutRef.current)
|
|
80
72
|
}
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
if (isLiveSubmit) {
|
|
85
|
-
const subscription = watch(handleSubmit(onSubmit))
|
|
86
|
-
return () => subscription.unsubscribe()
|
|
87
|
-
}
|
|
88
|
-
}, [isLiveSubmit, handleSubmit, watch])
|
|
73
|
+
setIsBusy(false)
|
|
74
|
+
}
|
|
75
|
+
}, [formState.isSubmitting, formState.isLoading, setIsBusy])
|
|
89
76
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
77
|
+
useEffect(() => {
|
|
78
|
+
if (isLiveSubmit) {
|
|
79
|
+
const subscription = watch(handleSubmit(onSubmit))
|
|
80
|
+
return () => subscription.unsubscribe()
|
|
81
|
+
}
|
|
82
|
+
}, [isLiveSubmit, handleSubmit, watch])
|
|
96
83
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
84
|
+
const onHideHandler = () => {
|
|
85
|
+
log("onHideHandler")
|
|
86
|
+
if (!isLiveSubmit) {
|
|
87
|
+
onHide()
|
|
88
|
+
return
|
|
100
89
|
}
|
|
101
90
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
await handleSubmit(onSubmit)()
|
|
91
|
+
if (alertBannerRef.current.onRequestHide()) {
|
|
92
|
+
onHide()
|
|
105
93
|
}
|
|
94
|
+
}
|
|
106
95
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
96
|
+
const onClickSubmit = async() => {
|
|
97
|
+
log("onClickSubmit")
|
|
98
|
+
await handleSubmit(onSubmit)()
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
return (
|
|
102
|
+
<Modal
|
|
103
|
+
className="modal-form"
|
|
104
|
+
show={show}
|
|
105
|
+
onHide={onHideHandler}
|
|
106
|
+
ref={ref}
|
|
107
|
+
backdrop={isBusy ? "static" : true}
|
|
108
|
+
{...props}
|
|
109
|
+
>
|
|
110
|
+
<FormProvider {...form}>
|
|
111
|
+
<AlertBanner ref={alertBannerRef} modalRef={ref} />
|
|
112
|
+
<Modal.Header closeButton>
|
|
113
|
+
<div>
|
|
114
|
+
{icon && (
|
|
115
|
+
<img
|
|
116
|
+
width={20}
|
|
117
|
+
height={20}
|
|
118
|
+
style={{marginTop: 0}}
|
|
119
|
+
className="me-2"
|
|
120
|
+
src={`/static/icons/${icon}.svg`}
|
|
121
|
+
/>
|
|
122
|
+
)}
|
|
123
|
+
{title}
|
|
124
|
+
</div>
|
|
125
|
+
</Modal.Header>
|
|
126
|
+
<Modal.Body className="pb-3" style={{position: "relative"}}>
|
|
127
|
+
<div className={cx("modal-form-body", {"is-busy": isBusy})}>
|
|
128
|
+
<fieldset disabled={isBusy}>{children}</fieldset>
|
|
129
|
+
</div>
|
|
130
|
+
|
|
131
|
+
{isBusy && (
|
|
132
|
+
<div className="loading-overlay">
|
|
133
|
+
{isLiveSubmit && (
|
|
134
|
+
<>
|
|
135
|
+
<ActivityIndicator />
|
|
136
|
+
<div className="mt-2 fw-normal">
|
|
137
|
+
{formState.isLoading && <>Loading...</>}
|
|
138
|
+
{formState.isSubmitting && (
|
|
139
|
+
<>{submittingTitle || "Submitting..."}</>
|
|
140
|
+
)}
|
|
141
|
+
</div>
|
|
142
|
+
</>
|
|
128
143
|
)}
|
|
129
|
-
{title}
|
|
130
144
|
</div>
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
145
|
+
)}
|
|
146
|
+
</Modal.Body>
|
|
147
|
+
|
|
148
|
+
{footerLink && isLiveSubmit && (
|
|
149
|
+
<Modal.Footer className="justify-content-start bg-light">
|
|
150
|
+
For more information, check out
|
|
151
|
+
<ExtLink to={footerLink} />
|
|
152
|
+
</Modal.Footer>
|
|
153
|
+
)}
|
|
154
|
+
|
|
155
|
+
{footerLink && !isLiveSubmit && (
|
|
156
|
+
<Modal.Footer className="p-0 justify-content-start align-items-start flex-column">
|
|
157
|
+
<div className="d-flex flex-row-reverse justify-content-start w-100 py-2">
|
|
158
|
+
<SubmitButton
|
|
159
|
+
className="me-3"
|
|
160
|
+
disabled={formState.isSubmitting || formState.isLoading}
|
|
161
|
+
isLoading={formState.isSubmitting}
|
|
162
|
+
onClick={onClickSubmit}
|
|
163
|
+
>
|
|
164
|
+
{formState.isSubmitting
|
|
165
|
+
? submittingTitle || submitTitle
|
|
166
|
+
: submitTitle}
|
|
167
|
+
</SubmitButton>
|
|
168
|
+
|
|
169
|
+
<button
|
|
170
|
+
className="btn btn-link btn-cancel"
|
|
171
|
+
disabled={formState.isSubmitting}
|
|
172
|
+
onClick={onHideHandler}
|
|
173
|
+
>
|
|
174
|
+
Cancel
|
|
175
|
+
</button>
|
|
135
176
|
</div>
|
|
136
|
-
|
|
137
|
-
{isBusy && (
|
|
138
|
-
<div className="loading-overlay">
|
|
139
|
-
{isLiveSubmit && (
|
|
140
|
-
<>
|
|
141
|
-
<ActivityIndicator />
|
|
142
|
-
<div className="mt-2 fw-normal">
|
|
143
|
-
{formState.isLoading && <>Loading...</>}
|
|
144
|
-
{formState.isSubmitting && <>{submittingTitle || "Submitting..."}</>}
|
|
145
|
-
</div>
|
|
146
|
-
</>
|
|
147
|
-
)}
|
|
148
|
-
</div>
|
|
149
|
-
)}
|
|
150
|
-
</Modal.Body>
|
|
151
|
-
|
|
152
|
-
{footerLink && isLiveSubmit && (
|
|
153
|
-
<Modal.Footer className="justify-content-start bg-light">
|
|
177
|
+
<div className="w-100 bg-light border-top mx-0 my-0 p-3">
|
|
154
178
|
For more information, check out
|
|
155
179
|
<ExtLink to={footerLink} />
|
|
156
|
-
</
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
className="me-3"
|
|
164
|
-
disabled={formState.isSubmitting || formState.isLoading}
|
|
165
|
-
isLoading={formState.isSubmitting}
|
|
166
|
-
onClick={onClickSubmit}
|
|
167
|
-
>
|
|
168
|
-
{formState.isSubmitting ? submittingTitle || submitTitle : submitTitle}
|
|
169
|
-
</SubmitButton>
|
|
170
|
-
|
|
171
|
-
<button
|
|
172
|
-
className="btn btn-link btn-cancel"
|
|
173
|
-
disabled={formState.isSubmitting}
|
|
174
|
-
onClick={onHideHandler}
|
|
175
|
-
>
|
|
176
|
-
Cancel
|
|
177
|
-
</button>
|
|
178
|
-
</div>
|
|
179
|
-
<div className="w-100 bg-light border-top mx-0 my-0 p-3">
|
|
180
|
-
For more information, check out
|
|
181
|
-
<ExtLink to={footerLink} />
|
|
182
|
-
</div>
|
|
183
|
-
</Modal.Footer>
|
|
184
|
-
)}
|
|
185
|
-
</FormProvider>
|
|
186
|
-
</Modal>
|
|
187
|
-
)
|
|
188
|
-
},
|
|
189
|
-
)
|
|
190
|
-
|
|
191
|
-
ModalForm.displayName = "ModalForm"
|
|
180
|
+
</div>
|
|
181
|
+
</Modal.Footer>
|
|
182
|
+
)}
|
|
183
|
+
</FormProvider>
|
|
184
|
+
</Modal>
|
|
185
|
+
)
|
|
186
|
+
}
|
|
192
187
|
|
|
193
188
|
export default ModalForm
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import {View as RNView} from "react-native"
|
|
2
|
+
import {ReactNode, Ref} from "react"
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
interface Props {
|
|
6
|
+
ref?: Ref<any>;
|
|
7
|
+
children?: ReactNode;
|
|
8
|
+
[key: string]: any;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export const View = ({ref, children, ...props}: Props) => {
|
|
12
|
+
return (
|
|
13
|
+
<RNView ref={ref} {...props}>
|
|
14
|
+
{children}
|
|
15
|
+
</RNView>
|
|
16
|
+
)
|
|
17
|
+
}
|
package/ui/View/index.web.js
CHANGED
|
@@ -1,41 +1,44 @@
|
|
|
1
|
-
/* @flow */
|
|
2
|
-
import {forwardRef} from "react"
|
|
3
1
|
import {OverlayTrigger, Tooltip} from "react-bootstrap"
|
|
4
2
|
|
|
5
3
|
|
|
6
|
-
export const View =
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
4
|
+
export const View = ({
|
|
5
|
+
ref,
|
|
6
|
+
children,
|
|
7
|
+
tooltip,
|
|
8
|
+
tooltipProps = {style: {}},
|
|
9
|
+
className = "",
|
|
10
|
+
...props
|
|
11
|
+
}) => {
|
|
12
|
+
const comp = (
|
|
13
|
+
<div ref={ref} className={cx("d-flex", className)} {...props}>
|
|
14
|
+
{children}
|
|
15
|
+
</div>
|
|
16
|
+
)
|
|
13
17
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
return (
|
|
17
|
-
<Tooltip
|
|
18
|
-
{...overlayTooltipProps}
|
|
19
|
-
{...tooltipProps}
|
|
20
|
-
placement="left-start"
|
|
21
|
-
style={{...style, ...tooltipProps.style}}
|
|
22
|
-
>
|
|
23
|
-
{tooltip}
|
|
24
|
-
</Tooltip>
|
|
25
|
-
)
|
|
26
|
-
}
|
|
18
|
+
if (tooltip) {
|
|
19
|
+
const renderTooltip = ({style, ...overlayTooltipProps}) => {
|
|
27
20
|
return (
|
|
28
|
-
<
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
21
|
+
<Tooltip
|
|
22
|
+
{...overlayTooltipProps}
|
|
23
|
+
{...tooltipProps}
|
|
24
|
+
placement="left-start"
|
|
25
|
+
style={{...style, ...tooltipProps.style}}
|
|
33
26
|
>
|
|
34
|
-
{
|
|
35
|
-
</
|
|
27
|
+
{tooltip}
|
|
28
|
+
</Tooltip>
|
|
36
29
|
)
|
|
37
30
|
}
|
|
31
|
+
return (
|
|
32
|
+
<OverlayTrigger
|
|
33
|
+
placement="top"
|
|
34
|
+
transition={false}
|
|
35
|
+
delay={{show: 0, hide: 100}}
|
|
36
|
+
overlay={renderTooltip}
|
|
37
|
+
>
|
|
38
|
+
{comp}
|
|
39
|
+
</OverlayTrigger>
|
|
40
|
+
)
|
|
41
|
+
}
|
|
38
42
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
)
|
|
43
|
+
return comp
|
|
44
|
+
}
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
import React, {Suspense, useEffect, useState} from "react"
|
|
1
|
+
import {Suspense, useEffect, useState, lazy} from "react"
|
|
3
2
|
|
|
4
3
|
import ActivityIndicator from "../../ActivityIndicator"
|
|
5
4
|
|
|
@@ -7,7 +6,7 @@ import ActivityIndicator from "../../ActivityIndicator"
|
|
|
7
6
|
const DELAY_BEFORE_LOADER = 200
|
|
8
7
|
|
|
9
8
|
export const withSuspense = (loadFn, opts = {hideLoader: false}) => {
|
|
10
|
-
const Component =
|
|
9
|
+
const Component = lazy(loadFn)
|
|
11
10
|
|
|
12
11
|
const Loader = () => {
|
|
13
12
|
const [showLoader, setShowLoader] = useState(false)
|
|
@@ -28,11 +27,11 @@ export const withSuspense = (loadFn, opts = {hideLoader: false}) => {
|
|
|
28
27
|
)
|
|
29
28
|
}
|
|
30
29
|
|
|
31
|
-
const Wrapper =
|
|
30
|
+
const Wrapper = ({ref, ...props}) => (
|
|
32
31
|
<Suspense fallback={<Loader />}>
|
|
33
32
|
<Component ref={ref} {...props} />
|
|
34
33
|
</Suspense>
|
|
35
|
-
)
|
|
34
|
+
)
|
|
36
35
|
|
|
37
36
|
return Wrapper
|
|
38
37
|
}
|
|
@@ -1,120 +0,0 @@
|
|
|
1
|
-
/* @flow */
|
|
2
|
-
import {motion} from "framer-motion"
|
|
3
|
-
import {useRef, useState, useImperativeHandle, forwardRef} from "react"
|
|
4
|
-
|
|
5
|
-
import {SPRING_DEFAULT} from "../springs"
|
|
6
|
-
import useBackdrop from "./useBackdrop"
|
|
7
|
-
|
|
8
|
-
import "./exp.scss"
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
export const ExpandableFloatView = forwardRef(
|
|
12
|
-
({renderContent, containerRef, onGetExpandedRect, targetRef, ...props}, ref) => {
|
|
13
|
-
const parentRef = useRef(null)
|
|
14
|
-
const wrapperRef = useRef(null)
|
|
15
|
-
|
|
16
|
-
const isOpen = useRef(false)
|
|
17
|
-
const openedFromRect = useRef(null)
|
|
18
|
-
|
|
19
|
-
const [animatedVals, setAnimatedVals] = useState({})
|
|
20
|
-
|
|
21
|
-
const getRect = () => {
|
|
22
|
-
const boundingRect = containerRef.current.getBoundingClientRect()
|
|
23
|
-
const rect = {
|
|
24
|
-
left: `${boundingRect.x}px`,
|
|
25
|
-
top: `${boundingRect.top}px`,
|
|
26
|
-
right: `${window.innerWidth - boundingRect.right}px`,
|
|
27
|
-
bottom: `${window.innerHeight - boundingRect.bottom}px`,
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
return rect
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
const onOpen = () => {
|
|
34
|
-
showBackdrop()
|
|
35
|
-
|
|
36
|
-
isOpen.current = true
|
|
37
|
-
|
|
38
|
-
const expandedRect = onGetExpandedRect()
|
|
39
|
-
|
|
40
|
-
requestAnimationFrame(() => {
|
|
41
|
-
// set the wrapper ref to current + fixed
|
|
42
|
-
const rect = getRect()
|
|
43
|
-
openedFromRect.current = rect
|
|
44
|
-
|
|
45
|
-
const newStyle = {
|
|
46
|
-
position: "fixed",
|
|
47
|
-
top: rect.top,
|
|
48
|
-
right: rect.right,
|
|
49
|
-
bottom: rect.bottom,
|
|
50
|
-
left: rect.left,
|
|
51
|
-
zIndex: 1000,
|
|
52
|
-
}
|
|
53
|
-
Object.keys(newStyle).forEach((k) => {
|
|
54
|
-
wrapperRef.current.style[k] = newStyle[k]
|
|
55
|
-
})
|
|
56
|
-
|
|
57
|
-
document.body.appendChild(wrapperRef.current)
|
|
58
|
-
|
|
59
|
-
setAnimatedVals(expandedRect)
|
|
60
|
-
})
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
const onClose = () => {
|
|
64
|
-
isOpen.current = false
|
|
65
|
-
|
|
66
|
-
const returnToRect = openedFromRect.current
|
|
67
|
-
setAnimatedVals({
|
|
68
|
-
top: returnToRect.top,
|
|
69
|
-
right: returnToRect.right,
|
|
70
|
-
bottom: returnToRect.bottom,
|
|
71
|
-
left: returnToRect.left,
|
|
72
|
-
zIndex: "unset",
|
|
73
|
-
})
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
const onToggle = () => {
|
|
77
|
-
if (isOpen.current) {
|
|
78
|
-
onClose()
|
|
79
|
-
} else {
|
|
80
|
-
onOpen()
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
// BACKDROP
|
|
85
|
-
const {showBackdrop} = useBackdrop(isOpen, onToggle)
|
|
86
|
-
// BACKDROP
|
|
87
|
-
|
|
88
|
-
useImperativeHandle(ref, () => {
|
|
89
|
-
return {
|
|
90
|
-
open: () => {
|
|
91
|
-
onOpen()
|
|
92
|
-
},
|
|
93
|
-
toggle: () => {
|
|
94
|
-
onToggle()
|
|
95
|
-
},
|
|
96
|
-
close: () => onClose(),
|
|
97
|
-
}
|
|
98
|
-
})
|
|
99
|
-
|
|
100
|
-
return (
|
|
101
|
-
<>
|
|
102
|
-
<div ref={parentRef}>
|
|
103
|
-
<motion.div
|
|
104
|
-
ref={wrapperRef}
|
|
105
|
-
style={{
|
|
106
|
-
inset: "unset",
|
|
107
|
-
// zIndex: 10000,
|
|
108
|
-
// ...styleProps,
|
|
109
|
-
}}
|
|
110
|
-
transition={SPRING_DEFAULT}
|
|
111
|
-
animate={{...animatedVals}}
|
|
112
|
-
onClick={(e) => e.stopPropagation()}
|
|
113
|
-
>
|
|
114
|
-
{renderContent({})}
|
|
115
|
-
</motion.div>
|
|
116
|
-
</div>
|
|
117
|
-
</>
|
|
118
|
-
)
|
|
119
|
-
},
|
|
120
|
-
)
|
package/ui/View/index.js
DELETED