@things-factory/integration-ui 7.0.31 → 7.0.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.
- package/client/analysis/graph-data.ts +34 -0
- package/client/analysis/graph-viewer-old.ts +1097 -0
- package/client/analysis/graph-viewer-style.ts +5 -1
- package/client/analysis/graph-viewer.ts +245 -942
- package/client/analysis/node.ts +73 -0
- package/client/analysis/relationship.ts +19 -0
- package/client/analysis/utils.ts +41 -0
- package/client/pages/integration-analysis.ts +160 -71
- package/dist-client/analysis/graph-data.d.ts +36 -0
- package/dist-client/analysis/graph-data.js +2 -0
- package/dist-client/analysis/graph-data.js.map +1 -0
- package/dist-client/analysis/graph-viewer-old.d.ts +110 -0
- package/dist-client/analysis/graph-viewer-old.js +808 -0
- package/dist-client/analysis/graph-viewer-old.js.map +1 -0
- package/dist-client/analysis/graph-viewer-style.js +5 -1
- package/dist-client/analysis/graph-viewer-style.js.map +1 -1
- package/dist-client/analysis/graph-viewer.d.ts +25 -99
- package/dist-client/analysis/graph-viewer.js +189 -703
- package/dist-client/analysis/graph-viewer.js.map +1 -1
- package/dist-client/analysis/node.d.ts +4 -0
- package/dist-client/analysis/node.js +59 -0
- package/dist-client/analysis/node.js.map +1 -0
- package/dist-client/analysis/relationship.d.ts +4 -0
- package/dist-client/analysis/relationship.js +13 -0
- package/dist-client/analysis/relationship.js.map +1 -0
- package/dist-client/analysis/utils.d.ts +20 -0
- package/dist-client/analysis/utils.js +31 -0
- package/dist-client/analysis/utils.js.map +1 -0
- package/dist-client/pages/integration-analysis.d.ts +8 -3
- package/dist-client/pages/integration-analysis.js +153 -70
- package/dist-client/pages/integration-analysis.js.map +1 -1
- package/dist-client/tsconfig.tsbuildinfo +1 -1
- package/dist-server/tsconfig.tsbuildinfo +1 -1
- package/package.json +6 -6
| @@ -0,0 +1,73 @@ | |
| 1 | 
            +
            import { Node } from './graph-data'
         | 
| 2 | 
            +
            import * as d3 from 'd3'
         | 
| 3 | 
            +
            import { class2color, class2darkenColor } from './utils'
         | 
| 4 | 
            +
             | 
| 5 | 
            +
            export function appendNodeToGraph(
         | 
| 6 | 
            +
              svgNodes: d3.Selection<SVGGElement, unknown, HTMLElement, any>, // 타입 지정
         | 
| 7 | 
            +
              nodes: Node[],
         | 
| 8 | 
            +
              options: any
         | 
| 9 | 
            +
            ) {
         | 
| 10 | 
            +
              const nodeEnter = svgNodes
         | 
| 11 | 
            +
                .selectAll<SVGGElement, Node>('.node')
         | 
| 12 | 
            +
                .data(nodes, d => d.id)
         | 
| 13 | 
            +
                .enter()
         | 
| 14 | 
            +
                .append('g')
         | 
| 15 | 
            +
                .attr('class', d => {
         | 
| 16 | 
            +
                  let classes = 'node'
         | 
| 17 | 
            +
                  if (d.icon) classes += ' node-icon'
         | 
| 18 | 
            +
                  if (d.image) classes += ' node-image'
         | 
| 19 | 
            +
                  if (options.highlight) {
         | 
| 20 | 
            +
                    options.highlight.forEach((highlight: any) => {
         | 
| 21 | 
            +
                      if (d.labels[0] === highlight.class && d.properties[highlight.property] === highlight.value) {
         | 
| 22 | 
            +
                        classes += ' node-highlighted'
         | 
| 23 | 
            +
                      }
         | 
| 24 | 
            +
                    })
         | 
| 25 | 
            +
                  }
         | 
| 26 | 
            +
                  return classes
         | 
| 27 | 
            +
                })
         | 
| 28 | 
            +
                .call(
         | 
| 29 | 
            +
                  d3
         | 
| 30 | 
            +
                    .drag<SVGGElement, Node>() // 제네릭 타입 지정
         | 
| 31 | 
            +
                    .on('start', (event, d) => dragStarted(event, d, options.simulation))
         | 
| 32 | 
            +
                    .on('drag', (event, d) => dragged(event, d))
         | 
| 33 | 
            +
                    .on('end', (event, d) => dragEnded(event, d, options.simulation))
         | 
| 34 | 
            +
                )
         | 
| 35 | 
            +
             | 
| 36 | 
            +
              nodeEnter
         | 
| 37 | 
            +
                .append('circle')
         | 
| 38 | 
            +
                .attr('r', options.nodeRadius)
         | 
| 39 | 
            +
                .style('fill', d => class2color(options.classes2colors, d.labels[0], options.colors, options.numClasses))
         | 
| 40 | 
            +
                .style('stroke', d =>
         | 
| 41 | 
            +
                  class2darkenColor(class2color(options.classes2colors, d.labels[0], options.colors, options.numClasses))
         | 
| 42 | 
            +
                )
         | 
| 43 | 
            +
                .style('stroke-width', 2)
         | 
| 44 | 
            +
             | 
| 45 | 
            +
              nodeEnter
         | 
| 46 | 
            +
                .append('text')
         | 
| 47 | 
            +
                .attr('dy', 4)
         | 
| 48 | 
            +
                .attr('text-anchor', 'middle')
         | 
| 49 | 
            +
                .text(d => d.properties.name || d.id)
         | 
| 50 | 
            +
             | 
| 51 | 
            +
              return nodeEnter
         | 
| 52 | 
            +
            }
         | 
