@rpcbase/client 0.207.0 → 0.208.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.
@@ -13,7 +13,7 @@ import setStoredValues from "./setStoredValues"
13
13
  const DEFAULT_REMOTE = true
14
14
  const UPDATE_THROTTLE_DELAY = 300
15
15
 
16
- const useStoredValue = (_key, defaultValueOrFn = null, options = {}) => {
16
+ export const useStoredValue = (_key, defaultValueOrFn = null, options = {}) => {
17
17
  const uid = getUid()
18
18
  const key = `${uid}.${_key}`
19
19
 
@@ -2,7 +2,7 @@
2
2
  import Form from "react-bootstrap/Form"
3
3
  import TimePicker from "react-time-picker"
4
4
 
5
- import {useStoredValue} from "@rpcbase/client"
5
+ import {useStoredValue} from "../../helpers/useStoredValue"
6
6
 
7
7
 
8
8
  const TIME_FORMAT = "HH:mm"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rpcbase/client",
3
- "version": "0.207.0",
3
+ "version": "0.208.0",
4
4
  "scripts": {
5
5
  "build": "../../node_modules/.bin/wireit",
6
6
  "test": "../../node_modules/.bin/wireit"
@@ -1,16 +1,15 @@
1
- /* @flow */
2
1
  import React, {useState, useEffect, useRef} from "react"
3
2
  import _isEmpty from "lodash/isEmpty"
4
3
 
5
4
  import stopEventPropagation from "../helpers/stopEventPropagation"
6
- import useThrottledMeasure from "..//helpers/useThrottledMeasure"
5
+ import {useThrottledMeasure} from "..//helpers/useThrottledMeasure"
7
6
 
8
7
  import "./tabs.scss"
9
8
 
10
9
 
11
10
  type Props = {
12
11
  active: boolean,
13
- onChange: Function,
12
+ onChange: () => void,
14
13
  scrollIntoView: boolean
15
14
  }
16
15
 
@@ -0,0 +1,11 @@
1
+ import {createContext, useContext} from "react"
2
+
3
+
4
+ type SizeContextType = {
5
+ width: number,
6
+ height: number,
7
+ }
8
+
9
+ export const SizeContext = createContext<SizeContextType>(undefined!)
10
+
11
+ export const useSizeContext = () => useContext(SizeContext)
@@ -6,7 +6,7 @@ import isEqual from "fast-deep-equal/react"
6
6
 
7
7
  const DEFAULT_THROTTLE_TIME = 16
8
8
 
9
- const useThrottledMeasure = (throttleDuration = DEFAULT_THROTTLE_TIME) => {
9
+ export const useThrottledMeasure = (throttleDuration = DEFAULT_THROTTLE_TIME) => {
10
10
  const hasInitialMeasure = useRef(false)
11
11
 
12
12
  const [ref, measuredRect] = useMeasure()
@@ -45,5 +45,3 @@ const useThrottledMeasure = (throttleDuration = DEFAULT_THROTTLE_TIME) => {
45
45
 
46
46
  return [ref, rect]
47
47
  }
48
-
49
- export default useThrottledMeasure
@@ -0,0 +1,22 @@
1
+ import {Dispatch, SetStateAction, createContext, useContext} from "react"
2
+
3
+
4
+ type ContentViewContextType = {
5
+ storageKeyPrefix: string;
6
+ isDocked: boolean;
7
+ setIsDocked: (value: boolean) => void;
8
+ sideItemViewWidth: number;
9
+ onUpdateSlideoutViewWidth: () => void;
10
+ contentViewWidth: number;
11
+ contentViewHeight: number;
12
+ sidebarWidth: number;
13
+ setSidebarWidth: Dispatch<SetStateAction<number>>;
14
+ };
15
+
16
+ export const ContentViewContext = createContext<ContentViewContextType>(
17
+ undefined!,
18
+ )
19
+
20
+ export default ContentViewContext
21
+
22
+ export const useContentViewContext = () => useContext(ContentViewContext)
@@ -0,0 +1,109 @@
1
+ import {useState, memo, ReactNode, useMemo} from "react"
2
+
3
+ import {useStoredValue} from "../../../helpers/useStoredValue"
4
+ import {useThrottledMeasure} from "../../helpers/useThrottledMeasure"
5
+
6
+ import ContentViewContext from "./ContentViewContext"
7
+ import getUid from "../../../auth/getUid"
8
+
9
+
10
+ const DEFAULT_SIDEBAR_WIDTH = 200
11
+
12
+ const SIDEVIEW_WIDTH = "sideview_width"
13
+
14
+ export const ContentView = memo(
15
+ ({
16
+ children,
17
+ storageKeyPrefix: _storageKeyPrefix,
18
+ }: {
19
+ children: ReactNode;
20
+ storageKeyPrefix?: string;
21
+ }) => {
22
+ const uid = getUid()
23
+
24
+ const [isDocked, setIsDocked] = useState(false)
25
+
26
+ const [
27
+ contentViewRef,
28
+ {width: contentViewWidth, height: contentViewHeight},
29
+ ] = useThrottledMeasure()
30
+
31
+ const storageKeyPrefix = _storageKeyPrefix
32
+ ? _storageKeyPrefix
33
+ : uid || "unknown"
34
+
35
+ const sidebarWidthKey = `${storageKeyPrefix}.sidebar_width`
36
+ const [sidebarWidth, setSidebarWidth] = useStoredValue(
37
+ sidebarWidthKey,
38
+ DEFAULT_SIDEBAR_WIDTH,
39
+ )
40
+
41
+ const getKey = (k) => `${storageKeyPrefix}.content_view.${k}`
42
+
43
+ const [sideItemViewWidth, setSlideoutViewWidth] = useStoredValue(
44
+ getKey(SIDEVIEW_WIDTH),
45
+ 320,
46
+ )
47
+
48
+ const onUpdateSlideoutViewWidth = (width) => {
49
+ setSlideoutViewWidth(width)
50
+ }
51
+
52
+ const contentWidthOffset = useMemo(() => {
53
+ if (isDocked) {
54
+ return sidebarWidth + sideItemViewWidth
55
+ } else {
56
+ return sidebarWidth
57
+ }
58
+ }, [isDocked, sidebarWidth, sideItemViewWidth])
59
+
60
+ // don't render anything before values are initialized
61
+ if (
62
+ typeof sidebarWidth === "undefined" ||
63
+ typeof sideItemViewWidth === "undefined"
64
+ )
65
+ return null
66
+
67
+ return (
68
+ <ContentViewContext
69
+ value={{
70
+ storageKeyPrefix,
71
+ isDocked,
72
+ setIsDocked,
73
+ sideItemViewWidth,
74
+ onUpdateSlideoutViewWidth,
75
+ contentViewWidth,
76
+ contentViewHeight,
77
+ sidebarWidth,
78
+ setSidebarWidth,
79
+ }}
80
+ >
81
+ <div
82
+ className=""
83
+ style={{
84
+ display: "flex",
85
+ flexDirection: "row",
86
+ }}
87
+ >
88
+ <div id="sidebar-container" />
89
+
90
+ <div
91
+ ref={contentViewRef}
92
+ key={`content-wrapper-${"content_view_"}`}
93
+ style={{
94
+ width: `calc(100vw - ${contentWidthOffset}px)`,
95
+ height: "calc(100vh - 45px)",
96
+ marginTop: 45,
97
+ overflowY: "scroll",
98
+ marginLeft: sidebarWidth,
99
+ }}
100
+ >
101
+ {children}
102
+ </div>
103
+ </div>
104
+
105
+ <div id="slideout-container" />
106
+ </ContentViewContext>
107
+ )
108
+ },
109
+ )
@@ -0,0 +1,53 @@
1
+ @import "helpers";
2
+
3
+ @import "./variables";
4
+
5
+ $brand-v-padding: 11px;
6
+ $account-max-width: 160px;
7
+ $img-size: 32px;
8
+ $img-border-radius: 3px;
9
+
10
+ #main-header-nav {
11
+ border-bottom: 1px solid $black;
12
+ background-color: $black !important;
13
+ height: 45px;
14
+
15
+ .nav-link {
16
+ color: $gray-200;
17
+
18
+ &.active {
19
+ font-weight: bold;
20
+ color: $white;
21
+ text-shadow: lighten(rgba($blue, 0.6), 80%) 1px 0 8px;
22
+ }
23
+ }
24
+ }
25
+
26
+ .current-account-subtitle {
27
+ color: $gray-900;
28
+ max-width: $account-max-width;
29
+ }
30
+
31
+ .accounts-list-item {
32
+ display: flex !important;
33
+ flex-direction: row !important;
34
+ align-items: center;
35
+
36
+ .text-truncate {
37
+ max-width: $account-max-width;
38
+ }
39
+
40
+ img {
41
+ width: $img-size;
42
+ height: $img-size;
43
+ border-radius: $img-border-radius;
44
+ }
45
+
46
+ .account-info {
47
+ max-width: 130px;
48
+ }
49
+
50
+ .account-info-small {
51
+ color: $gray-600;
52
+ }
53
+ }
@@ -0,0 +1,150 @@
1
+ /* eslint-disable */
2
+ import assert from "assert"
3
+ import {useState, useEffect} from "react"
4
+
5
+ import apiClient from "../../../apiClient"
6
+
7
+ // import SearchAnything from "components/search/SearchAnything"
8
+
9
+ // import LogoNav from "./components/LogoNav"
10
+
11
+ // Env Selector
12
+ // import useFilteredEnvs from "./components/EnvSelector/useFilteredEnvs"
13
+ // import EnvSelectorToggle from "./components/EnvSelector/Toggle"
14
+ // import EnvSelectorDropdown from "./components/EnvSelector/Dropdown"
15
+ // Group Selector
16
+ // import useFilteredGroups from "./components/GroupSelector/useFilteredGroups"
17
+ // import GroupSelectorToggle from "./components/GroupSelector/Toggle"
18
+ // import GroupSelectorDropdown from "./components/GroupSelector/Dropdown"
19
+
20
+ // import MorphingDropdown from "./components/MorphingDropdown"
21
+
22
+ // import AccountsToggle from "./components/AccountsToggle"
23
+ // import NotificationsToggle from "./components/NotificationsToggle"
24
+ // import EnvSettingsToggle from "./components/EnvSettingsToggle"
25
+ // import PublishControl from "./components/PublishControl"
26
+
27
+ // import AccountsDropdown from "./components/AccountsDropdown"
28
+ // import NotificationsDropdown from "./components/NotificationsDropdown"
29
+ // import PhoneDropdown from "components/phone/PhoneDropdown"
30
+ // import EnvSettingsDropdown from "./components/EnvSettingsDropdown"
31
+
32
+ import "./header.scss"
33
+
34
+
35
+ const DefaultHeader = () => {
36
+ return (
37
+ <>
38
+ <LogoNav isSignedIn={false} />
39
+
40
+ <div className="nav-default d-flex flex-row justify-content-between w-100">
41
+ <div className="d-flex flex-row"></div>
42
+
43
+ <div className="d-flex flex-row">
44
+ <a className="nav-link px-2 me-2 fw-bold" href="/signin">
45
+ Sign In
46
+ </a>
47
+ </div>
48
+ </div>
49
+ </>
50
+ )
51
+ }
52
+
53
+ const SignedInHeader = () => {
54
+ const loc = window.location.pathname
55
+
56
+ const [accounts, setAccounts] = useState([])
57
+
58
+ const {envs, activeEnv, setFilter: setEnvsFilter} = useFilteredEnvs()
59
+ const {groups, activeGroup, setFilter: setGroupsFilter} = useFilteredGroups()
60
+
61
+ useEffect(() => {
62
+ // TODO: useapi hook with cache
63
+ const load = async() => {
64
+ const res = await apiClient.post("/api/v1/auth/get_accounts")
65
+ assert(res.data.status === "ok")
66
+ setAccounts(res.data.accounts)
67
+ // console.log("accounts", res.data.accounts)
68
+ }
69
+
70
+ load()
71
+ }, [])
72
+
73
+ return (
74
+ <>
75
+ <MorphingDropdown.Provider side="left">
76
+ <LogoNav isSignedIn={true} />
77
+
78
+ <EnvSelectorToggle activeEnv={activeEnv} />
79
+ <GroupSelectorToggle activeGroup={activeGroup} />
80
+
81
+ <MorphingDropdown.Portal>
82
+ <EnvSelectorDropdown
83
+ id="env-selector"
84
+ envs={envs}
85
+ activeEnv={activeEnv}
86
+ setFilter={setEnvsFilter}
87
+ />
88
+
89
+ <GroupSelectorDropdown
90
+ id="group-selector"
91
+ groups={groups}
92
+ activeGroup={activeGroup}
93
+ setFilter={setGroupsFilter}
94
+ />
95
+ </MorphingDropdown.Portal>
96
+ </MorphingDropdown.Provider>
97
+
98
+ {/* Search anything */}
99
+ <SearchAnything />
100
+
101
+ <div className="ms-auto d-none d-md-flex flex-row text-truncate">
102
+ <a className="nav-link mx-2 px-1" href="/docs">
103
+ Docs
104
+ </a>
105
+ <a
106
+ className={cx("nav-link px-1", {active: loc.startsWith("/marketplace")})}
107
+ href="/marketplace"
108
+ >
109
+ Marketplace
110
+ </a>
111
+ </div>
112
+
113
+ <PublishControl />
114
+
115
+ {/* Phone */}
116
+ <PhoneDropdown />
117
+
118
+ <MorphingDropdown.Provider side="right">
119
+ {/* WARNING: update the anchor-${ids} on the toggle to match the menus if adding or removing */}
120
+ <NotificationsToggle />
121
+
122
+ <EnvSettingsToggle />
123
+
124
+ <AccountsToggle />
125
+
126
+ <MorphingDropdown.Portal>
127
+ {/* Notifications */}
128
+ <NotificationsDropdown id="notifications" />
129
+
130
+ {/* Env Settings */}
131
+ <EnvSettingsDropdown id="env-setup" />
132
+
133
+ {/* Accounts + Org */}
134
+ <AccountsDropdown id="accounts" accounts={accounts} />
135
+ </MorphingDropdown.Portal>
136
+ </MorphingDropdown.Provider>
137
+ </>
138
+ )
139
+ }
140
+
141
+ export const Header = ({isSignedIn}) => {
142
+ return (
143
+ <nav
144
+ id="main-header-nav"
145
+ className={"d-flex align-items-center fixed-top bg-dark flex-md-nowrap shadow p-0 text-light"}
146
+ >
147
+ this is my header yo
148
+ </nav>
149
+ )
150
+ }
@@ -0,0 +1 @@
1
+ $header-height: 44px;
@@ -0,0 +1,48 @@
1
+ import {createPortal} from "react-dom"
2
+ import {ResizableBox} from "react-resizable"
3
+
4
+ import {useContentViewContext} from "../ContentView/ContentViewContext"
5
+
6
+ import "./sidebar-container.scss"
7
+
8
+
9
+ const OPENED_MIN_WIDTH = 60
10
+
11
+ export const SidebarContainer = ({children}) => {
12
+ const {sidebarWidth, setSidebarWidth} = useContentViewContext()
13
+
14
+ const onResize = (e, data) => {
15
+ const width = data.size.width
16
+
17
+ // only apply new width if different from previous one
18
+ if (width !== sidebarWidth) {
19
+ setSidebarWidth(width)
20
+ }
21
+ }
22
+
23
+ const containerElement = document.getElementById("sidebar-container")
24
+
25
+ if (!containerElement) return null
26
+
27
+ return createPortal((
28
+ <ResizableBox
29
+ id="sidebar"
30
+ width={sidebarWidth}
31
+ // style={{ position: "fixed", top: 45, bottom: 0, overflowX: "visible" }}
32
+ draggableOpts={{}}
33
+ handle={
34
+ <div
35
+ className="sidebar-resize-handle"
36
+ />
37
+ }
38
+ resizeHandles={["e"]}
39
+ axis="x"
40
+ minConstraints={[OPENED_MIN_WIDTH]}
41
+ maxConstraints={[800]}
42
+ onResize={onResize}
43
+ style={{overflow: "hidden"}}
44
+ >
45
+ {children}
46
+ </ResizableBox>
47
+ ), containerElement)
48
+ }
@@ -0,0 +1,22 @@
1
+ @import "helpers";
2
+
3
+ #sidebar-container {
4
+ position: fixed;
5
+ top: 45px;
6
+ left: 0;
7
+ max-height: calc(100vh - 45px);
8
+
9
+ .sidebar-resize-handle {
10
+ z-index: 3;
11
+
12
+ // transform: translateX(2px);
13
+ position: absolute;
14
+ right: -2px;
15
+ top: 0;
16
+ display: block;
17
+ height: 100%;
18
+ width: 4px;
19
+ cursor: ew-resize;
20
+ }
21
+
22
+ }
@@ -0,0 +1,19 @@
1
+ import {useContext} from "react"
2
+ import Offcanvas from "react-bootstrap/Offcanvas"
3
+
4
+ import ContentViewContext from "../../ContentView/ContentViewContext"
5
+
6
+
7
+ export const Body = ({children}) => {
8
+ const contentViewContext = useContext(ContentViewContext)
9
+
10
+ if (contentViewContext.isDocked) {
11
+ return children
12
+ }
13
+
14
+ return (
15
+ <Offcanvas.Body className="d-flex flex-column px-0 pb-0">
16
+ {children}
17
+ </Offcanvas.Body>
18
+ )
19
+ }
@@ -0,0 +1,23 @@
1
+ import {useContext} from "react"
2
+ import Offcanvas from "react-bootstrap/Offcanvas"
3
+
4
+ import {ContentViewContext} from "../../ContentView/ContentViewContext"
5
+
6
+
7
+ export const Header = ({children}) => {
8
+ const contentViewContext = useContext(ContentViewContext)
9
+
10
+ if (contentViewContext.isDocked) {
11
+ return (
12
+ <div className="bg-light" style={{height: 41}}>
13
+ {children}
14
+ </div>
15
+ )
16
+ }
17
+
18
+ return (
19
+ <Offcanvas.Header closeButton>
20
+ <Offcanvas.Title>{children}</Offcanvas.Title>
21
+ </Offcanvas.Header>
22
+ )
23
+ }
@@ -0,0 +1,46 @@
1
+ import Offcanvas from "react-bootstrap/Offcanvas"
2
+ import {ResizableBox} from "react-resizable"
3
+
4
+ import {useContentViewContext} from "../../ContentView/ContentViewContext"
5
+
6
+
7
+ export const Wrapper = ({onUpdateWidth, show, onHide, children}) => {
8
+ const contentViewContext = useContentViewContext()
9
+
10
+
11
+ const onResize = (e, data) => {
12
+ const val = data.size.width
13
+ onUpdateWidth(val)
14
+ }
15
+
16
+ if (contentViewContext.isDocked) {
17
+ return (
18
+ <ResizableBox
19
+ id="slideout-view-wrapper"
20
+ width={contentViewContext.sideItemViewWidth}
21
+ style={{position: "fixed", top: 45, right: 0, bottom: 0, overflowX: "visible"}}
22
+ draggableOpts={{}}
23
+ handle={<div className="slideout-view-resize-handle" />}
24
+ resizeHandles={["w"]}
25
+ axis="x"
26
+ minConstraints={[120]}
27
+ maxConstraints={[768]}
28
+ onResize={onResize}
29
+ >
30
+ <div className="h-100">{children}</div>
31
+ </ResizableBox>
32
+ )
33
+ } else {
34
+ return (
35
+ <Offcanvas
36
+ className="slideout-view-offcanvas"
37
+ placement="end"
38
+ backdropClassName="slideout-view-backdrop"
39
+ show={show}
40
+ onHide={onHide}
41
+ >
42
+ {children}
43
+ </Offcanvas>
44
+ )
45
+ }
46
+ }
@@ -0,0 +1,50 @@
1
+ import {memo, ReactNode, useEffect} from "react"
2
+ import {createPortal} from "react-dom"
3
+
4
+ import {useContentViewContext} from "../ContentView/ContentViewContext"
5
+
6
+ import {Wrapper} from "./components/Wrapper"
7
+ import {Header} from "./components/Header"
8
+ import {Body} from "./components/Body"
9
+
10
+ import "./slideout-container.scss"
11
+
12
+
13
+ export const SlideoutContainer = memo(
14
+ ({
15
+ children,
16
+ onHide,
17
+ isDocked = false,
18
+ header,
19
+ }: {
20
+ children: ReactNode;
21
+ onHide?: () => void;
22
+ isDocked?: boolean;
23
+ header?: ReactNode | string;
24
+ }) => {
25
+ const contentViewContext = useContentViewContext()
26
+
27
+ const {onUpdateSlideoutViewWidth: onUpdateWidth} = contentViewContext
28
+
29
+ useEffect(() => {
30
+ contentViewContext.setIsDocked(isDocked)
31
+
32
+ return () => contentViewContext.setIsDocked(false)
33
+ }, [isDocked])
34
+
35
+ // if (!contentViewContext.isDocked) return null
36
+ // if (contentViewContext.isCollapsed && contentViewContext.isDocked) return null
37
+
38
+ const containerElement = document.getElementById("slideout-container")
39
+ if (!containerElement) return null
40
+
41
+ return createPortal(
42
+ <Wrapper onUpdateWidth={onUpdateWidth} show={true} onHide={onHide}>
43
+ {header && <Header>{header}</Header>}
44
+
45
+ <Body>{children}</Body>
46
+ </Wrapper>,
47
+ containerElement,
48
+ )
49
+ },
50
+ )
@@ -0,0 +1,40 @@
1
+ @import "helpers";
2
+
3
+ #slideout-view-wrapper {
4
+ border-left: 1px solid $border-color;
5
+ }
6
+
7
+
8
+ .slideout-view-resize-handle {
9
+ z-index: 3;
10
+
11
+ position: absolute;
12
+ left: -2px;
13
+ top: 0;
14
+ display: block;
15
+ height: 100%;
16
+ width: 4px;
17
+ cursor: ew-resize;
18
+ }
19
+
20
+ .slideout-view-offcanvas.offcanvas {
21
+ transition: none;
22
+
23
+ .offcanvas-header {
24
+ height: 45px;
25
+ }
26
+
27
+ .offcanvas-title {
28
+ font-size: 1rem;
29
+ }
30
+
31
+ .offcanvas-body {
32
+ border-top: 1px solid $border-color;
33
+ font-size: $font-size-base;
34
+ }
35
+ }
36
+
37
+ .slideout-view-backdrop.offcanvas-backdrop {
38
+ transition-duration: 80ms;
39
+ color: $red-800;
40
+ }
@@ -0,0 +1,4 @@
1
+ export * from "./Header"
2
+ export * from "./SidebarContainer"
3
+ export * from "./SlideoutContainer"
4
+ export * from "./ContentView"