@xyo-network/react-node-renderer 2.47.35

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.
Files changed (83) hide show
  1. package/LICENSE +165 -0
  2. package/README.md +13 -0
  3. package/dist/cjs/components/RelationalGraph.js +24 -0
  4. package/dist/cjs/components/RelationalGraph.js.map +1 -0
  5. package/dist/cjs/components/index.js +5 -0
  6. package/dist/cjs/components/index.js.map +1 -0
  7. package/dist/cjs/components/story/TestData.js +48 -0
  8. package/dist/cjs/components/story/TestData.js.map +1 -0
  9. package/dist/cjs/components/story/index.js +5 -0
  10. package/dist/cjs/components/story/index.js.map +1 -0
  11. package/dist/cjs/hooks/index.js +6 -0
  12. package/dist/cjs/hooks/index.js.map +1 -0
  13. package/dist/cjs/hooks/useCytoscapeElements.js +68 -0
  14. package/dist/cjs/hooks/useCytoscapeElements.js.map +1 -0
  15. package/dist/cjs/hooks/useCytoscapeOptions.js +54 -0
  16. package/dist/cjs/hooks/useCytoscapeOptions.js.map +1 -0
  17. package/dist/cjs/hooks/useIcons.js +33 -0
  18. package/dist/cjs/hooks/useIcons.js.map +1 -0
  19. package/dist/cjs/index.js +6 -0
  20. package/dist/cjs/index.js.map +1 -0
  21. package/dist/cjs/lib/index.js +5 -0
  22. package/dist/cjs/lib/index.js.map +1 -0
  23. package/dist/cjs/lib/utils.js +41 -0
  24. package/dist/cjs/lib/utils.js.map +1 -0
  25. package/dist/docs.json +18746 -0
  26. package/dist/esm/components/RelationalGraph.js +22 -0
  27. package/dist/esm/components/RelationalGraph.js.map +1 -0
  28. package/dist/esm/components/index.js +2 -0
  29. package/dist/esm/components/index.js.map +1 -0
  30. package/dist/esm/components/story/TestData.js +45 -0
  31. package/dist/esm/components/story/TestData.js.map +1 -0
  32. package/dist/esm/components/story/index.js +2 -0
  33. package/dist/esm/components/story/index.js.map +1 -0
  34. package/dist/esm/hooks/index.js +3 -0
  35. package/dist/esm/hooks/index.js.map +1 -0
  36. package/dist/esm/hooks/useCytoscapeElements.js +61 -0
  37. package/dist/esm/hooks/useCytoscapeElements.js.map +1 -0
  38. package/dist/esm/hooks/useCytoscapeOptions.js +50 -0
  39. package/dist/esm/hooks/useCytoscapeOptions.js.map +1 -0
  40. package/dist/esm/hooks/useIcons.js +28 -0
  41. package/dist/esm/hooks/useIcons.js.map +1 -0
  42. package/dist/esm/index.js +3 -0
  43. package/dist/esm/index.js.map +1 -0
  44. package/dist/esm/lib/index.js +2 -0
  45. package/dist/esm/lib/index.js.map +1 -0
  46. package/dist/esm/lib/utils.js +36 -0
  47. package/dist/esm/lib/utils.js.map +1 -0
  48. package/dist/types/components/RelationalGraph.d.ts +9 -0
  49. package/dist/types/components/RelationalGraph.d.ts.map +1 -0
  50. package/dist/types/components/index.d.ts +2 -0
  51. package/dist/types/components/index.d.ts.map +1 -0
  52. package/dist/types/components/story/TestData.d.ts +3 -0
  53. package/dist/types/components/story/TestData.d.ts.map +1 -0
  54. package/dist/types/components/story/index.d.ts +2 -0
  55. package/dist/types/components/story/index.d.ts.map +1 -0
  56. package/dist/types/hooks/index.d.ts +3 -0
  57. package/dist/types/hooks/index.d.ts.map +1 -0
  58. package/dist/types/hooks/useCytoscapeElements.d.ts +6 -0
  59. package/dist/types/hooks/useCytoscapeElements.d.ts.map +1 -0
  60. package/dist/types/hooks/useCytoscapeOptions.d.ts +3 -0
  61. package/dist/types/hooks/useCytoscapeOptions.d.ts.map +1 -0
  62. package/dist/types/hooks/useIcons.d.ts +3 -0
  63. package/dist/types/hooks/useIcons.d.ts.map +1 -0
  64. package/dist/types/index.d.ts +3 -0
  65. package/dist/types/index.d.ts.map +1 -0
  66. package/dist/types/lib/index.d.ts +2 -0
  67. package/dist/types/lib/index.d.ts.map +1 -0
  68. package/dist/types/lib/utils.d.ts +4 -0
  69. package/dist/types/lib/utils.d.ts.map +1 -0
  70. package/package.json +91 -0
  71. package/src/components/RelationalGraph.stories.tsx +86 -0
  72. package/src/components/RelationalGraph.tsx +35 -0
  73. package/src/components/index.ts +1 -0
  74. package/src/components/story/TestData.tsx +49 -0
  75. package/src/components/story/index.ts +1 -0
  76. package/src/hooks/index.ts +2 -0
  77. package/src/hooks/useCytoscapeElements.ts +67 -0
  78. package/src/hooks/useCytoscapeOptions.ts +57 -0
  79. package/src/hooks/useIcons.tsx +33 -0
  80. package/src/index.ts +2 -0
  81. package/src/lib/index.ts +1 -0
  82. package/src/lib/utils.ts +41 -0
  83. package/src/types/images.d.ts +5 -0