| 53 | 
            +
             | 
| 54 | 
            +
            function dragStarted(
         | 
| 55 | 
            +
              event: d3.D3DragEvent<SVGGElement, Node, any>,
         | 
| 56 | 
            +
              d: Node,
         | 
| 57 | 
            +
              simulation: d3.Simulation<Node, undefined>
         | 
| 58 | 
            +
            ) {
         | 
| 59 | 
            +
              if (!event.active) simulation.alphaTarget(0.3).restart()
         | 
| 60 | 
            +
              d.fx = d.x
         | 
| 61 | 
            +
              d.fy = d.y
         | 
| 62 | 
            +
            }
         | 
| 63 | 
            +
             | 
| 64 | 
            +
            function dragged(event: d3.D3DragEvent<SVGGElement, Node, any>, d: Node) {
         | 
| 65 | 
            +
              d.fx = event.x
         | 
| 66 | 
            +
              d.fy = event.y
         | 
| 67 | 
            +
            }
         | 
| 68 | 
            +
             | 
| 69 | 
            +
            function dragEnded(event: d3.D3DragEvent<SVGGElement, Node, any>, d: Node, simulation: d3.Simulation<Node, undefined>) {
         | 
| 70 | 
            +
              if (!event.active) simulation.alphaTarget(0)
         | 
| 71 | 
            +
              d.fx = null
         | 
| 72 | 
            +
              d.fy = null
         | 
| 73 | 
            +
            }
         | 
| @@ -0,0 +1,19 @@ | |
| 1 | 
            +
            import { Relationship } from './graph-data'
         | 
| 2 | 
            +
            import * as d3 from 'd3'
         | 
| 3 | 
            +
             | 
| 4 | 
            +
            export function appendRelationshipToGraph(
         | 
| 5 | 
            +
              svgRelationships: d3.Selection<SVGGElement, unknown, HTMLElement, any>, // 타입 지정
         | 
| 6 | 
            +
              relationships: Relationship[],
         | 
| 7 | 
            +
              options: any
         | 
| 8 | 
            +
            ) {
         | 
| 9 | 
            +
              const relationshipEnter = svgRelationships
         | 
| 10 | 
            +
                .selectAll<SVGLineElement, Relationship>('.relationship')
         | 
| 11 | 
            +
                .data(relationships, d => d.id)
         | 
| 12 | 
            +
                .enter()
         | 
| 13 | 
            +
                .append('line')
         | 
| 14 | 
            +
                .attr('class', 'relationship')
         | 
| 15 | 
            +
                .style('stroke', '#aaa')
         | 
| 16 | 
            +
                .style('stroke-width', 2)
         | 
| 17 | 
            +
             | 
| 18 | 
            +
              return relationshipEnter
         | 
| 19 | 
            +
            }
         | 
| @@ -0,0 +1,41 @@ | |
| 1 | 
            +
            import * as d3 from 'd3'
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            export function class2color(
         | 
| 4 | 
            +
              classes2colors: Record<string, string>,
         | 
| 5 | 
            +
              cls: string,
         | 
| 6 | 
            +
              colors: string[],
         | 
| 7 | 
            +
              numClasses: number
         | 
| 8 | 
            +
            ): string {
         | 
| 9 | 
            +
              if (!classes2colors[cls]) {
         | 
| 10 | 
            +
                classes2colors[cls] = colors[numClasses % colors.length]
         | 
| 11 | 
            +
              }
         | 
| 12 | 
            +
              return classes2colors[cls]
         | 
| 13 | 
            +
            }
         | 
| 14 | 
            +
             | 
| 15 | 
            +
            export function class2darkenColor(color: string): string {
         | 
| 16 | 
            +
              return d3.rgb(color).darker(1).toString()
         | 
| 17 | 
            +
            }
         | 
| 18 | 
            +
             | 
