feat: pages visualize improvements (#1914)
* viz pages | add ctrl-click to open page in new tab * viz pages | rewrite `hierarchy` method * viz pages | pan and zoom * fix: pages visualize height + UI fixes Co-authored-by: NGPixel <github@ngpixel.com>
This commit is contained in:
parent
662480e33b
commit
ab1f93be1b
@ -29,7 +29,7 @@
|
|||||||
v-btn.px-5(value='rradial')
|
v-btn.px-5(value='rradial')
|
||||||
v-icon(left, :color='graphMode === `rradial` ? `primary` : `grey darken-3`') mdi-blur-radial
|
v-icon(left, :color='graphMode === `rradial` ? `primary` : `grey darken-3`') mdi-blur-radial
|
||||||
span.text-none Relational Radial
|
span.text-none Relational Radial
|
||||||
.admin-pages-visualize-svg.pa-10(ref='svgContainer')
|
.admin-pages-visualize-svg(ref='svgContainer', v-show='pages.length >= 1')
|
||||||
v-alert(v-if='pages.length < 1', outlined, type='warning', style='max-width: 650px; margin: 0 auto;') Looks like there's no data yet to graph!
|
v-alert(v-if='pages.length < 1', outlined, type='warning', style='max-width: 650px; margin: 0 auto;') Looks like there's no data yet to graph!
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@ -61,8 +61,14 @@ export default {
|
|||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
goToPage (d) {
|
goToPage (d) {
|
||||||
if (_.get(d, 'data.id', 0) > 0) {
|
const id = d.data.id
|
||||||
this.$router.push(`${d.data.id}`)
|
if (id) {
|
||||||
|
if (d3.event.ctrlKey || d3.event.metaKey) {
|
||||||
|
const { href } = this.$router.resolve(String(id))
|
||||||
|
window.open(href, '_blank')
|
||||||
|
} else {
|
||||||
|
this.$router.push(String(id))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
bilink (root) {
|
bilink (root) {
|
||||||
@ -86,41 +92,36 @@ export default {
|
|||||||
}
|
}
|
||||||
return root
|
return root
|
||||||
},
|
},
|
||||||
hierarchy (data, rootOnly = false) {
|
hierarchy (pages) {
|
||||||
let result = []
|
const map = new Map(pages.map(p => [p.path, p]))
|
||||||
let level = { result }
|
const getPage = path => map.get(path) || {
|
||||||
const map = new Map(data.map(d => [d.path, d]))
|
path: path,
|
||||||
data.forEach(d => {
|
title: path.split('/').slice(-1)[0],
|
||||||
const pathParts = d.path.split('/')
|
links: []
|
||||||
pathParts.reduce((r, part, i) => {
|
|
||||||
const curPath = _.take(pathParts, i + 1).join('/')
|
|
||||||
if (!r[part]) {
|
|
||||||
r[part] = { result: [] }
|
|
||||||
const page = map.get(curPath)
|
|
||||||
r.result.push(page ? {
|
|
||||||
...d,
|
|
||||||
children: r[part].result
|
|
||||||
} : {
|
|
||||||
title: part,
|
|
||||||
links: [],
|
|
||||||
path: curPath,
|
|
||||||
children: r[part].result
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return r[part]
|
function recurse (depth, [parent, descendants]) {
|
||||||
}, level)
|
const truncatePath = path => _.take(path.split('/'), depth).join('/')
|
||||||
})
|
const descendantsByChild =
|
||||||
|
Object.entries(_.groupBy(descendants, page => truncatePath(page.path)))
|
||||||
return rootOnly ? _.head(result) || { children: [] } : {
|
.map(([childPath, descendantsGroup]) => [getPage(childPath), descendantsGroup])
|
||||||
children: result
|
.map(([child, descendantsGroup]) =>
|
||||||
|
[child, _.filter(descendantsGroup, d => d.path !== child.path)])
|
||||||
|
return {
|
||||||
|
...parent,
|
||||||
|
children: descendantsByChild.map(_.partial(recurse, depth + 1))
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
const root = { path: this.currentLocale, title: this.currentLocale, links: [] }
|
||||||
|
// start at depth=2 because we're taking {locale} as the root and
|
||||||
|
// all paths start with {locale}/
|
||||||
|
return recurse(2, [root, pages])
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* Relational Radial
|
* Relational Radial
|
||||||
*/
|
*/
|
||||||
drawRelations () {
|
drawRelations () {
|
||||||
const data = this.hierarchy(this.pages, true)
|
const data = this.hierarchy(this.pages)
|
||||||
|
|
||||||
const line = d3.lineRadial()
|
const line = d3.lineRadial()
|
||||||
.curve(d3.curveBundle.beta(0.85))
|
.curve(d3.curveBundle.beta(0.85))
|
||||||
@ -136,7 +137,13 @@ export default {
|
|||||||
const svg = d3.create('svg')
|
const svg = d3.create('svg')
|
||||||
.attr('viewBox', [-this.width / 2, -this.width / 2, this.width, this.width])
|
.attr('viewBox', [-this.width / 2, -this.width / 2, this.width, this.width])
|
||||||
|
|
||||||
const link = svg.append('g')
|
const g = svg.append('g')
|
||||||
|
|
||||||
|
svg.call(d3.zoom().on('zoom', function() {
|
||||||
|
g.attr('transform', d3.event.transform)
|
||||||
|
}))
|
||||||
|
|
||||||
|
const link = g.append('g')
|
||||||
.attr('stroke', '#CCC')
|
.attr('stroke', '#CCC')
|
||||||
.attr('fill', 'none')
|
.attr('fill', 'none')
|
||||||
.selectAll('path')
|
.selectAll('path')
|
||||||
@ -146,7 +153,7 @@ export default {
|
|||||||
.attr('d', ([i, o]) => line(i.path(o)))
|
.attr('d', ([i, o]) => line(i.path(o)))
|
||||||
.each(function(d) { d.path = this })
|
.each(function(d) { d.path = this })
|
||||||
|
|
||||||
svg.append('g')
|
g.append('g')
|
||||||
.attr('font-family', 'sans-serif')
|
.attr('font-family', 'sans-serif')
|
||||||
.attr('font-size', 10)
|
.attr('font-size', 10)
|
||||||
.selectAll('g')
|
.selectAll('g')
|
||||||
@ -195,7 +202,7 @@ export default {
|
|||||||
* Hierarchical Tree
|
* Hierarchical Tree
|
||||||
*/
|
*/
|
||||||
drawTree () {
|
drawTree () {
|
||||||
const data = this.hierarchy(this.pages, true)
|
const data = this.hierarchy(this.pages)
|
||||||
|
|
||||||
const treeRoot = d3.hierarchy(data)
|
const treeRoot = d3.hierarchy(data)
|
||||||
treeRoot.dx = 10
|
treeRoot.dx = 10
|
||||||
@ -212,7 +219,16 @@ export default {
|
|||||||
const svg = d3.create('svg')
|
const svg = d3.create('svg')
|
||||||
.attr('viewBox', [0, 0, this.width, x1 - x0 + root.dx * 2])
|
.attr('viewBox', [0, 0, this.width, x1 - x0 + root.dx * 2])
|
||||||
|
|
||||||
const g = svg.append('g')
|
// this extra level is necessary because the element that we
|
||||||
|
// apply the zoom tranform to must be above the element where
|
||||||
|
// we apply the translation (`g`), or else zoom is wonky
|
||||||
|
const gZoom = svg.append('g')
|
||||||
|
|
||||||
|
svg.call(d3.zoom().on('zoom', function() {
|
||||||
|
gZoom.attr('transform', d3.event.transform)
|
||||||
|
}))
|
||||||
|
|
||||||
|
const g = gZoom.append('g')
|
||||||
.attr('font-family', 'sans-serif')
|
.attr('font-family', 'sans-serif')
|
||||||
.attr('font-size', 10)
|
.attr('font-size', 10)
|
||||||
.attr('transform', `translate(${root.dy / 3},${root.dx - x0})`)
|
.attr('transform', `translate(${root.dy / 3},${root.dx - x0})`)
|
||||||
@ -270,7 +286,14 @@ export default {
|
|||||||
const svg = d3.create('svg')
|
const svg = d3.create('svg')
|
||||||
.style('font', '10px sans-serif')
|
.style('font', '10px sans-serif')
|
||||||
|
|
||||||
svg.append('g')
|
const g = svg.append('g')
|
||||||
|
|
||||||
|
svg.call(d3.zoom().on('zoom', function () {
|
||||||
|
g.attr('transform', d3.event.transform)
|
||||||
|
}))
|
||||||
|
|
||||||
|
// eslint-disable-next-line no-unused-vars
|
||||||
|
const link = g.append('g')
|
||||||
.attr('fill', 'none')
|
.attr('fill', 'none')
|
||||||
.attr('stroke', this.$vuetify.theme.dark ? 'white' : '#555')
|
.attr('stroke', this.$vuetify.theme.dark ? 'white' : '#555')
|
||||||
.attr('stroke-opacity', 0.4)
|
.attr('stroke-opacity', 0.4)
|
||||||
@ -282,7 +305,7 @@ export default {
|
|||||||
.angle(d => d.x)
|
.angle(d => d.x)
|
||||||
.radius(d => d.y))
|
.radius(d => d.y))
|
||||||
|
|
||||||
const node = svg.append('g')
|
const node = g.append('g')
|
||||||
.attr('stroke-linejoin', 'round')
|
.attr('stroke-linejoin', 'round')
|
||||||
.attr('stroke-width', 3)
|
.attr('stroke-width', 3)
|
||||||
.selectAll('g')
|
.selectAll('g')
|
||||||
@ -371,9 +394,12 @@ export default {
|
|||||||
<style lang='scss'>
|
<style lang='scss'>
|
||||||
.admin-pages-visualize-svg {
|
.admin-pages-visualize-svg {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
|
// 100vh - header - title section - footer - content padding
|
||||||
|
height: calc(100vh - 64px - 92px - 32px - 16px);
|
||||||
|
|
||||||
> svg {
|
> svg {
|
||||||
height: 100vh;
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
Loading…
Reference in New Issue
Block a user