@rpcbase/client 0.209.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.
@@ -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 = forwardRef(
7
- ({children, tooltip, tooltipProps = {style: {}}, className = "", ...props}, ref) => {
8
- const comp = (
9
- <div ref={ref} className={cx("d-flex", className)} {...props}>
10
- {children}
11
- </div>
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
- if (tooltip) {
15
- const renderTooltip = ({style, ...overlayTooltipProps}) => {
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
- <OverlayTrigger
29
- placement="top"
30
- transition={false}
31
- delay={{show: 0, hide: 100}}
32
- overlay={renderTooltip}
21
+ <Tooltip
22
+ {...overlayTooltipProps}
23
+ {...tooltipProps}
24
+ placement="left-start"
25
+ style={{...style, ...tooltipProps.style}}
33
26
  >
34
- {comp}
35
- </OverlayTrigger>
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
- return comp
40
- },
41
- )
43
+ return comp
44
+ }
@@ -1,5 +1,4 @@
1
- /* @flow */
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 = React.lazy(loadFn)
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 = React.forwardRef((props, ref) => (
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
  }
@@ -7,7 +7,7 @@ $account-max-width: 160px;
7
7
  $img-size: 32px;
8
8
  $img-border-radius: 3px;
9
9
 
10
- #main-header-nav {
10
+ #header-container {
11
11
  border-bottom: 1px solid $black;
12
12
  background-color: $black !important;
13
13
  height: 45px;
@@ -0,0 +1,151 @@
1
+ import {ReactNode} from "react"
2
+
3
+ // import apiClient from "../../../apiClient"
4
+
5
+ // import SearchAnything from "components/search/SearchAnything"
6
+
7
+ // import LogoNav from "./components/LogoNav"
8
+
9
+ // Env Selector
10
+ // import useFilteredEnvs from "./components/EnvSelector/useFilteredEnvs"
11
+ // import EnvSelectorToggle from "./components/EnvSelector/Toggle"
12
+ // import EnvSelectorDropdown from "./components/EnvSelector/Dropdown"
13
+ // Group Selector
14
+ // import useFilteredGroups from "./components/GroupSelector/useFilteredGroups"
15
+ // import GroupSelectorToggle from "./components/GroupSelector/Toggle"
16
+ // import GroupSelectorDropdown from "./components/GroupSelector/Dropdown"
17
+
18
+ // import MorphingDropdown from "./components/MorphingDropdown"
19
+
20
+ // import AccountsToggle from "./components/AccountsToggle"
21
+ // import NotificationsToggle from "./components/NotificationsToggle"
22
+ // import EnvSettingsToggle from "./components/EnvSettingsToggle"
23
+ // import PublishControl from "./components/PublishControl"
24
+
25
+ // import AccountsDropdown from "./components/AccountsDropdown"
26
+ // import NotificationsDropdown from "./components/NotificationsDropdown"
27
+ // import PhoneDropdown from "components/phone/PhoneDropdown"
28
+ // import EnvSettingsDropdown from "./components/EnvSettingsDropdown"
29
+
30
+ import "./header.scss"
31
+
32
+
33
+ // const DefaultHeader = () => {
34
+ // return (
35
+ // <>
36
+ // <LogoNav isSignedIn={false} />
37
+
38
+ // <div className="nav-default d-flex flex-row justify-content-between w-100">
39
+ // <div className="d-flex flex-row"></div>
40
+
41
+ // <div className="d-flex flex-row">
42
+ // <a className="nav-link px-2 me-2 fw-bold" href="/signin">
43
+ // Sign In
44
+ // </a>
45
+ // </div>
46
+ // </div>
47
+ // </>
48
+ // )
49
+ // }
50
+
51
+ // const SignedInHeader = () => {
52
+ // const loc = window.location.pathname
53
+
54
+ // const [accounts, setAccounts] = useState([])
55
+
56
+ // const {envs, activeEnv, setFilter: setEnvsFilter} = useFilteredEnvs()
57
+ // const {groups, activeGroup, setFilter: setGroupsFilter} = useFilteredGroups()
58
+
59
+ // useEffect(() => {
60
+ // // TODO: useapi hook with cache
61
+ // const load = async() => {
62
+ // const res = await apiClient.post("/api/v1/auth/get_accounts")
63
+ // assert(res.data.status === "ok")
64
+ // setAccounts(res.data.accounts)
65
+ // // console.log("accounts", res.data.accounts)
66
+ // }
67
+
68
+ // load()
69
+ // }, [])
70
+
71
+ // return (
72
+ // <>
73
+ // <MorphingDropdown.Provider side="left">
74
+ // <LogoNav isSignedIn={true} />
75
+
76
+ // <EnvSelectorToggle activeEnv={activeEnv} />
77
+ // <GroupSelectorToggle activeGroup={activeGroup} />
78
+
79
+ // <MorphingDropdown.Portal>
80
+ // <EnvSelectorDropdown
81
+ // id="env-selector"
82
+ // envs={envs}
83
+ // activeEnv={activeEnv}
84
+ // setFilter={setEnvsFilter}
85
+ // />
86
+
87
+ // <GroupSelectorDropdown
88
+ // id="group-selector"
89
+ // groups={groups}
90
+ // activeGroup={activeGroup}
91
+ // setFilter={setGroupsFilter}
92
+ // />
93
+ // </MorphingDropdown.Portal>
94
+ // </MorphingDropdown.Provider>
95
+
96
+ // {/* Search anything */}
97
+ // <SearchAnything />
98
+
99
+ // <div className="ms-auto d-none d-md-flex flex-row text-truncate">
100
+ // <a className="nav-link mx-2 px-1" href="/docs">
101
+ // Docs
102
+ // </a>
103
+ // <a
104
+ // className={cx("nav-link px-1", {active: loc.startsWith("/marketplace")})}
105
+ // href="/marketplace"
106
+ // >
107
+ // Marketplace
108
+ // </a>
109
+ // </div>
110
+
111
+ // <PublishControl />
112
+
113
+ // {/* Phone */}
114
+ // <PhoneDropdown />
115
+
116
+ // <MorphingDropdown.Provider side="right">
117
+ // {/* WARNING: update the anchor-${ids} on the toggle to match the menus if adding or removing */}
118
+ // <NotificationsToggle />
119
+
120
+ // <EnvSettingsToggle />
121
+
122
+ // <AccountsToggle />
123
+
124
+ // <MorphingDropdown.Portal>
125
+ // {/* Notifications */}
126
+ // <NotificationsDropdown id="notifications" />
127
+
128
+ // {/* Env Settings */}
129
+ // <EnvSettingsDropdown id="env-setup" />
130
+
131
+ // {/* Accounts + Org */}
132
+ // <AccountsDropdown id="accounts" accounts={accounts} />
133
+ // </MorphingDropdown.Portal>
134
+ // </MorphingDropdown.Provider>
135
+ // </>
136
+ // )
137
+ // }
138
+
139
+ export const HeaderContainer = ({children}: {children: ReactNode}) => {
140
+
141
+ if (!children) return null
142
+
143
+ return (
144
+ <nav
145
+ id="header-container"
146
+ className={"d-flex align-items-center fixed-top bg-dark flex-md-nowrap shadow p-0 text-light"}
147
+ >
148
+ {children}
149
+ </nav>
150
+ )
151
+ }
@@ -0,0 +1,151 @@
1
+ import assert from "assert"
2
+ import {
3
+ ReactNode,
4
+ createContext,
5
+ useContext,
6
+ useId,
7
+ useRef,
8
+ useState,
9
+ useEffect,
10
+ useCallback,
11
+ } from "react"
12
+
13
+
14
+ const TOGGLE_OFF_DELAY = 180
15
+
16
+ export const MorphingDropdownContext = createContext(undefined!)
17
+
18
+ export const useMorphingDropdown = () => useContext(MorphingDropdownContext)
19
+
20
+ export const MorphingDropdownProvider = ({
21
+ children,
22
+ side,
23
+ }: {
24
+ children: ReactNode;
25
+ side: "left" | "right";
26
+ }) => {
27
+ const portalId = useId()
28
+
29
+ const setNextMountedRectRef = useRef(() => null)
30
+ const onResetStateRef = useRef(() => null)
31
+ const mousePosRef = useRef({x: 0, y: 0})
32
+ const isMouseOverToggleRef = useRef(false)
33
+
34
+ const [activeId, _setActiveId] = useState()
35
+
36
+ useEffect(() => {
37
+ assert(["left", "right"].includes(side))
38
+ }, [])
39
+
40
+ useEffect(() => {
41
+ const listener = (e) => {
42
+ mousePosRef.current = {
43
+ x: e.pageX,
44
+ y: e.pageY,
45
+ }
46
+ }
47
+
48
+ document.addEventListener("mousemove", listener)
49
+
50
+ return () => {
51
+ document.removeEventListener("mousemove", listener)
52
+ }
53
+ }, [])
54
+
55
+ const setActiveId = useCallback((nextId) => {
56
+ let shouldReset = false
57
+ _setActiveId((currentId) => {
58
+ if (nextId === currentId || nextId === null) {
59
+ shouldReset = true
60
+
61
+ return null
62
+ } else return nextId
63
+ })
64
+
65
+ if (shouldReset) {
66
+ const fn = onResetStateRef.current
67
+ if (typeof fn === "function") {
68
+ fn()
69
+ }
70
+ }
71
+ }, [])
72
+
73
+ const close = useCallback(() => {
74
+ setActiveId(null)
75
+ }, [setActiveId])
76
+
77
+ const hideAfterMouseLeave = useCallback(() => {
78
+ // hovering a toggle, skip
79
+ if (isMouseOverToggleRef.current) return
80
+
81
+ // check if we are hovering the menu's portal
82
+ const portalEl = document.getElementById(portalId)
83
+
84
+ if (!portalEl) {
85
+ setActiveId(null)
86
+ return
87
+ }
88
+
89
+ // portal exists, check if cursor is hovering it
90
+ const rect = portalEl.getBoundingClientRect()
91
+ const {x: mouseX, y: mouseY} = mousePosRef.current
92
+
93
+ const isHovering =
94
+ mouseX >= rect.left &&
95
+ mouseX <= rect.right &&
96
+ mouseY >= rect.top &&
97
+ mouseY <= rect.bottom
98
+
99
+ if (!isHovering) {
100
+ setActiveId(null)
101
+ return
102
+ }
103
+ }, [])
104
+
105
+ const registerSetNextMountedHandler = (fn) => {
106
+ setNextMountedRectRef.current = fn
107
+ }
108
+
109
+ const registerOnResetState = (fn) => {
110
+ onResetStateRef.current = fn
111
+ }
112
+
113
+ // when a toggle is hovered
114
+ const applyMouseEnterToggle = (id) => {
115
+ isMouseOverToggleRef.current = true
116
+ _setActiveId(id)
117
+ }
118
+
119
+ const applyMouseLeaveToggle = () => {
120
+ isMouseOverToggleRef.current = false
121
+ setTimeout(() => {
122
+ hideAfterMouseLeave()
123
+ }, TOGGLE_OFF_DELAY)
124
+ }
125
+
126
+ const applyMouseLeaveMenu = () => {
127
+ setTimeout(() => {
128
+ hideAfterMouseLeave()
129
+ }, TOGGLE_OFF_DELAY)
130
+ }
131
+
132
+ return (
133
+ <MorphingDropdownContext.Provider
134
+ value={{
135
+ registerSetNextMountedHandler,
136
+ setNextMountedRect: setNextMountedRectRef.current,
137
+ registerOnResetState,
138
+ applyMouseEnterToggle,
139
+ applyMouseLeaveToggle,
140
+ applyMouseLeaveMenu,
141
+ activeId,
142
+ setActiveId,
143
+ close,
144
+ side,
145
+ portalId,
146
+ }}
147
+ >
148
+ {children}
149
+ </MorphingDropdownContext.Provider>
150
+ )
151
+ }
@@ -0,0 +1,38 @@
1
+ import assert from "assert"
2
+ import {useRef, useLayoutEffect} from "react"
3
+
4
+ import {useMorphingDropdown} from "./MorphingDropdownContext"
5
+
6
+
7
+ export const MorphingDropdownMenu = ({
8
+ ref: parentRef,
9
+ id,
10
+ style = {},
11
+ children,
12
+ className = "",
13
+ ...props
14
+ }) => {
15
+ assert(id, "missing dropdown menu id")
16
+
17
+ const selfRef = useRef(null)
18
+
19
+ const dropdownContext = useMorphingDropdown()
20
+
21
+ const ref = parentRef || selfRef
22
+
23
+ useLayoutEffect(() => {
24
+ const rect = ref.current.getBoundingClientRect()
25
+ dropdownContext.setNextMountedRect(id, rect)
26
+ }, [])
27
+
28
+ return (
29
+ <div
30
+ ref={ref}
31
+ {...props}
32
+ className={cx("text-light", className)}
33
+ style={{...style}}
34
+ >
35
+ {children}
36
+ </div>
37
+ )
38
+ }
@@ -0,0 +1,166 @@
1
+ import assert from "assert"
2
+ import {useState, useEffect, useRef, ReactNode} from "react"
3
+ import {createPortal} from "react-dom"
4
+ import {CSSTransition, TransitionGroup} from "react-transition-group"
5
+
6
+ import {motion} from "framer-motion"
7
+
8
+ import {SPRING_DEFAULT} from "../../springs"
9
+
10
+ import {useMorphingDropdown} from "./MorphingDropdownContext"
11
+
12
+
13
+ const RIGHT_OFFSET = 0
14
+
15
+ const getElementId = (id) => `header-dd-transition-${id}`
16
+
17
+ export const MorphingDropdownPortal = ({children}: {children: ReactNode}) => {
18
+ const portalElRef = useRef(document.createElement("div"))
19
+ const previousMountedIdRef = useRef(null)
20
+ const transitionNodeRef = useRef(null)
21
+
22
+ const dropdownContext = useMorphingDropdown()
23
+ const {portalId} = dropdownContext
24
+
25
+ const [animatedVals, setAnimatedVals] = useState({})
26
+ const [shouldAnimate, setShouldAnimate] = useState(false)
27
+
28
+ useEffect(() => {
29
+ document.body.appendChild(portalElRef.current)
30
+
31
+ return () => {
32
+ document.body.removeChild(portalElRef.current)
33
+ }
34
+ }, [])
35
+
36
+ // on click outside dismiss
37
+ useEffect(() => {
38
+ // do nothing if menu isn't open
39
+ if (!dropdownContext.activeId) {
40
+ return
41
+ }
42
+
43
+ const onClick = (e) => {
44
+ const wrapperEl = document.getElementById(portalId)
45
+
46
+ // click is inside the menu itself, so don't do anything
47
+ if (wrapperEl.contains(e.target)) {
48
+ return
49
+ }
50
+
51
+ // clicked outside, but we must ensure we didn't click on an anchor
52
+ let isInsideAnchor = false
53
+ const anchorElements = Array.from(document.querySelectorAll(".morphing-dropdown-anchor"))
54
+ anchorElements.forEach((anchorEl) => {
55
+ if (anchorEl.contains(e.target)) {
56
+ isInsideAnchor = true
57
+ }
58
+ })
59
+
60
+ // we have clicked, not in the menu and not on a menu anchor, dismiss
61
+ if (!isInsideAnchor) {
62
+ dropdownContext.setActiveId(null)
63
+ }
64
+ }
65
+
66
+ document.body.addEventListener("click", onClick)
67
+
68
+ return () => {
69
+ document.body.removeEventListener("click", onClick)
70
+ }
71
+ }, [dropdownContext.activeId])
72
+
73
+ //
74
+ dropdownContext.registerSetNextMountedHandler((id, rect) => {
75
+ const {height: nextHeight, width: nextWidth} = rect
76
+
77
+ const anchorElement = document.getElementById(`anchor-${id}`)
78
+ assert(anchorElement, "anchor element not found")
79
+ const anchorRect = anchorElement.getBoundingClientRect()
80
+ const centerX = anchorRect.x + anchorRect.width / 2
81
+
82
+ const viewportWidth = window.innerWidth
83
+
84
+ const nextAnimatedVals = {opacity: 1, height: nextHeight, width: nextWidth}
85
+
86
+ // LEFT
87
+ if (dropdownContext.side === "left") {
88
+ const nextLeft = centerX - nextWidth / 2
89
+ let left = 0 + nextLeft
90
+
91
+ if (left < 0) {
92
+ left = 0
93
+ // handles the right corner radius + border
94
+ document.getElementById(portalId).classList.add("docked-left")
95
+ } else {
96
+ document.getElementById(portalId).classList.remove("docked-left")
97
+ }
98
+
99
+ nextAnimatedVals.left = left
100
+ }
101
+ // RIGHT
102
+ else if (dropdownContext.side === "right") {
103
+ const nextRight = centerX + nextWidth / 2
104
+ let right = viewportWidth - nextRight
105
+
106
+ if (right < 0) {
107
+ right = 0
108
+ // handles the right corner radius + border
109
+ document.getElementById(portalId).classList.add("docked-right")
110
+ } else {
111
+ document.getElementById(portalId).classList.remove("docked-right")
112
+ }
113
+
114
+ nextAnimatedVals.right = right
115
+ }
116
+
117
+ // do not animate on first mount / render
118
+ const isNewMount = !previousMountedIdRef.current
119
+ previousMountedIdRef.current = id
120
+
121
+ if (isNewMount) {
122
+ setShouldAnimate(false)
123
+ } else {
124
+ setShouldAnimate(true)
125
+ }
126
+
127
+ setAnimatedVals(nextAnimatedVals)
128
+ })
129
+
130
+ dropdownContext.registerOnResetState(() => {
131
+ setShouldAnimate(false)
132
+ previousMountedIdRef.current = null
133
+ setAnimatedVals({opacity: 0})
134
+ })
135
+
136
+ const activeComp = children.find((c) => c.props.id === dropdownContext.activeId)
137
+
138
+ if (!activeComp) return null
139
+
140
+ return createPortal(
141
+ <motion.div
142
+ id={portalId}
143
+ className="morphing-dropdown-wrapper"
144
+ style={{position: "fixed", top: 45, right: RIGHT_OFFSET, overflow: "hidden"}}
145
+ transition={shouldAnimate ? SPRING_DEFAULT : {duration: 0}}
146
+ animate={animatedVals}
147
+ onMouseLeave={dropdownContext.applyMouseLeaveMenu}
148
+ >
149
+ <TransitionGroup>
150
+ <CSSTransition
151
+ id={getElementId(dropdownContext.activeId)}
152
+ key={dropdownContext.activeId}
153
+ nodeRef={transitionNodeRef}
154
+ timeout={60}
155
+ className="header-dropdown"
156
+ classNames="header-dropdown-fade"
157
+ unmountOnExit
158
+ in={true}
159
+ >
160
+ <div ref={transitionNodeRef} style={{position: "absolute", overflow: "hidden", minWidth: 100}}>{activeComp}</div>
161
+ </CSSTransition>
162
+ </TransitionGroup>
163
+ </motion.div>,
164
+ portalElRef.current,
165
+ )
166
+ }
@@ -0,0 +1,34 @@
1
+ import {CSSProperties, ReactNode} from "react"
2
+ import {useMorphingDropdown} from "./MorphingDropdownContext"
3
+
4
+
5
+ export const MorphingDropdownToggle = ({
6
+ id,
7
+ className = "",
8
+ style = {},
9
+ children,
10
+ }: {
11
+ id: string;
12
+ className?: string;
13
+ style?: CSSProperties;
14
+ children: ReactNode;
15
+ }) => {
16
+ const morphingDropdown = useMorphingDropdown()
17
+
18
+ const onMouseEnter = () => {
19
+ morphingDropdown.applyMouseEnterToggle(id)
20
+ }
21
+
22
+ return (
23
+ <div
24
+ id={`anchor-${id}`}
25
+ className={cx("morphing-dropdown-anchor", className)}
26
+ onClick={() => morphingDropdown.setActiveId(id)}
27
+ onMouseEnter={onMouseEnter}
28
+ onMouseLeave={morphingDropdown.applyMouseLeaveToggle}
29
+ style={{cursor: "pointer", ...style}}
30
+ >
31
+ {children}
32
+ </div>
33
+ )
34
+ }
@@ -0,0 +1,16 @@
1
+ import "./morphing-dropdown.scss"
2
+
3
+ import {MorphingDropdownPortal} from "./MorphingDropdownPortal"
4
+ import {useMorphingDropdown, MorphingDropdownProvider} from "./MorphingDropdownContext"
5
+ import {MorphingDropdownMenu} from "./MorphingDropdownMenu"
6
+ import {MorphingDropdownToggle} from "./MorphingDropdownToggle"
7
+
8
+
9
+ export {
10
+ MorphingDropdownPortal as Portal,
11
+ MorphingDropdownProvider as Provider,
12
+ MorphingDropdownMenu as Menu,
13
+ MorphingDropdownToggle as Toggle,
14
+ }
15
+
16
+ export {useMorphingDropdown}