@rpcbase/client 0.183.0 → 0.184.0-treefloatview.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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rpcbase/client",
3
- "version": "0.183.0",
3
+ "version": "0.184.0-treefloatview.0",
4
4
  "scripts": {
5
5
  "test": "../../node_modules/.bin/wireit"
6
6
  },
@@ -90,6 +90,7 @@
90
90
  "i18next": "23.12.2",
91
91
  "i18next-chained-backend": "4.6.2",
92
92
  "i18next-resources-to-backend": "1.2.1",
93
+ "js-tree": "2.0.2",
93
94
  "lz-string": "1.5.0",
94
95
  "posthog-js": "1.147.0",
95
96
  "pouchdb-adapter-indexeddb": "8.0.1",
@@ -103,8 +104,8 @@
103
104
  "@babel/plugin-transform-modules-commonjs": "7.24.8",
104
105
  "@babel/plugin-transform-runtime": "7.24.7",
105
106
  "@babel/preset-react": "7.24.7",
106
- "@testing-library/react": "16.0.0",
107
107
  "@testing-library/dom": "10.4.0",
108
+ "@testing-library/react": "16.0.0",
108
109
  "axios": "1.7.2",
109
110
  "babel-jest": "29.7.0",
110
111
  "bluebird": "3.7.2",