| 19 | 
            +
            export function unitaryVector(source: any, target: any, newLength?: number) {
         | 
| 20 | 
            +
              const length =
         | 
| 21 | 
            +
                Math.sqrt(Math.pow(target.x - source.x, 2) + Math.pow(target.y - source.y, 2)) / Math.sqrt(newLength || 1)
         | 
| 22 | 
            +
              return {
         | 
| 23 | 
            +
                x: (target.x - source.x) / length,
         | 
| 24 | 
            +
                y: (target.y - source.y) / length
         | 
| 25 | 
            +
              }
         | 
| 26 | 
            +
            }
         | 
| 27 | 
            +
             | 
| 28 | 
            +
            export function unitaryNormalVector(source: any, target: any, newLength?: number) {
         | 
| 29 | 
            +
              const vector = unitaryVector(source, target, newLength)
         | 
| 30 | 
            +
              return rotate({ x: 0, y: 0 }, vector, 90)
         | 
| 31 | 
            +
            }
         | 
| 32 | 
            +
             | 
| 33 | 
            +
            export function rotate(center: { x: number; y: number }, point: { x: number; y: number }, angle: number) {
         | 
| 34 | 
            +
              const radians = (Math.PI / 180) * angle
         | 
| 35 | 
            +
              const cos = Math.cos(radians)
         | 
| 36 | 
            +
              const sin = Math.sin(radians)
         | 
| 37 | 
            +
              return {
         | 
| 38 | 
            +
                x: cos * (point.x - center.x) + sin * (point.y - center.y) + center.x,
         | 
| 39 | 
            +
                y: cos * (point.y - center.y) - sin * (point.x - center.x) + center.y
         | 
| 40 | 
            +
              }
         | 
| 41 | 
            +
            }
         | 
| @@ -1,13 +1,14 @@ | |
| 1 1 | 
             
            import '../analysis/graph-viewer'
         | 
| 2 2 |  | 
| 3 3 | 
             
            import gql from 'graphql-tag'
         | 
| 4 | 
            -
            import { css, html } from 'lit'
         | 
| 4 | 
            +
            import { css, html, nothing } from 'lit'
         | 
| 5 5 | 
             
            import { customElement, query, state } from 'lit/decorators.js'
         | 
| 6 6 |  | 
| 7 7 | 
             
            import { client } from '@operato/graphql'
         | 
| 8 8 | 
             
            import { i18next, localize } from '@operato/i18n'
         | 
| 9 9 | 
             
            import { PageView } from '@operato/shell'
         | 
| 10 10 | 
             
            import { ScrollbarStyles } from '@operato/styles'
         | 
| 11 | 
            +
            import { GraphData, Node, Relationship } from 'analysis/graph-data'
         | 
| 11 12 | 
             
            import { GraphViewer } from '../analysis/graph-viewer'
         | 
| 12 13 | 
             
            import { GraphViewerStyles } from '../analysis/graph-viewer-style'
         | 
| 13 14 |  | 
| @@ -18,6 +19,8 @@ export class IntegrationAnalysis extends localize(i18next)(PageView) { | |
| 18 19 | 
             
                ScrollbarStyles,
         | 
| 19 20 | 
             
                css`
         | 
| 20 21 | 
             
                  :host {
         | 
| 22 | 
            +
                    position: relative;
         | 
| 23 | 
            +
             | 
| 21 24 | 
             
                    display: flex;
         | 
| 22 25 | 
             
                    flex-direction: column;
         | 
| 23 26 |  | 
| @@ -31,98 +34,107 @@ export class IntegrationAnalysis extends localize(i18next)(PageView) { | |
| 31 34 | 
             
                    margin: 0;
         | 
| 32 35 | 
             
                    padding: 0;
         | 
| 33 36 | 
             
                  }
         | 
| 37 | 
            +
             | 
| 38 | 
            +
                  .graph-info {
         | 
| 39 | 
            +
                    position: absolute;
         | 
| 40 | 
            +
                    top: 10px;
         | 
| 41 | 
            +
                    left: 10px;
         | 
| 42 | 
            +
                    opacity: 0.8;
         | 
| 43 | 
            +
                  }
         | 
| 34 44 | 
             
                `
         | 
| 35 45 | 
             
              ]
         | 
| 36 46 |  | 
| 37 | 
            -
              @state()  | 
| 38 | 
            -
              @state()  | 
| 47 | 
            +
              @state() private graphViewer?: GraphViewer
         | 
| 48 | 
            +
              @state() private analysis?: { nodes: Node[]; relationships: Relationship[] }
         | 
| 49 | 
            +
              @state() private info?: { cls: string; property: string; value: string } | null
         | 
| 39 50 |  | 
| 40 51 | 
             
              @query('[graph-container]') container!: HTMLDivElement
         | 
| 52 | 
            +
              @query('[node-info]') nodeInfo!: HTMLAnchorElement
         | 
