Skip to content

Commit e1ee906

Browse files
committed
2 parents 98dca75 + fa1e0b1 commit e1ee906

File tree

12 files changed

+318
-17
lines changed

12 files changed

+318
-17
lines changed
Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
2+
function pathMatch(pattern: string, path: string) {
3+
let regStr = ''
4+
for (let i = 0; i < pattern.length; i++) {
5+
const ch = pattern[i]
6+
if ('^$+()[]{}'.includes(ch)) {
7+
continue
8+
}
9+
if (ch === '?') {
10+
regStr += '[\\S]'
11+
continue
12+
}
13+
if (ch === '*') {
14+
if (pattern[i + 1] === ch) {
15+
regStr += `${regStr.at(-1) === '/' ? '?' : '' }[\\S]{0,}`
16+
i++
17+
} else {
18+
regStr += '[^/]{0,}'
19+
}
20+
continue
21+
}
22+
if (ch === '.') {
23+
regStr += '\\' + ch
24+
continue
25+
}
26+
regStr += ch
27+
}
28+
return new RegExp(`^${regStr}$`).test(path)
29+
}
30+
31+
// function assertTrue(value) { console.log(value === true) }
32+
// function assertFalse(value) { console.log(value === false) }
33+
34+
// assertTrue(pathMatch("/foo/bar/**x{}x[]", "/foo/bar")) ;
35+
36+
// assertTrue(pathMatch("https://jex.im/regulex/", "https://jex.im/regulex/"));
37+
// assertTrue(pathMatch("com/**/*.jsp", "com/abc.jsp"));
38+
// assertTrue(pathMatch("com/{ filename: \\w+ }.jsp", "com/abc.jsp2"));
39+
40+
// // test exact matching
41+
// assertTrue(pathMatch("test", "test"));
42+
// assertTrue(pathMatch("/test", "/test"));
43+
// assertTrue(pathMatch("http://example.org", "http://example.org")); // SPR-14141
44+
// assertFalse(pathMatch("/test.jpg", "test.jpg"));
45+
// assertFalse(pathMatch("test", "/test"));
46+
// assertFalse(pathMatch("/test", "test"));
47+
// // test matching with ?'s
48+
// assertTrue(pathMatch("t?st", "test"));
49+
// assertTrue(pathMatch("??st", "test"));
50+
// assertTrue(pathMatch("tes?", "test"));
51+
// assertTrue(pathMatch("te??", "test"));
52+
// assertTrue(pathMatch("?es?", "test"));
53+
// assertFalse(pathMatch("tes?", "tes"));
54+
// assertFalse(pathMatch("tes?", "testt"));
55+
// assertFalse(pathMatch("tes?", "tsst"));
56+
// // test matching with *'s
57+
// assertFalse(pathMatch("*.*", "tsttst"));
58+
// assertTrue(pathMatch("*", "test"));
59+
// assertTrue(pathMatch("test*", "test"));
60+
// assertTrue(pathMatch("test*", "testTest"));
61+
// assertTrue(pathMatch("test/*", "test/Test"));
62+
// assertTrue(pathMatch("test/*", "test/t"));
63+
// assertTrue(pathMatch("test/*", "test/"));
64+
// assertTrue(pathMatch("*test*", "AnothertestTest"));
65+
// assertTrue(pathMatch("*test", "Anothertest"));
66+
// assertTrue(pathMatch("*.*", "test."));
67+
// assertTrue(pathMatch("*.*", "test.test"));
68+
// assertTrue(pathMatch("*.*", "test.test.test"));
69+
// assertTrue(pathMatch("test*aaa", "testblaaaa"));
70+
// assertFalse(pathMatch("test*", "tst"));
71+
// assertFalse(pathMatch("test*", "tsttest"));
72+
// assertFalse(pathMatch("test*", "test/"));
73+
// assertFalse(pathMatch("test*", "test/t"));
74+
// assertFalse(pathMatch("test/*", "test"));
75+
// assertFalse(pathMatch("*test*", "tsttst"));
76+
// assertFalse(pathMatch("*test", "tsttst"));
77+
// assertFalse(pathMatch("test*aaa", "test"));
78+
// assertFalse(pathMatch("test*aaa", "testblaaab"));
79+
// // test matching with ?'s and /'s
80+
// assertTrue(pathMatch("/?", "/a"));
81+
// assertTrue(pathMatch("/?/a", "/a/a"));
82+
// assertTrue(pathMatch("/a/?", "/a/b"));
83+
// assertTrue(pathMatch("/??/a", "/aa/a"));
84+
// assertTrue(pathMatch("/a/??", "/a/bb"));
85+
// assertTrue(pathMatch("/?", "/a"));
86+
// // test matching with **'s
87+
// assertTrue(pathMatch("/foo/bar/**", "/foo/bar")) ;
88+
// assertTrue(pathMatch("/**", "/testing/testing"));
89+
// assertTrue(pathMatch("/*/**", "/testing/testing"));
90+
// assertTrue(pathMatch("/**/*", "/testing/testing"));
91+
// assertTrue(pathMatch("/bla/**/bla", "/bla/testing/testing/bla"));
92+
// assertTrue(pathMatch("/bla/**/bla", "/bla/testing/testing/bla/bla"));
93+
// assertTrue(pathMatch("/**/test", "/bla/bla/test"));
94+
// assertTrue(pathMatch("/bla/**/**/bla", "/bla/bla/bla/bla/bla/bla"));
95+
// assertTrue(pathMatch("/bla*bla/test", "/blaXXXbla/test"));
96+
// assertTrue(pathMatch("/*bla/test", "/XXXbla/test"));
97+
// assertFalse(pathMatch("/bla*bla/test", "/blaXXXbl/test"));
98+
// assertFalse(pathMatch("/*bla/test", "XXXblab/test"));
99+
// assertFalse(pathMatch("/*bla/test", "XXXbl/test"));
100+
// assertFalse(pathMatch("/????", "/bala/bla"));
101+
// assertFalse(pathMatch("/**/*bla", "/bla/bla/bla/bbb"));
102+
// assertTrue(pathMatch("/*bla*/**/bla/**", "/XXXblaXXXX/testing/testing/bla/testing/testing/"));
103+
// assertTrue(pathMatch("/*bla*/**/bla/*", "/XXXblaXXXX/testing/testing/bla/testing"));
104+
// assertTrue(pathMatch("/*bla*/**/bla/**", "/XXXblaXXXX/testing/testing/bla/testing/testing"));
105+
// assertTrue(pathMatch("/*bla*/**/bla/**", "/XXXblaXXXX/testing/testing/bla/testing/testing.jpg"));
106+
// assertTrue(pathMatch("*bla*/**/bla/**", "XXXblaXXXX/testing/testing/bla/testing/testing/"));
107+
// assertTrue(pathMatch("*bla*/**/bla/*", "XXXblaXXXX/testing/testing/bla/testing"));
108+
// assertTrue(pathMatch("*bla*/**/bla/**", "XXXblaXXXX/testing/testing/bla/testing/testing"));
109+
// assertFalse(pathMatch("*bla*/**/bla/*", "XXXblaXXXX/testing/testing/bla/testing/testing"));
110+
// assertFalse(pathMatch("/x/x/**/bla", "/x/x/x/"));
111+
// assertTrue(pathMatch("", ""));
112+
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
2+
3+
/**
4+
* 开头结尾不能有-
5+
* 不能有两个以上的--
6+
* 数字、字母和-
7+
* 2-40个字符
8+
*/
9+
const reg = /^(?!-)(?!.*-$)(?!.*--.*)[a-z\d-]{2,40}$/i
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
function isEmptyNode(value) {
2+
return value == null
3+
}
4+
/**
5+
* 根节点为空直接返回false
6+
* 否则维护一个队列,层序遍历
7+
* 1. 如果左右子节点都不为空,该节点出队列,子节点入队列
8+
* 2. 如果左子节点为空,右子节点不为空,则判定为false
9+
* 3. 如果右子节点为空,则队列的后续节点必须都为叶子节点才判定为完全二叉树
10+
*/
11+
function isComplete(tree) {
12+
if (! tree?.length) return false
13+
14+
const queue = []
15+
queue.push({ index: 0, value: tree[0] })
16+
17+
while (queue.length) {
18+
const { index, value } = queue[0]
19+
const left = tree[index * 2 + 1]
20+
const right = tree[index * 2 + 2]
21+
if (!isEmptyNode(left) && !isEmptyNode(right)) {
22+
queue.shift()
23+
queue.push({ index: index * 2 + 1, value: left })
24+
queue.push({ index: index * 2 + 2, value: right })
25+
continue
26+
}
27+
if (isEmptyNode(left) && !isEmptyNode(right)) {
28+
return false
29+
}
30+
// 判断后续节点为叶子节点
31+
while (queue.length) {
32+
const { index, value } = queue[0]
33+
const left = tree[index * 2 + 1]
34+
const right = tree[index * 2 + 2]
35+
if (!isEmptyNode(left) || !isEmptyNode(right)) {
36+
return false
37+
}
38+
queue.shift()
39+
}
40+
return true
41+
}
42+
43+
return false
44+
}
45+
46+
/**
47+
* 1
48+
* / \
49+
* 2 3
50+
* / \
51+
* 4 5
52+
*/
53+
console.log(isComplete([1, 2, 3, 4, 5, null, null]) === true)
54+
/**
55+
* 1
56+
* / \
57+
* 2 3
58+
* / /
59+
* 4 5
60+
*/
61+
console.log(isComplete([1, 2, 3, 4, null, 5, null]) === false)