@@ -0,0 +1,15 @@
1
+ @import "helpers";
2
+
3
+ #exp-float-view-backdrop {
4
+ // background-color: rgba(0, 0, 0, 0.2);
5
+ background-color: $black;
6
+ position: fixed;
7
+
8
+ // top: 700px;
9
+ // right: 1000px;
10
+ top: 0;
11
+ right: 0;
12
+ bottom: 0;
13
+ left: 0;
14
+ transition: opacity 200ms ease-in-out;
15
+ }
@@ -0,0 +1,120 @@
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
+ )
@@ -0,0 +1,45 @@
1
+ /* @flow */
2
+ import {useRef, useEffect} from "react"
3
+
4
+
5
+ const useBackdrop = (isOpenRef, onToggle) => {
6
+ const elRef = useRef(document.createElement("div"))
7
+
8
+ useEffect(() => {
9
+ const onTransitionEnd = (ev) => {
10
+ // hide backdrop when css opacity transition has ended
11
+ if (!isOpenRef.current) {
12
+ requestAnimationFrame(() => {
13
+ elRef.current.style.visibility = "hidden"
14
+ })
15
+ }
16
+ }
17
+
18
+ elRef.current.id = "exp-float-view-backdrop"
19
+ elRef.current.style.opacity = 0
20
+ elRef.current.style.visibility = "hidden"
21
+
22
+ elRef.current.addEventListener("transitionend", onTransitionEnd)
23
+
24
+ elRef.current.onclick = () => {
25
+ elRef.current.style.opacity = 0
26
+ onToggle()
27
+ }
28
+
29
+ document.body.appendChild(elRef.current)
30
+
31
+ return () => {
32
+ elRef.current.remove()
33
+ }
34
+ }, [])
35
+
36
+ const showBackdrop = () => {
37
+ // console.lgo
38
+ elRef.current.style.visibility = "visible"
39
+ elRef.current.style.opacity = 0.3
40
+ }
41
+
42
+ return {showBackdrop}
43
+ }
44
+
45
+ export default useBackdrop
@@ -0,0 +1,257 @@
1
+ /* @flow */
2
+ import * as React from "react"
3
+
4
+ import TreeModel from "./model"
5
+ import Node from "./node"
6
+
7
+ import "./tree.scss"
8
+
9
+
10
+ type Props = {|
11
+ id: String,
12
+ tree: Object,
13
+ paddingLeft: number,
14
+ renderNode: () => any,
15
+ onChange: ?Function,
16
+ style?: Object,
17
+ disableDragging?: boolean,
18
+ shouldHideNode?: (nodeProps) => boolean,
19
+ |}
20
+
21
+ type State = {|
22
+ tree: Object,
23
+ dragging: Object,
24
+ |}
25
+
26
+ export class Tree extends React.Component<Props, State> {
27
+ static defaultProps = {
28
+ paddingLeft: 10,
29
+ }
30
+
31
+ dragging: Object
32
+ _updated: boolean
33
+ _startX: number
34
+ _startY: number
35
+ _offsetX: number
36
+ _offsetY: number
37
+ _start: boolean
38
+
39
+ constructor(props: Props) {
40
+ super(props)
41
+ this.state = this.init(props)
42
+ }
43
+
44
+ UNSAFE_componentWillReceiveProps(nextProps: Props) {
45
+ if (!this._updated) {
46
+ this.setState(this.init(nextProps))
47
+ } else {
48
+ this._updated = false
49
+ }
50
+ }
51
+
52
+ init = (props: Props) => {
53
+ const tree = new TreeModel(props.tree)
54
+ tree.renderNode = props.renderNode
55
+ tree.updateNodesPosition()
56
+
57
+ return {
58
+ tree: tree,
59
+ dragging: {
60
+ id: null,
61
+ x: null,
62
+ y: null,
63
+ w: null,
64
+ h: null,
65
+ },
66
+ }
67
+ }
68
+
69
+ renderDraggingElement = () => {
70
+ const {tree, dragging} = this.state
71
+
72
+ if (dragging?.id) {
73
+ const draggingIndex = tree.getIndex(dragging.id)
74
+ const draggingStyles = {
75
+ top: dragging.y,
76
+ left: dragging.x,
77
+ width: dragging.w,
78
+ }
79
+
80
+ return (
81
+ <div className="m-draggable" style={draggingStyles}>
82
+ <Node
83
+ tree={tree}
84
+ index={draggingIndex}
85
+ paddingLeft={this.props.paddingLeft}
86
+ shouldHideNode={this.props.shouldHideNode}
87
+ />
88
+ </div>
89
+ )
90
+ }
91
+
92
+ return null
93
+ }
94
+
95
+ dragStart = (id: number, dom: HTMLElement, e: SyntheticMouseEvent<HTMLElement>) => {
96
+ if (e.button !== 0) return
97
+ this.dragging = {
98
+ id: id,
99
+ w: dom.offsetWidth,
100
+ h: dom.offsetHeight,
101
+ x: dom.offsetLeft,
102
+ y: dom.offsetTop,
103
+ }
104
+
105
+ this._startX = dom.offsetLeft
106
+ this._startY = dom.offsetTop
107
+ this._offsetX = e.clientX
108
+ this._offsetY = e.clientY
109
+ this._start = true
110
+
111
+ window.addEventListener("mousemove", this.dragMouseMove)
112
+ window.addEventListener("mouseup", this.dragEnd)
113
+ }
114
+
115
+ //
116
+ dragMouseMove = (e: SyntheticMouseEvent<HTMLElement>) => {
117
+ if (this._start) {
118
+ this.setState({
119
+ dragging: this.dragging,
120
+ })
121
+ this._start = false
122
+ }
123
+
124
+ const {tree} = this.state
125
+ const dragging = this.dragging
126
+ const paddingLeft = this.props.paddingLeft
127
+ let newIndex = null
128
+ let index = tree.getIndex(dragging.id)
129
+ const collapsed = index.node.collapsed
130
+
131
+ const _startX = this._startX
132
+ const _startY = this._startY
133
+ const _offsetX = this._offsetX
134
+ const _offsetY = this._offsetY
135
+
136
+ const pos = {
137
+ x: _startX + e.clientX - _offsetX,
138
+ y: _startY + e.clientY - _offsetY,
139
+ }
140
+
141
+ dragging.x = pos.x
142
+ dragging.y = pos.y
143
+
144
+ const diffX = dragging.x - paddingLeft / 2 - (index.left - 2) * paddingLeft
145
+ const diffY = dragging.y - dragging.h / 2 - (index.top - 2) * dragging.h
146
+
147
+ if (diffX < 0) {
148
+ // left
149
+ if (index.parent && !index.next) {
150
+ newIndex = tree.move(index.id, index.parent, "after")
151
+ }
152
+ } else if (diffX > paddingLeft) {
153
+ // right
154
+ if (index.prev) {
155
+ const prevNode = tree.getIndex(index.prev).node
156
+ if (!prevNode.collapsed && !prevNode.leaf) {
157
+ newIndex = tree.move(index.id, index.prev, "append")
158
+ }
159
+ }
160
+ }
161
+
162
+ if (newIndex) {
163
+ index = newIndex
164
+ newIndex.node.collapsed = collapsed
165
+ dragging.id = newIndex.id
166
+ }
167
+
168
+ if (diffY < 0) {
169
+ // up
170
+ const above = tree.getNodeByTop(index.top - 1)
171
+ newIndex = tree.move(index.id, above.id, "before")
172
+ } else if (diffY > dragging.h) {
173
+ // down
174
+ if (index.next) {
175
+ const below = tree.getIndex(index.next)
176
+ if (below.children && below.children.length && !below.node.collapsed) {
177
+ newIndex = tree.move(index.id, index.next, "prepend")
178
+ } else {
179
+ newIndex = tree.move(index.id, index.next, "after")
180
+ }
181
+ } else {
182
+ const below = tree.getNodeByTop(index.top + index.height)
183
+ if (below && below.parent !== index.id) {
184
+ if (below.children && below.children.length && !below.node.collapsed) {
185
+ newIndex = tree.move(index.id, below.id, "prepend")
186
+ } else {
187
+ newIndex = tree.move(index.id, below.id, "after")
188
+ }
189
+ }
190
+ }
191
+ }
192
+
193
+ if (newIndex) {
194
+ newIndex.node.collapsed = collapsed
195
+ dragging.id = newIndex.id
196
+ }
197
+
198
+ this.setState({
199
+ tree: tree,
200
+ dragging: dragging,
201
+ })
202
+ }
203
+
204
+ dragEnd = () => {
205
+ this.setState({
206
+ dragging: {
207
+ id: null,
208
+ x: null,
209
+ y: null,
210
+ w: null,
211
+ h: null,
212
+ },
213
+ })
214
+
215
+ this.change(this.state.tree)
216
+ window.removeEventListener("mousemove", this.dragMouseMove)
217
+ window.removeEventListener("mouseup", this.dragEnd)
218
+ }
219
+
220
+ change = (tree: Object) => {
221
+ this._updated = true
222
+ if (this.props.onChange) this.props.onChange(tree.obj)
223
+ }
224
+
225
+ toggleCollapse = (nodeId: number) => {
226
+ const {tree} = this.state
227
+ const index = tree.getIndex(nodeId)
228
+ const node = index.node
229
+ node.collapsed = !node.collapsed
230
+ tree.updateNodesPosition()
231
+
232
+ this.setState({tree})
233
+
234
+ this.change(tree)
235
+ }
236
+
237
+ render() {
238
+ const tree = this.state.tree
239
+ const dragging = this.state.dragging
240
+
241
+ return (
242
+ <div id={this.props.id} className="m-tree" style={this.props.style || {}}>
243
+ {this.renderDraggingElement()}
244
+ <Node
245
+ tree={tree}
246
+ index={tree.getIndex(1)}
247
+ key={1}
248
+ paddingLeft={this.props.paddingLeft}
249
+ onDragStart={this.props.disableDragging ? null : this.dragStart}
250
+ onCollapse={this.toggleCollapse}
251
+ dragging={dragging?.id}
252
+ shouldHideNode={this.props.shouldHideNode}
253
+ />
254
+ </div>
255
+ )
256
+ }
257
+ }
@@ -0,0 +1,71 @@
1
+ /* @flow */
2
+ /* eslint-disable */
3
+ const Tree = require("js-tree")
4
+
5
+ const proto = Tree.prototype
6
+
7
+ proto.updateNodesPosition = function () {
8
+ let top = 1
9
+ let left = 1
10
+ let root = this.getIndex(1)
11
+ let self = this
12
+
13
+ root.top = top++
14
+ root.left = left++
15
+
16
+ if (root.children && root.children.length) {
17
+ walk(root.children, root, left, root.node.collapsed)
18
+ }
19
+
20
+ function walk(children, parent, left, collapsed) {
21
+ var height = 1
22
+ children.forEach(function (id) {
23
+ var node = self.getIndex(id)
24
+ if (collapsed) {
25
+ node.top = null
26
+ node.left = null
27
+ } else {
28
+ node.top = top++
29
+ node.left = left
30
+ }
31
+
32
+ if (node.children && node.children.length) {
33
+ height += walk(node.children, node, left + 1, collapsed || node.node.collapsed)
34
+ } else {
35
+ node.height = 1
36
+ height += 1
37
+ }
38
+ })
39
+
40
+ if (parent.node.collapsed) parent.height = 1
41
+ else parent.height = height
42
+ return parent.height
43
+ }
44
+ }
45
+
46
+ proto.move = function (fromId, toId, placement) {
47
+ if (fromId === toId || toId === 1) return
48
+
49
+ var obj = this.remove(fromId)
50
+ var index = null
51
+
52
+ if (placement === "before") index = this.insertBefore(obj, toId)
53
+ else if (placement === "after") index = this.insertAfter(obj, toId)
54
+ else if (placement === "prepend") index = this.prepend(obj, toId)
55
+ else if (placement === "append") index = this.append(obj, toId)
56
+
57
+ // todo: perf
58
+ this.updateNodesPosition()
59
+ return index
60
+ }
61
+
62
+ proto.getNodeByTop = function (top) {
63
+ var indexes = this.indexes
64
+ for (var id in indexes) {
65
+ if (indexes.hasOwnProperty(id)) {
66
+ if (indexes[id].top === top) return indexes[id]
67
+ }
68
+ }
69
+ }
70
+
71
+ module.exports = Tree
@@ -0,0 +1,112 @@
1
+ /* @flow */
2
+ import React, {Component} from "react"
3
+
4
+
5
+ type Props = {|
6
+ paddingLeft: number,
7
+ index: Object,
8
+ tree: Object,
9
+ dragging?: ?boolean,
10
+ onDragStart?: ?Function,
11
+ onCollapse?: ?Function,
12
+ shouldHideNode?: () => boolean,
13
+ |}
14
+
15
+ type State = {}
16
+
17
+ export default class UITreeNode extends Component<Props, State> {
18
+ innerRef = React.createRef<HTMLDivElement>()
19
+
20
+ renderCollapse = () => {
21
+ const {index} = this.props
22
+
23
+ if (index.children && index.children.length) {
24
+ const {collapsed} = index.node
25
+
26
+ return (
27
+ <div
28
+ className={cx("tree-collapse", collapsed ? "caret-right" : "caret-down")}
29
+ onMouseDown={(e) => e.stopPropagation()}
30
+ onClick={this.handleCollapse}
31
+ />
32
+ )
33
+ }
34
+
35
+ return null
36
+ }
37
+
38
+ renderChildren = () => {
39
+ const {index, tree, dragging} = this.props
40
+
41
+ if (index.children && index.children.length) {
42
+ const childrenStyles = {
43
+ paddingLeft: this.props.paddingLeft,
44
+ }
45
+
46
+ return (
47
+ <div className="children" style={childrenStyles}>
48
+ {index.children.map((child) => {
49
+ const childIndex = tree.getIndex(child)
50
+
51
+ return (
52
+ <UITreeNode
53
+ tree={tree}
54
+ index={childIndex}
55
+ key={childIndex.id}
56
+ dragging={dragging}
57
+ paddingLeft={this.props.paddingLeft}
58
+ onCollapse={this.props.onCollapse}
59
+ onDragStart={this.props.onDragStart}
60
+ />
61
+ )
62
+ })}
63
+ </div>
64
+ )
65
+ }
66
+
67
+ return null
68
+ }
69
+
70
+ handleCollapse = (e: SyntheticMouseEvent<HTMLElement>) => {
71
+ e.stopPropagation()
72
+ const nodeId = this.props.index.id
73
+
74
+ if (this.props.onCollapse) {
75
+ this.props.onCollapse(nodeId)
76
+ }
77
+ }
78
+
79
+ handleMouseDown = (e: SyntheticMouseEvent<HTMLDivElement>) => {
80
+ const nodeId = this.props.index.id
81
+ const dom = this.innerRef.current
82
+
83
+ if (this.props.onDragStart) {
84
+ this.props.onDragStart(nodeId, dom, e)
85
+ }
86
+ }
87
+
88
+ render() {
89
+ const {tree, index, dragging, shouldHideNode} = this.props
90
+ const {node} = index
91
+ const styles = {}
92
+
93
+ if (typeof shouldHideNode === "function" && shouldHideNode(index)) {
94
+ return this.renderChildren()
95
+ }
96
+
97
+ return (
98
+ <div
99
+ className={cx("m-node", {
100
+ placeholder: index.id === dragging,
101
+ })}
102
+ style={styles}
103
+ >
104
+ <div className="d-flex flex-row align-items-center inner" ref={this.innerRef} onMouseDown={this.handleMouseDown}>
105
+ {this.renderCollapse()}
106
+ {tree.renderNode(node)}
107
+ </div>
108
+ {node.collapsed ? null : this.renderChildren()}
109
+ </div>
110
+ )
111
+ }
112
+ }
@@ -0,0 +1,98 @@
1
+ @import "helpers";
2
+
3
+ .f-no-select {
4
+ user-select: none;
5
+ }
6
+
7
+ .m-tree {
8
+ position: relative;
9
+ overflow: hidden;
10
+ user-select: none;
11
+
12
+ // Utility to remove scrollbars on browsers that constantly displays them
13
+ overflow-y: scroll;
14
+ scrollbar-width: none; /* Firefox */
15
+ -ms-overflow-style: none; /* Internet Explorer 10+ */
16
+
17
+ &::-webkit-scrollbar {
18
+ display: none;
19
+ width: 0;
20
+ height: 0;
21
+ }
22
+
23
+
24
+ .m-draggable {
25
+ position: absolute;
26
+ opacity: 0.8;
27
+ user-select: none;
28
+ }
29
+
30
+ .m-node.placeholder > * {
31
+ visibility: hidden;
32
+ }
33
+
34
+ .m-node.placeholder {
35
+ border: 1px dashed $primary;
36
+ background: $gray-500;
37
+
38
+ // TODO: why is cursor pointer required here
39
+ cursor: pointer;
40
+ }
41
+
42
+ .m-node .inner {
43
+ position: relative;
44
+ cursor: pointer;
45
+ padding-left: 8px;
46
+
47
+ & > *:first-child {
48
+ width: 100%;
49
+ }
50
+ }
51
+
52
+ .m-node .node {
53
+ display: inline-block;
54
+ white-space: nowrap;
55
+ /* font-size: 14px; */
56
+ width: 100%;
57
+ padding: 4px 5px;
58
+ /* @include font-monospace;
59
+
60
+ &.is-label {
61
+ @include font-default;
62
+ } */
63
+ }
64
+
65
+ .m-node .node.is-active {
66
+ background-color: $primary;
67
+ }
68
+
69
+
70
+ .m-node .tree-collapse {
71
+ position: absolute;
72
+ left: 0;
73
+ cursor: pointer;
74
+ }
75
+
76
+ .m-node .caret-right::before {
77
+ content: "\25B8";
78
+ display: block;
79
+
80
+ margin-top: 4px;
81
+ width: 12px;
82
+ height: 29px;
83
+ }
84
+
85
+ .m-node .caret-down::before {
86
+ display: block;
87
+ content: "\25BE";
88
+ margin-top: 4px;
89
+ width: 12px;
90
+ height: 29px;
91
+ }
92
+ }
93
+
94
+
95
+ /* .m-node .inner {
96
+ color: $gray-900;
97
+ font-size: 12px;
98
+ } */