| 41 53 |  | 
| 42 54 | 
             
              get context() {
         | 
| 43 55 | 
             
                return {
         | 
| 44 56 | 
             
                  title: i18next.t('text.integration analysis'),
         | 
| 57 | 
            +
                  search: {
         | 
| 58 | 
            +
                    handler: search => this.onSearch(search)
         | 
| 59 | 
            +
                  },
         | 
| 45 60 | 
             
                  help: 'integration/ui/integration-analysis'
         | 
| 46 61 | 
             
                }
         | 
| 47 62 | 
             
              }
         | 
| 48 63 |  | 
| 49 64 | 
             
              render() {
         | 
| 50 | 
            -
                 | 
| 65 | 
            +
                const info = this.info
         | 
| 66 | 
            +
             | 
| 67 | 
            +
                return html`
         | 
| 68 | 
            +
                  <div
         | 
| 69 | 
            +
                    graph-container
         | 
| 70 | 
            +
                    @node-mouseenter=${(e: CustomEvent) => console.log(e.detail)}
         | 
| 71 | 
            +
                    @node-mouseleave=${(e: CustomEvent) => console.log(e.detail)}
         | 
| 72 | 
            +
                  ></div>
         | 
| 73 | 
            +
                  <div class="graph-info">
         | 
| 74 | 
            +
                    ${info
         | 
| 75 | 
            +
                      ? html`<a href="#" class="${info.cls}" node-info><strong>${info.property}</strong>${info.value}</a>`
         | 
| 76 | 
            +
                      : nothing}
         | 
| 77 | 
            +
                  </div>
         | 
| 78 | 
            +
                `
         | 
| 51 79 | 
             
              }
         | 
| 52 80 |  | 
| 53 81 | 
             
              updated(changes) {
         | 
| 54 82 | 
             
                if (changes.has('analysis')) {
         | 
| 55 | 
            -
                   | 
| 56 | 
            -
                     | 
| 57 | 
            -
                       | 
| 58 | 
            -
                         | 
| 59 | 
            -
             | 
| 60 | 
            -
             | 
| 61 | 
            -
             | 
| 62 | 
            -
             | 
| 63 | 
            -
                         | 
| 64 | 
            -
             | 
| 65 | 
            -
             | 
| 66 | 
            -
             | 
| 67 | 
            -
             | 
| 68 | 
            -
                        class: 'Tag',
         | 
| 69 | 
            -
                        property: 'missing',
         | 
| 70 | 
            -
                        value: true
         | 
| 71 | 
            -
                      }
         | 
| 72 | 
            -
                    ],
         | 
| 73 | 
            -
                    icons: {
         | 
| 74 | 
            -
                      Connection: 'gear',
         | 
| 75 | 
            -
                      Scenario: 'birthday-cake',
         | 
| 76 | 
            -
                      Tag: 'paw'
         | 
| 77 | 
            -
                    },
         | 
| 78 | 
            -
                    // images: {
         | 
| 79 | 
            -
                    //   Address: 'img/twemoji/1f3e0.svg',
         | 
| 80 | 
            -
                    //   BirthDate: 'img/twemoji/1f5d3.svg',
         | 
| 81 | 
            -
                    //   Cookie: 'img/twemoji/1f36a.svg',
         | 
| 82 | 
            -
                    //   CreditCard: 'img/twemoji/1f4b3.svg',
         | 
| 83 | 
            -
                    //   Device: 'img/twemoji/1f4bb.svg',
         | 
| 84 | 
            -
                    //   Email: 'img/twemoji/2709.svg',
         | 
| 85 | 
            -
                    //   Git: 'img/twemoji/1f5c3.svg',
         | 
| 86 | 
            -
                    //   Github: 'img/twemoji/1f5c4.svg',
         | 
| 87 | 
            -
                    //   icons: 'img/twemoji/1f38f.svg',
         | 
| 88 | 
            -
                    //   Ip: 'img/twemoji/1f4cd.svg',
         | 
| 89 | 
            -
                    //   Issues: 'img/twemoji/1f4a9.svg',
         | 
| 90 | 
            -
                    //   Language: 'img/twemoji/1f1f1-1f1f7.svg',
         | 
| 91 | 
            -
                    //   Options: 'img/twemoji/2699.svg',
         | 
| 92 | 
            -
                    //   Password: 'img/twemoji/1f511.svg',
         | 
| 93 | 
            -
                    //   'Project|name|d3': 'img/twemoji/32-20e3.svg',
         | 
| 94 | 
            -
                    //   User: 'img/twemoji/1f600.svg'
         | 
| 95 | 
            -
                    // },
         | 
| 96 | 
            -
                    minCollision: 60,
         | 