packages/test-react/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
"react-dom": "^18.0.0",
2323
"react-draggable": "^4.4.5",
2424
"react-markdown": "^8.0.3",
25+
"react-node-graph": "^1.1.1",
2526
"react-router-dom": "^6.3.0",
2627
"react-scripts": "3.4.1",
2728
"reflect-metadata": "^0.1.13",

packages/test-react/src/GraphNode测试/components/Dot.tsx

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1+
import { useMemo, useRef, useState } from "react"
12
import styled from "styled-components"
3+
import { clsx } from "../../utils"
24

35
const Wrapper = styled.div`
46
width: 8px;
@@ -18,9 +20,48 @@ export default function Dot(props: {
1820
className?: string
1921
style?: React.CSSProperties
2022
onClick?: () => void
23+
isFirstNode: boolean
2124
}) {
25+
const wrapperRef = useRef<HTMLDivElement>(null)
26+
const [startPoint, setStartPoint] = useState<[number, number] | null>(null)
27+
const [endPoint, setEndPoint] = useState<[number, number] | null>(null)
28+
const [active, setActive] = useState(false)
29+
30+
const d = useMemo(() => {
31+
if (startPoint && endPoint) {
32+
const [x1, y1] = startPoint
33+
const [x2, y2] = endPoint
34+
const d = (x1 + x2) / 2
35+
return `M ${x1} ${y1} C${d} ${y1} ${d} ${y2} ${x2} ${y2} l-1 0 l-5 -5 m5 5 l-5 5`
36+
}
37+
return ''
38+
}, [startPoint, endPoint])
39+
2240
return (
23-
<Wrapper onClick={props.onClick} style={props.style} className={props.className}>
41+
<Wrapper ref={wrapperRef} onClick={() => {
42+
if (! props.isFirstNode) {
43+
setActive(true)
44+
if (wrapperRef.current) {
45+
const { x, y } = wrapperRef.current.getBoundingClientRect()
46+
setStartPoint([x, y])
47+
window.addEventListener('mousemove', (e) => {
48+
setEndPoint([e.clientX, e.clientY])
49+
})
50+
}
51+
}
52+
53+
props.onClick?.()
54+
}} style={props.style} className={clsx({
55+
[props.className!]: true,
56+
active,
57+
})}>
58+
{
59+
!!startPoint && !! endPoint && (
60+
<svg style={{ position: 'absolute', left: 0, top: 0 }}>
61+
<path d={d} strokeWidth={2} stroke="green" fill="transparent"></path>
62+
</svg>
63+
)
64+
}
2465
</Wrapper>
2566
)
2667
}

packages/test-react/src/GraphNode测试/components/GraphNodePanel.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ const config = [
2828

2929
export default function GraphNodePanel(props: {
3030
spriteProps: SpriteProps
31+
isFirstNode: boolean
3132
onLink: (props: {
3233
key: string
3334
type: 'input' | 'output'
@@ -43,7 +44,7 @@ export default function GraphNodePanel(props: {
4344
{
4445
config.map(item => {
4546
return (
46-
<Tile onLink={(type) => {
47+
<Tile isFirstNode={props.isFirstNode} onLink={(type) => {
4748
props.onLink({
4849
key: item.key,
4950
type,

packages/test-react/src/GraphNode测试/components/Tile.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,18 +27,19 @@ const Wrapper = styled.div`
2727
export default function Tile(props: {
2828
type: 'input' | 'output'
2929
children: React.ReactNode
30+
isFirstNode: boolean
3031
onLink: (type: 'input'| 'output') => void
3132
}) {
3233

3334
const isLeft = useMemo(() => props.type === 'input', [props.type])
3435

3536
return (
3637
<Wrapper className={clsx({ [props.type]: true })}>
37-
{ isLeft && <Dot onClick={() => {
38+
{ isLeft && <Dot isFirstNode={props.isFirstNode} onClick={() => {
3839
props.onLink('input')
3940
}} className="dot" /> }
4041
<div className="content">{props.children}</div>
41-
{ !isLeft && <Dot onClick={() => {
42+
{ !isLeft && <Dot isFirstNode={props.isFirstNode} onClick={() => {
4243
props.onLink('output')
4344
}} className="dot" /> }
4445
</Wrapper>

packages/test-react/src/GraphNode测试/index.tsx

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
import { Button, Col, Input, InputNumber, Row, Select } from "antd"
22
import { useEffect, useState } from "react"
33
import styled from "styled-components"
4-
import GraphNodePanel from "./components/GraphNodePanel"
54
import { Scene, SpriteProps } from "./core/model"
65
import { analysis } from "./core/xnode"
76
import Interpolation from "./core/xnode/InterpolationNode"
87
import { OperationNode } from "./core/xnode/OperationNode"
8+
// @ts-ignore
9+
import ReactNodeGraph from 'react-node-graph'
910

1011
const Wrapper = styled.div`
1112
width: 400px;
@@ -16,12 +17,24 @@ Scene.registXNode(OperationNode)
1617
Scene.registXNode(Interpolation)
1718
const scene = new Scene()
1819

20+
const exampleGraph = {
21+
"nodes":[
22+
{"nid":0,"type":"Timer","x":89,"y":82,"fields":{"in":[{"name":"reset"},{"name":"pause"},{"name":"max"}],"out":[{"name":"out"}]}},
23+
{"nid":1,"type":"MathMult","x":284,"y":82,"fields":{"in":[{"name":"in"},{"name":"factor"}],"out":[{"name":"out"}]}},
24+
{"nid":2,"type":"Vector3","x":486,"y":188,"fields":{"in":[{"name":"xyz"},{"name":"x"},{"name":"y"},{"name":"z"}],"out":[{"name":"xyz"},{"name":"x"},{"name":"y"},{"name":"z"}]}}
25+
],
26+
"connections":[
27+
// {"from_node": nid,"from":"field_name","to_node": nid,"to":"field_name"},
28+
]
29+
}
30+
1931
export default function GraphNodeTest() {
2032

2133
const [schema, setSchema] = useState(analysis(OperationNode))
2234
const [result, setResult] = useState(0)
2335
const [type, setType] = useState('')
2436
const [data, setData] = useState<SpriteProps[]>([])
37+
const [isFirstNode, setIsFirstNode] = useState(false)
2538

2639
return (
2740
<Wrapper>
@@ -36,15 +49,19 @@ export default function GraphNodeTest() {
3649
scene.add(type)
3750
setData(scene.toJson())
3851
}}>添加</Button>
39-
{
52+
<ReactNodeGraph data={exampleGraph} ></ReactNodeGraph>
53+
{/* {
4054
data.map((item) => {
4155
return (
42-
<GraphNodePanel onLink={(props) => {
56+
<GraphNodePanel isFirstNode={isFirstNode} onLink={(props) => {
57+
if (! isFirstNode) {
58+
setIsFirstNode(true)
59+
}
4360
console.log(props)
4461
}} spriteProps={item} key={item.id} />
4562
)
4663
})
47-
}
64+
} */}
4865
{/* <div>
4966
<svg>
5067
<path d={'M 0 0 C50 0 50 100 100 100 l-1 0 l-5 -5 m5 5 l-5 5'} strokeWidth={2} stroke="red" fill="transparent" />
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import { useCallback, useEffect, useRef } from "react"
2+
3+
export default function useEvent(callback) {
4+
const callbackRef = useRef(null);
5+
6+
useEffect(() => {
7+
callbackRef.current = callback;
8+
})
9+
10+
const event = useCallback((...args) => {
11+
if (callbackRef.current) {
12+
callbackRef.current.apply(null, args);
13+
}
14+
}, []);
15+
16+
return event;
17+
}
Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
1-
import { useCallback, useEffect, useRef } from "react"
1+
import { useRef } from 'react'
22

3-
export default function useRefCallback<T extends (...args: any[]) => any>(fn: T, dependencies: any[]) {
4-
const ref = useRef<T>(fn)
5-
// 每次调用的时候,fn 都是一个全新的函数,函数中的变量有自己的作用域
6-
// 当依赖改变的时候,传入的 fn 中的依赖值也会更新,这时更新 ref 的指向为新传入的 fn
7-
useEffect(() => (ref.current = fn), [fn, ...dependencies])
3+
export default function useEventCallback<T>(handler?: (value: T) => void): (value: T) => void {
4+
const callbackRef = useRef(handler);
5+
const fn = useRef((value: T) => {
6+
callbackRef.current && callbackRef.current(value);
7+
});
8+
callbackRef.current = handler;
89

9-
return useCallback(() => ref.current(), [ref]) as T
10-
}
10+
return fn.current;
11+
}

0 commit comments

Comments
 (0)