@@ -0,0 +1,35 @@
1
+ import { FlexBoxProps, FlexCol } from '@xylabs/react-flexbox'
2
+ import { WithChildren } from '@xylabs/react-shared'
3
+ import { useShareForwardedRef } from '@xyo-network/react-shared'
4
+ import cytoscape, { Core, CytoscapeOptions } from 'cytoscape'
5
+ import { forwardRef, useEffect, useState } from 'react'
6
+
7
+ export interface NodeRelationalGraph extends WithChildren<FlexBoxProps> {
8
+ options?: CytoscapeOptions
9
+ }
10
+
11
+ export const NodeRelationalGraph = forwardRef<HTMLDivElement, NodeRelationalGraph>(({ children, options, ...props }, ref) => {
12
+ const sharedRef = useShareForwardedRef(ref)
13
+ // TODO - likely a value to stick in context
14
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
15
+ const [cy, setCy] = useState<Core>()
16
+
17
+ useEffect(() => {
18
+ if (sharedRef) {
19
+ setCy(
20
+ cytoscape({
21
+ container: sharedRef.current,
22
+ ...options,
23
+ }),
24
+ )
25
+ }
26
+ }, [options, sharedRef])
27
+
28
+ return (
29
+ <FlexCol ref={sharedRef} {...props}>
30
+ {children}
31
+ </FlexCol>
32
+ )
33
+ })
34
+
35
+ NodeRelationalGraph.displayName = 'NodeRelationalGraph'
@@ -0,0 +1 @@
1
+ export * from './RelationalGraph'
@@ -0,0 +1,49 @@
1
+ import { CytoscapeOptions } from 'cytoscape'
2
+
3
+ const elements: CytoscapeOptions['elements'] = [
4
+ {
5
+ // node a
6
+ data: { id: 'a', name: 'element a' },
7
+ },
8
+ {
9
+ // node b
10
+ data: { id: 'b', name: 'element b' },
11
+ },
12
+ {
13
+ // node c
14
+ data: { id: 'c', name: 'element c' },
15
+ },
16
+ {
17
+ // edge ab
18
+ data: { id: 'ab', source: 'a', target: 'b' },
19
+ },
20
+ {
21
+ // edge ac
22
+ data: { id: 'ac', source: 'a', target: 'c' },
23
+ },
24
+ ]
25
+
26
+ const style: CytoscapeOptions['style'] = [
27
+ {
28
+ selector: 'node',
29
+ style: {
30
+ label: 'data(id)',
31
+ },
32
+ },
33
+
34
+ {
35
+ selector: 'edge',
36
+ style: {
37
+ 'curve-style': 'bezier',
38
+ 'line-color': '#ccc',
39
+ 'target-arrow-color': '#ccc',
40
+ 'target-arrow-shape': 'triangle',
41
+ width: 3,
42
+ },
43
+ },
44
+ ]
45
+
46
+ export const options: CytoscapeOptions = {
47
+ elements,
48
+ style,
49
+ }
@@ -0,0 +1 @@
1
+ export * from './TestData'
@@ -0,0 +1,2 @@
1
+ export * from './useCytoscapeElements'
2
+ export * from './useCytoscapeOptions'
@@ -0,0 +1,67 @@
1
+ import { useAsyncEffect } from '@xylabs/react-shared'
2
+ import { ModuleWrapper } from '@xyo-network/module'
3
+ import { NodeWrapper } from '@xyo-network/node'
4
+ import { useProvidedWrappedNode } from '@xyo-network/react-node'
5
+ import { CytoscapeOptions } from 'cytoscape'
6
+ import { useState } from 'react'
7
+
8
+ import { parseModuleType } from '../lib'
9
+
10
+ /**
11
+ * Note: Relies on describe but could eventually be converted to a discover call
12
+ * Logic would be similar to what the bridge does
13
+ */
14
+ export const useCytoscapeElements = () => {
15
+ const [node] = useProvidedWrappedNode()
16
+ const [elements, setElements] = useState<CytoscapeOptions['elements']>()
17
+
18
+ useAsyncEffect(
19
+ // eslint-disable-next-line react-hooks/exhaustive-deps
20
+ async () => {
21
+ if (node) {
22
+ try {
23
+ const newElements = []
24
+ const wrapper = NodeWrapper.wrap(node)
25
+ const description = await wrapper?.describe()
26
+ const rootNodeId = description.name ?? description.address.substring(0, 6)
27
+ newElements.push({
28
+ data: { id: rootNodeId, type: parseModuleType(description.queries) },
29
+ })
30
+ const children = description.children
31
+ await Promise.all(
32
+ (children ?? [])?.map(async (address) => {
33
+ const [result] = await wrapper.resolveWrapped(ModuleWrapper, { address: [address] })
34
+ try {
35
+ const description = await result.describe()
36
+ const newNodeId = description.name ?? description.address.substring(0, 6)
37
+ const newNode = {
38
+ data: {
39
+ id: newNodeId,
40
+ type: parseModuleType(description.queries),
41
+ },
42
+ }
43
+ newElements.push(newNode)
44
+ const newEdge = {
45
+ data: {
46
+ id: `${rootNodeId}/${newNodeId}`,
47
+ source: rootNodeId,
48
+ target: newNodeId,
49
+ },
50
+ }
51
+ newElements.push(newEdge)
52
+ } catch (e) {
53
+ console.error(e, result)
54
+ }
55
+ }),
56
+ )
57
+ setElements(newElements)
58
+ } catch (e) {
59
+ console.error(e)
60
+ }
61
+ }
62
+ },
63
+ [node],
64
+ )
65
+
66
+ return elements
67
+ }
@@ -0,0 +1,57 @@
1
+ import { useTheme } from '@mui/material'
2
+ import { CytoscapeOptions } from 'cytoscape'
3
+ import { useMemo } from 'react'
4
+
5
+ import { CyNodeIcons, useIcons } from './useIcons'
6
+
7
+ export const useCytoscapeOptions = (elements: CytoscapeOptions['elements']) => {
8
+ const theme = useTheme()
9
+ const icons = useIcons()
10
+
11
+ const options = useMemo<CytoscapeOptions>(
12
+ () => ({
13
+ elements,
14
+ layout: {
15
+ minNodeSpacing: 75,
16
+ name: 'concentric',
17
+ },
18
+ style: [
19
+ {
20
+ selector: 'node[id]',
21
+ style: {
22
+ color: theme.palette.text.primary,
23
+ 'font-family': 'Lexend Deca, Helvetica, sans-serif',
24
+ 'font-size': 14,
25
+ 'text-margin-y': -5,
26
+ },
27
+ },
28
+ {
29
+ selector: 'node',
30
+ style: {
31
+ 'background-color': theme.palette.primary.main,
32
+ 'background-height': '75%',
33
+ // TODO - make dynamic
34
+ 'background-image': (elem) => icons[elem.data('type') as CyNodeIcons],
35
+ 'background-image-smoothing': 'yes',
36
+ 'background-width': '75%',
37
+ label: 'data(id)',
38
+ },
39
+ },
40
+ {
41
+ selector: 'edge',
42
+ style: {
43
+ 'curve-style': 'bezier',
44
+ 'line-color': theme.palette.divider,
45
+ 'line-opacity': 0.1,
46
+ 'target-arrow-color': theme.palette.divider,
47
+ 'target-arrow-shape': 'triangle',
48
+ width: 3,
49
+ },
50
+ },
51
+ ],
52
+ }),
53
+ [elements, icons, theme.palette.divider, theme.palette.primary.main, theme.palette.text.primary],
54
+ )
55
+
56
+ return options
57
+ }
@@ -0,0 +1,33 @@
1
+ import GridViewRoundedIcon from '@mui/icons-material/GridViewRounded'
2
+ import HubIcon from '@mui/icons-material/Hub'
3
+ import QuestionMarkRoundedIcon from '@mui/icons-material/QuestionMarkRounded'
4
+ import VisibilityRoundedIcon from '@mui/icons-material/VisibilityRounded'
5
+ import { SvgIconTypeMap, useTheme } from '@mui/material'
6
+ import { OverridableComponent } from '@mui/material/OverridableComponent'
7
+ import { useMemo } from 'react'
8
+
9
+ import { encodeSvg } from '../lib'
10
+
11
+ export type CyNodeIcons = 'node' | 'archivist' | 'diviner' | 'module'
12
+
13
+ // eslint-disable-next-line @typescript-eslint/ban-types
14
+ const CyIconSet: Record<CyNodeIcons, OverridableComponent<SvgIconTypeMap<{}, 'svg'>>> = {
15
+ archivist: GridViewRoundedIcon,
16
+ diviner: VisibilityRoundedIcon,
17
+ module: QuestionMarkRoundedIcon,
18
+ node: HubIcon,
19
+ }
20
+
21
+ export const useIcons = () => {
22
+ const theme = useTheme()
23
+ const icons = useMemo(() => {
24
+ const iconMap: Record<CyNodeIcons, string> = { archivist: '', diviner: '', module: '', node: '' }
25
+ return Object.entries(CyIconSet).reduce((acc, [name, IconComponent]) => {
26
+ const icon = <IconComponent fontSize="small" />
27
+ acc[name as CyNodeIcons] = encodeSvg(icon, theme.palette.getContrastText(theme.palette.text.primary))
28
+ return acc
29
+ }, iconMap)
30
+ }, [theme.palette])
31
+
32
+ return icons
33
+ }
package/src/index.ts ADDED
@@ -0,0 +1,2 @@
1
+ export * from './components'
2
+ export * from './hooks'
@@ -0,0 +1 @@
1
+ export * from './utils'
@@ -0,0 +1,41 @@
1
+ import { ReactElement } from 'react'
2
+ // eslint-disable-next-line import/no-internal-modules
3
+ import { renderToStaticMarkup } from 'react-dom/server'
4
+
5
+ const dataUri = 'data:image/svg+xml,'
6
+
7
+ export const encodeSvg = (reactElement: ReactElement, color?: string) => {
8
+ const svgString = renderToStaticMarkup(reactElement)
9
+
10
+ const doc = new DOMParser().parseFromString(svgString, 'text/html')
11
+ const svgElement = doc.getElementsByTagName('svg')[0]
12
+ if (svgElement) {
13
+ svgElement.setAttribute('xmlns', 'http://www.w3.org/2000/svg')
14
+ svgElement.setAttribute('height', '100')
15
+ svgElement.style.fill = color ?? 'black'
16
+ }
17
+
18
+ return `${dataUri}${window.encodeURIComponent(svgElement.outerHTML)}`
19
+ }
20
+
21
+ export const parseModuleType = (queries?: string[]) => {
22
+ let type
23
+ if (queries) {
24
+ for (let i = 0; i < queries.length; i++) {
25
+ if (queries[i].includes('archivist')) {
26
+ type = 'archivist'
27
+ break
28
+ }
29
+ if (queries[i].includes('diviner')) {
30
+ type = 'diviner'
31
+ break
32
+ }
33
+ if (queries[i].includes('node')) {
34
+ type = 'node'
35
+ break
36
+ }
37
+ type = 'module'
38
+ }
39
+ return type
40
+ }
41
+ }
@@ -0,0 +1,5 @@
1
+ declare module '*.png'
2
+ declare module '*.jpg'
3
+ declare module '*.svg'
4
+ declare module '*.gif'
5
+ declare module '*.webp'