| 97 | 
            -
                    graphData: {
         | 
| 98 | 
            -
                      results: [
         | 
| 83 | 
            +
                  if (!this.graphViewer) {
         | 
| 84 | 
            +
                    this.graphViewer = new GraphViewer(this.container, {
         | 
| 85 | 
            +
                      highlight: [
         | 
| 86 | 
            +
                        {
         | 
| 87 | 
            +
                          class: 'Connection',
         | 
| 88 | 
            +
                          property: 'missing',
         | 
| 89 | 
            +
                          value: true
         | 
| 90 | 
            +
                        },
         | 
| 91 | 
            +
                        {
         | 
| 92 | 
            +
                          class: 'Scenario',
         | 
| 93 | 
            +
                          property: 'missing',
         | 
| 94 | 
            +
                          value: true
         | 
| 95 | 
            +
                        },
         | 
| 99 96 | 
             
                        {
         | 
| 100 | 
            -
                           | 
| 101 | 
            -
                           | 
| 102 | 
            -
             | 
| 103 | 
            -
                              graph: this.analysis
         | 
| 104 | 
            -
                            }
         | 
| 105 | 
            -
                          ]
         | 
| 97 | 
            +
                          class: 'Tag',
         | 
| 98 | 
            +
                          property: 'missing',
         | 
| 99 | 
            +
                          value: true
         | 
| 106 100 | 
             
                        }
         | 
| 107 101 | 
             
                      ],
         | 
| 102 | 
            +
                      minCollision: 60,
         | 
| 103 | 
            +
                      graphData: {
         | 
| 104 | 
            +
                        results: [
         | 
| 105 | 
            +
                          {
         | 
| 106 | 
            +
                            columns: [],
         | 
| 107 | 
            +
                            data: [
         | 
| 108 | 
            +
                              {
         | 
| 109 | 
            +
                                graph: this.analysis
         | 
| 110 | 
            +
                              }
         | 
| 111 | 
            +
                            ]
         | 
| 112 | 
            +
                          }
         | 
| 113 | 
            +
                        ],
         | 
| 114 | 
            +
                        errors: []
         | 
| 115 | 
            +
                      },
         | 
| 116 | 
            +
                      nodeRadius: 25,
         | 
| 117 | 
            +
                      onNodeDoubleClick: node => {
         | 
| 118 | 
            +
                        switch (node.id) {
         | 
| 119 | 
            +
                          case '25':
         | 
| 120 | 
            +
                            // Google
         | 
| 121 | 
            +
                            window.open(node.properties.url, '_blank')
         | 
| 122 | 
            +
                            break
         | 
| 123 | 
            +
                          default:
         | 
| 124 | 
            +
                            break
         | 
| 125 | 
            +
                        }
         | 
| 126 | 
            +
                      },
         | 
| 127 | 
            +
                      zoomFit: true
         | 
| 128 | 
            +
                    })
         | 
| 129 | 
            +
                  } else {
         | 
| 130 | 
            +
                    this.graphViewer.updateWithGraphData({
         | 
| 131 | 
            +
                      results: [{ columns: [], data: [{ graph: this.analysis! }] }],
         | 
| 108 132 | 
             
                      errors: []
         | 
| 109 | 
            -
                    } | 
| 110 | 
            -
             | 
| 111 | 
            -
             | 
| 112 | 
            -
             | 
| 113 | 
            -
             | 
| 114 | 
            -
                          // Google
         | 
| 115 | 
            -
                          window.open(node.properties.url, '_blank')
         | 
| 116 | 
            -
                          break
         | 
| 117 | 
            -
                        default:
         | 
| 118 | 
            -
                          var maxNodes = 5,
         | 
| 119 | 
            -
                            data = this.graphViewer!.randomD3Data(node, maxNodes)
         | 
| 120 | 
            -
                          this.graphViewer!.updateWithD3Data(data)
         | 
| 121 | 
            -
                          break
         | 
| 122 | 
            -
                      }
         | 
| 123 | 
            -
                    },
         | 
| 124 | 
            -
                    zoomFit: true
         | 
| 125 | 
            -
                  })
         | 
| 133 | 
            +
                    })
         | 
| 134 | 
            +
                  }
         | 
| 135 | 
            +
                }
         | 
| 136 | 
            +
             | 
| 137 | 
            +
                if (changes.has('info') && this.info) {
         | 
| 126 138 | 
             
                }
         | 
| 127 139 | 
             
              }
         | 
| 128 140 |  | 
| @@ -148,4 +160,81 @@ export class IntegrationAnalysis extends localize(i18next)(PageView) { | |
| 148 160 |  | 
| 149 161 | 
             
                this.analysis = response.data.integrationAnalysis
         | 
| 150 162 | 
             
              }
         | 
| 163 | 
            +
             | 
