You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

162 lines
5.5 KiB

4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
  1. export class Display {
  2. constructor(baseUrl, dispatch, target) {
  3. this.baseUrl = baseUrl
  4. this.target = target
  5. const findA = target => {
  6. while (target.nodeName !== 'A') {
  7. target = target.parentNode
  8. if (!target) return
  9. }
  10. return target
  11. }
  12. target.addEventListener('click', e => {
  13. let target = findA(e.target)
  14. if (!target) return
  15. window.history.pushState(null, "", target.href)
  16. dispatch(target.getAttribute('href'))
  17. e.preventDefault()
  18. })
  19. target.addEventListener('dragstart', e => {
  20. let target = findA(e.target)
  21. if (!target || !target.draggable) return
  22. e.dataTransfer.setData('application/prs.x', target.dataset['id'])
  23. e.dataTransfer.effectAllowed = 'link'
  24. })
  25. target.addEventListener('dragenter', e => {
  26. let target = findA(e.target)
  27. if (!target) return
  28. e.preventDefault()
  29. })
  30. target.addEventListener('dragover', e => {
  31. let target = findA(e.target)
  32. if (!target) return
  33. e.preventDefault()
  34. })
  35. target.addEventListener('drop', e => {
  36. let target = findA(e.target)
  37. if (!target || !target.draggable) return
  38. dispatch({ action: 'link', from: e.dataTransfer.getData("application/prs.x"), to: target.dataset['id']})
  39. && e.preventDefault()
  40. })
  41. this.graph = new Springy.Graph()
  42. this.layout = new Springy.Layout.ForceDirected(
  43. this.graph,
  44. 15,
  45. 1000.0, // Node repulsion
  46. 0.5 // Damping
  47. )
  48. }
  49. render(state) {
  50. const target = document.createElement('div')
  51. target.className = 'wrapper'
  52. const field = document.createElement('ul')
  53. field.className = 'items'
  54. const graph = this.graph
  55. for (const item of state.items) {
  56. graph.addNode(new Springy.Node(item.id, item))
  57. }
  58. for (const item of state.links) {
  59. graph.addEdge(new Springy.Edge(item.id, graph.nodeSet[item.from], graph.nodeSet[item.to], {
  60. text: state.items[item.from].linkable.filter(i => state.items[item.to].linkable.includes(i)).join(', ')
  61. }))
  62. }
  63. let currentBB = this.layout.getBoundingBox()
  64. let width = this.target.scrollWidth || 100
  65. let height = this.target.scrollHeight || 100
  66. var toScreen = function(p) {
  67. var size = currentBB.topright.subtract(currentBB.bottomleft);
  68. var sx = p.subtract(currentBB.bottomleft).divide(size.x).x * (width - 60) + 30;
  69. var sy = p.subtract(currentBB.bottomleft).divide(size.y).y * (height - 60) + 30;
  70. return new Springy.Vector(sx, sy);
  71. };
  72. var fromScreen = function(s) {
  73. var size = currentBB.topright.subtract(currentBB.bottomleft);
  74. var px = (s.x / canvas.width) * size.x + currentBB.bottomleft.x;
  75. var py = (s.y / canvas.height) * size.y + currentBB.bottomleft.y;
  76. return new Springy.Vector(px, py);
  77. };
  78. var renderer = new Springy.Renderer(
  79. this.layout,
  80. () => {
  81. currentBB = this.layout.getBoundingBox()
  82. width = this.target.scrollWidth
  83. height = this.target.scrollHeight
  84. field.innerHTML = ''
  85. },
  86. function drawEdge(edge, p1, p2) {
  87. const dom = document.createElement('span')
  88. dom.innerText = edge.data.text
  89. p1 = toScreen(p1)
  90. p2 = toScreen(p2)
  91. if (p1.y > p2.y) [p1, p2] = [p2, p1]
  92. const a = p2.x - p1.x
  93. const b = p2.y - p1.y
  94. const negative = (a < 0) != (b < 0)
  95. let rad = Math.atan(b / a)
  96. if (negative) rad = rad + Math.PI
  97. if (rad > Math.PI / 2) {
  98. console.warn(negative)
  99. rad += Math.PI
  100. ;[p2, p1] = [p1, p2]
  101. } else if (negative) { console.warn(false) }
  102. dom.style.transformOrigin = '0 0'
  103. dom.style.transform = 'rotate(' + rad + 'rad) translateY(-1em)'
  104. dom.style.position = 'absolute'
  105. dom.style.left = p1.x + 'px'
  106. dom.style.top = p1.y + 'px'
  107. dom.style.right = p2.x + 'px'
  108. dom.style.bottom = p2.y + 'px'
  109. dom.style.width = Math.sqrt(Math.pow(a, 2) + Math.pow(b, 2)) + 'px'
  110. dom.style.height = '1em'
  111. dom.style.borderBottom = '3px solid black'
  112. dom.style.textAlign = 'center'
  113. // FIXME eigentlich falsches parent
  114. field.appendChild(dom)
  115. },
  116. function drawNode(item, p) {
  117. const target = toScreen(p)
  118. const dom = document.createElement('li')
  119. const a = document.createElement('a')
  120. const action = item.data.state == 'face-down' ? 'flip' : 'show'
  121. a.href = `/${item.id}/${action}`
  122. a.draggable = false
  123. if (item.data.state !== 'face-down') {
  124. a.style.backgroundImage = `url(/img/${item.data.img})`
  125. a.dataset['id'] = item.id
  126. a.draggable = true
  127. }
  128. dom.className = item.data.state
  129. dom.style.position = 'absolute'
  130. dom.style.left = (target.x - 60) + 'px'
  131. dom.style.top = (target.y - 60) + 'px'
  132. dom.appendChild(a)
  133. field.appendChild(dom)
  134. }, undefined, undefined,
  135. () => {
  136. const dd = new diffDOM.DiffDOM()
  137. const diff = dd.diff(this.target.children[0], field)
  138. dd.apply(this.target.children[0], diff)
  139. }
  140. );
  141. target.appendChild(field)
  142. if (state.show !== null) {
  143. const wrapper = document.createElement('a')
  144. wrapper.href = '/'
  145. const fullview = document.createElement('div')
  146. wrapper.className = 'fullview'
  147. wrapper.appendChild(fullview)
  148. target.appendChild(wrapper)
  149. }
  150. const dd = new diffDOM.DiffDOM()
  151. const diff = dd.diff(this.target, target)
  152. dd.apply(this.target, diff)
  153. renderer.start();
  154. }
  155. }