| 164 | 
            +
              async onSearch(search: string) {
         | 
| 165 | 
            +
                if (!this.graphViewer) {
         | 
| 166 | 
            +
                  return
         | 
| 167 | 
            +
                }
         | 
| 168 | 
            +
             | 
| 169 | 
            +
                if (!search) {
         | 
| 170 | 
            +
                  this.graphViewer.updateWithGraphData({
         | 
| 171 | 
            +
                    results: [{ columns: [], data: [{ graph: this.analysis! }] }],
         | 
| 172 | 
            +
                    errors: []
         | 
| 173 | 
            +
                  })
         | 
| 174 | 
            +
                  return
         | 
| 175 | 
            +
                }
         | 
| 176 | 
            +
             | 
| 177 | 
            +
                const { nodes = [], relationships = [] } = this.analysis || {}
         | 
| 178 | 
            +
             | 
| 179 | 
            +
                // 검색어와 일치하는 노드를 필터링
         | 
| 180 | 
            +
                const matchingNodes = nodes.filter(node => {
         | 
| 181 | 
            +
                  return node.properties.name && node.properties.name.toLowerCase().includes(search.toLowerCase())
         | 
| 182 | 
            +
                })
         | 
| 183 | 
            +
             | 
| 184 | 
            +
                if (matchingNodes.length === 0) {
         | 
| 185 | 
            +
                  this.graphViewer.updateWithGraphData({
         | 
| 186 | 
            +
                    results: [{ columns: [], data: [{ graph: { nodes: [], relationships: [] } }] }],
         | 
| 187 | 
            +
                    errors: []
         | 
| 188 | 
            +
                  })
         | 
| 189 | 
            +
                  return
         | 
| 190 | 
            +
                }
         | 
| 191 | 
            +
             | 
| 192 | 
            +
                // 1차로 연결된 노드와 관계를 찾기 위해 관련된 노드 ID를 추적
         | 
| 193 | 
            +
                const relatedNodeIds = new Set<string>()
         | 
| 194 | 
            +
                const filteredNodes = [] as Node[]
         | 
| 195 | 
            +
                const filteredRelationships = [] as Relationship[]
         | 
| 196 | 
            +
             | 
| 197 | 
            +
                matchingNodes.forEach(node => {
         | 
| 198 | 
            +
                  relatedNodeIds.add(node.id)
         | 
| 199 | 
            +
                  filteredNodes.push(node)
         | 
| 200 | 
            +
             | 
| 201 | 
            +
                  relationships.forEach(relationship => {
         | 
| 202 | 
            +
                    const { source, target } = relationship || {}
         | 
| 203 | 
            +
                    if (source!.id === node.id || target!.id === node.id) {
         | 
| 204 | 
            +
                      relatedNodeIds.add(source!.id)
         | 
| 205 | 
            +
                      relatedNodeIds.add(target!.id)
         | 
| 206 | 
            +
                      filteredRelationships.push(relationship!)
         | 
| 207 | 
            +
                    }
         | 
| 208 | 
            +
                  })
         | 
| 209 | 
            +
                })
         | 
| 210 | 
            +
             | 
| 211 | 
            +
                // 관련된 노드 추가
         | 
| 212 | 
            +
                relatedNodeIds.forEach(nodeId => {
         | 
| 213 | 
            +
                  const node = nodes.find(n => n.id === nodeId)
         | 
| 214 | 
            +
                  if (node) {
         | 
| 215 | 
            +
                    filteredNodes.push(node)
         | 
| 216 | 
            +
                  }
         | 
| 217 | 
            +
                })
         | 
| 218 | 
            +
             | 
| 219 | 
            +
                // 새로운 GraphData 구성
         | 
| 220 | 
            +
                const filteredGraphData: GraphData = {
         | 
| 221 | 
            +
                  results: [
         | 
| 222 | 
            +
                    {
         | 
| 223 | 
            +
                      columns: [],
         | 
| 224 | 
            +
                      data: [
         | 
| 225 | 
            +
                        {
         | 
| 226 | 
            +
                          graph: {
         | 
| 227 | 
            +
                            nodes: filteredNodes,
         | 
| 228 | 
            +
                            relationships: filteredRelationships
         | 
| 229 | 
            +
                          }
         | 
| 230 | 
            +
                        }
         | 
| 231 | 
            +
                      ]
         | 
| 232 | 
            +
                    }
         | 
| 233 | 
            +
                  ],
         | 
| 234 | 
            +
                  errors: []
         | 
| 235 | 
            +
                }
         | 
| 236 | 
            +
             | 
| 237 | 
            +
                // GraphViewer 업데이트
         | 
| 238 | 
            +
                this.graphViewer.updateWithGraphData(filteredGraphData)
         | 
| 239 | 
            +
              }
         | 
| 151 240 | 
             
            }
         | 
| @@ -0,0 +1,36 @@ | |
| 1 | 
            +
            export interface GraphData {
         | 
| 2 | 
            +
                results: Array<{
         | 
| 3 | 
            +
                    columns: string[];
         | 
| 4 | 
            +
                    data: Array<{
         | 
| 5 | 
            +
                        graph: {
         | 
| 6 | 
            +
                            nodes: Node[];
         | 
| 7 | 
            +
                            relationships: Relationship[];
         | 
| 8 | 
            +
                        };
         | 
| 9 | 
            +
                    }>;
         | 
| 10 | 
            +
                }>;
         | 
| 11 | 
            +
                errors: any[];
         | 
| 12 | 
            +
            }
         | 
| 13 | 
            +
            export interface Node {
         | 
| 14 | 
            +
                id: string;
         | 
| 15 | 
            +
                labels: string[];
         | 
| 16 | 
            +
                properties: {
         | 
| 17 | 
            +
                    [key: string]: any;
         | 
| 18 | 
            +
                };
         | 
| 19 | 
            +
                x?: number;
         | 
| 20 | 
            +
                y?: number;
         | 
| 21 | 
            +
                fx?: number | null;
         | 
| 22 | 
            +
                fy?: number | null;
         | 
| 23 | 
            +
                icon?: string;
         | 
| 24 | 
            +
            }
         | 
| 25 | 
            +
            export interface Relationship {
         | 
| 26 | 
            +
                id: string;
         | 
| 27 | 
            +
                type: string;
         | 
| 28 | 
            +
                startNode: string;
         | 
| 29 | 
            +
                endNode: string;
         | 
| 30 | 
            +
                source?: Node;
         | 
| 31 | 
            +
                target?: Node;
         | 
| 32 | 
            +
                properties: {
         | 
| 33 | 
            +
                    [key: string]: any;
         | 
| 34 | 
            +
                };
         | 
| 35 | 
            +
                linknum?: number;
         | 
| 36 | 
            +
            }
         | 
| @@ -0,0 +1 @@ | |
| 1 | 
            +
            {"version":3,"file":"graph-data.js","sourceRoot":"","sources":["../../client/analysis/graph-data.ts"],"names":[],"mappings":"","sourcesContent":["export interface GraphData {\n  results: Array<{\n    columns: string[]\n    data: Array<{\n      graph: {\n        nodes: Node[]\n        relationships: Relationship[]\n      }\n    }>\n  }>\n  errors: any[]\n}\n\nexport interface Node {\n  id: string\n  labels: string[]\n  properties: { [key: string]: any }\n  x?: number\n  y?: number\n  fx?: number | null\n  fy?: number | null\n  icon?: string\n}\n\nexport interface Relationship {\n  id: string\n  type: string\n  startNode: string\n  endNode: string\n  source?: Node\n  target?: Node\n  properties: { [key: string]: any }\n  linknum?: number\n}\n"]}
         | 
| @@ -0,0 +1,110 @@ | |
| 1 | 
            +
            export declare class GraphViewer {
         | 
| 2 | 
            +
                simulation: any;
         | 
| 3 | 
            +
                private container;
         | 
| 4 | 
            +
                private info;
         | 
| 5 | 
            +
                private node;
         | 
| 6 | 
            +
                private nodes;
         | 
| 7 | 
            +
                private relationship;
         | 
| 8 | 
            +
                private relationshipOutline;
         | 
| 9 | 
            +
                private relationshipOverlay;
         | 
| 10 | 
            +
                private relationshipText;
         | 
| 11 | 
            +
                private relationships;
         | 
| 12 | 
            +
                private selector;
         | 
| 13 | 
            +
                private svg;
         | 
| 14 | 
            +
                private svgNodes;
         | 
| 15 | 
            +
                private svgRelationships;
         | 
| 16 | 
            +
                private svgScale;
         | 
| 17 | 
            +
                private svgTranslate;
         | 
| 18 | 
            +
                private classes2colors;
         | 
| 19 | 
            +
                private justLoaded;
         | 
| 20 | 
            +
                private numClasses;
         | 
| 21 | 
            +
                private options;
         | 
| 22 | 
            +
                constructor(_selector: any, _options: any);
         | 
| 23 | 
            +
                appendGraph(container: any): void;
         | 
| 24 | 
            +
                appendImageToNode(node: any): any;
         | 
| 25 | 
            +
                appendInfoPanel(container: any): any;
         | 
| 26 | 
            +
                appendInfoElement(cls: any, isNode: any, property: any, value?: any): void;
         | 
| 27 | 
            +
                appendInfoElementClass(cls: any, node: any): void;
         | 
| 28 | 
            +
                appendInfoElementProperty(cls: any, property: any, value: any): void;
         | 
| 29 | 
            +
                appendInfoElementRelationship(cls: any, relationship: any): void;
         | 
| 30 | 
            +
                appendNode(): any;
         | 
| 31 | 
            +
                appendNodeToGraph(): any;
         | 
| 32 | 
            +
                appendOutlineToNode(node: any): any;
         | 
| 33 | 
            +
                appendBoxToNode(node: any): any;
         | 
| 34 | 
            +
                appendRingToNode(node: any): any;
         | 
| 35 | 
            +
                appendTextToNode(node: any): any;
         | 
| 36 | 
            +
                appendRelationship(): any;
         | 
| 37 | 
            +
                appendOutlineToRelationship(r: any): any;
         | 
| 38 | 
            +
                appendOverlayToRelationship(r: any): any;
         | 
| 39 | 
            +
                appendTextToRelationship(r: any): any;
         | 
| 40 | 
            +
                appendRelationshipToGraph(): {
         | 
| 41 | 
            +
                    outline: any;
         | 
| 42 | 
            +
                    overlay: any;
         | 
| 43 | 
            +
                    relationship: any;
         | 
| 44 | 
            +
                    text: any;
         | 
| 45 | 
            +
                };
         | 
| 46 | 
            +
                class2color(cls: any): any;
         | 
| 47 | 
            +
                class2darkenColor(cls: any): any;
         | 
| 48 | 
            +
                clearInfo(): void;
         | 
| 49 | 
            +
                color(): any;
         | 
| 50 | 
            +
                colors(): string[];
         | 
| 51 | 
            +
                contains(array: any, id: any): boolean;
         | 
| 52 | 
            +
                defaultColor(): any;
         | 
| 53 | 
            +
                defaultDarkenColor(): any;
         | 
| 54 | 
            +
                dragEnded(event: any, d: any): void;
         | 
| 55 | 
            +
                dragged(event: any, d: any): void;
         | 
| 56 | 
            +
                dragStarted(event: any, d: any): void;
         | 
| 57 | 
            +
                extend(obj1: any, obj2: any): {};
         | 
| 58 | 
            +
                icon(d: any): any;
         | 
| 59 | 
            +
                image(d: any): any;
         | 
| 60 | 
            +
                init(_selector: any, _options: any): void;
         | 
| 61 | 
            +
                initSimulation(): any;
         | 
| 62 | 
            +
                loadGraphData(): void;
         | 
| 63 | 
            +
                loadGraphDataFromUrl(dataUrl: any): void;
         | 
| 64 | 
            +
                merge(target: any, source: any): void;
         | 
| 65 | 
            +
                graphDataToD3Data(data: any): {
         | 
| 66 | 
            +
                    nodes: any[];
         | 
| 67 | 
            +
                    relationships: any[];
         | 
| 68 | 
            +
                };
         | 
| 69 | 
            +
                randomD3Data(d: any, maxNodesToGenerate: any): {
         | 
| 70 | 
            +
                    nodes: any[];
         | 
| 71 | 
            +
                    relationships: any[];
         | 
| 72 | 
            +
                };
         | 
| 73 | 
            +
                randomLabel(): string;
         | 
| 74 | 
            +
                rotate(cx: any, cy: any, x: any, y: any, angle: any): {
         | 
| 75 | 
            +
                    x: any;
         | 
| 76 | 
            +
                    y: any;
         | 
| 77 | 
            +
                };
         | 
| 78 | 
            +
                rotatePoint(c: any, p: any, angle: any): {
         | 
| 79 | 
            +
                    x: any;
         | 
| 80 | 
            +
                    y: any;
         | 
| 81 | 
            +
                };
         | 
| 82 | 
            +
                rotation(source: any, target: any): number;
         | 
| 83 | 
            +
                size(): {
         | 
| 84 | 
            +
                    nodes: any;
         | 
| 85 | 
            +
                    relationships: any;
         | 
| 86 | 
            +
                };
         | 
| 87 | 
            +
                stickNode(event: any, d: any): void;
         | 
| 88 | 
            +
                tick(): void;
         | 
| 89 | 
            +
                tickNodes(): void;
         | 
| 90 | 
            +
                tickRelationships(): void;
         | 
| 91 | 
            +
                tickRelationshipsOutlines(): void;
         | 
| 92 | 
            +
                tickRelationshipsOverlays(): void;
         | 
| 93 | 
            +
                tickRelationshipsTexts(): void;
         | 
| 94 | 
            +
                toString(d: any): any;
         | 
| 95 | 
            +
                unitaryNormalVector(source: any, target: any, newLength?: any): {
         | 
| 96 | 
            +
                    x: any;
         | 
| 97 | 
            +
                    y: any;
         | 
| 98 | 
            +
                };
         | 
| 99 | 
            +
                unitaryVector(source: any, target: any, newLength?: any): {
         | 
| 100 | 
            +
                    x: number;
         | 
| 101 | 
            +
                    y: number;
         | 
| 102 | 
            +
                };
         | 
| 103 | 
            +
                updateWithD3Data(d3Data: any): void;
         | 
| 104 | 
            +
                updateWithGraphData(graphData: any): void;
         | 
| 105 | 
            +
                updateInfo(d: any): void;
         | 
| 106 | 
            +
                updateNodes(n: any): void;
         | 
| 107 | 
            +
                updateNodesAndRelationships(n: any, r: any): void;
         | 
| 108 | 
            +
                updateRelationships(r: any): void;
         | 
| 109 | 
            +
                zoomFit(transitionDuration: any): void;
         | 
| 110 | 
            +
            }
         |