universal_table/app/assets/javascripts/mind_map/jsmind/jsmind.layout_provider.js

446 lines
14 KiB
JavaScript
Executable File

import { logger, Direction, EventType } from './jsmind.common.js'
export class LayoutProvider {
constructor(jm, options) {
this.opts = options
this.jm = jm
this.isside = this.opts.mode == 'side'
this.bounds = null
this.cache_valid = false
}
init() {
logger.debug('layout.init')
}
reset() {
logger.debug('layout.reset')
this.bounds = { n: 0, s: 0, w: 0, e: 0 }
}
calculate_next_child_direction(node) {
if (this.isside) {
return Direction.right
}
var children = node.children || []
var children_len = children.length
var r = 0
for (var i = 0; i < children_len; i++) {
if (children[i].direction === Direction.left) {
r--
} else {
r++
}
}
return children_len > 1 && r > 0 ? Direction.left : Direction.right
}
layout() {
logger.debug('layout.layout')
this.layout_direction()
this.layout_offset()
}
layout_direction() {
this._layout_direction_root()
}
_layout_direction_root() {
var node = this.jm.mind.root
var layout_data = null
if ('layout' in node._data) {
layout_data = node._data.layout
} else {
layout_data = {}
node._data.layout = layout_data
}
var children = node.children
var children_count = children.length
layout_data.direction = Direction.center
layout_data.side_index = 0
if (this.isside) {
var i = children_count
while (i--) {
this._layout_direction_side(children[i], Direction.right, i)
}
} else {
var i = children_count
var subnode = null
while (i--) {
subnode = children[i]
if (subnode.direction == Direction.left) {
this._layout_direction_side(subnode, Direction.left, i)
} else {
this._layout_direction_side(subnode, Direction.right, i)
}
}
}
}
_layout_direction_side(node, direction, side_index) {
var layout_data = null
if ('layout' in node._data) {
layout_data = node._data.layout
} else {
layout_data = {}
node._data.layout = layout_data
}
var children = node.children
var children_count = children.length
layout_data.direction = direction
layout_data.side_index = side_index
var i = children_count
while (i--) {
this._layout_direction_side(children[i], direction, i)
}
}
layout_offset() {
var node = this.jm.mind.root
var layout_data = node._data.layout
layout_data.offset_x = 0
layout_data.offset_y = 0
layout_data.outer_height = 0
var children = node.children
var i = children.length
var left_nodes = []
var right_nodes = []
var subnode = null
while (i--) {
subnode = children[i]
if (subnode._data.layout.direction == Direction.right) {
right_nodes.unshift(subnode)
} else {
left_nodes.unshift(subnode)
}
}
layout_data.left_nodes = left_nodes
layout_data.right_nodes = right_nodes
layout_data.outer_height_left = this._layout_offset_subnodes(left_nodes)
layout_data.outer_height_right = this._layout_offset_subnodes(right_nodes)
this.bounds.e = node._data.view.width / 2
this.bounds.w = 0 - this.bounds.e
this.bounds.n = 0
this.bounds.s = Math.max(layout_data.outer_height_left, layout_data.outer_height_right)
}
// layout both the x and y axis
_layout_offset_subnodes(nodes) {
var total_height = 0
var nodes_count = nodes.length
var i = nodes_count
var node = null
var node_outer_height = 0
var layout_data = null
var base_y = 0
var pd = null // parent._data
while (i--) {
node = nodes[i]
layout_data = node._data.layout
if (pd == null) {
pd = node.parent._data
}
node_outer_height = this._layout_offset_subnodes(node.children)
if (!node.expanded) {
node_outer_height = 0
this.set_visible(node.children, false)
}
node_outer_height = Math.max(node._data.view.height, node_outer_height)
if (node.children.length > 1) {
node_outer_height += this.opts.cousin_space
}
layout_data.outer_height = node_outer_height
layout_data.offset_y = base_y - node_outer_height / 2
layout_data.offset_x =
this.opts.hspace * layout_data.direction +
(pd.view.width * (pd.layout.direction + layout_data.direction)) / 2
if (!node.parent.isroot) {
layout_data.offset_x += this.opts.pspace * layout_data.direction
}
base_y = base_y - node_outer_height - this.opts.vspace
total_height += node_outer_height
}
if (nodes_count > 1) {
total_height += this.opts.vspace * (nodes_count - 1)
}
i = nodes_count
var middle_height = total_height / 2
while (i--) {
node = nodes[i]
node._data.layout.offset_y += middle_height
}
return total_height
}
// layout the y axis only, for collapse/expand a node
_layout_offset_subnodes_height(nodes) {
var total_height = 0
var nodes_count = nodes.length
var i = nodes_count
var node = null
var node_outer_height = 0
var layout_data = null
var base_y = 0
var pd = null // parent._data
while (i--) {
node = nodes[i]
layout_data = node._data.layout
if (pd == null) {
pd = node.parent._data
}
node_outer_height = this._layout_offset_subnodes_height(node.children)
if (!node.expanded) {
node_outer_height = 0
}
node_outer_height = Math.max(node._data.view.height, node_outer_height)
if (node.children.length > 1) {
node_outer_height += this.opts.cousin_space
}
layout_data.outer_height = node_outer_height
layout_data.offset_y = base_y - node_outer_height / 2
base_y = base_y - node_outer_height - this.opts.vspace
total_height += node_outer_height
}
if (nodes_count > 1) {
total_height += this.opts.vspace * (nodes_count - 1)
}
i = nodes_count
var middle_height = total_height / 2
while (i--) {
node = nodes[i]
node._data.layout.offset_y += middle_height
}
return total_height
}
get_node_offset(node) {
var layout_data = node._data.layout
var offset_cache = null
if ('_offset_' in layout_data && this.cache_valid) {
offset_cache = layout_data._offset_
} else {
offset_cache = { x: -1, y: -1 }
layout_data._offset_ = offset_cache
}
if (offset_cache.x == -1 || offset_cache.y == -1) {
var x = layout_data.offset_x
var y = layout_data.offset_y
if (!node.isroot) {
var offset_p = this.get_node_offset(node.parent)
x += offset_p.x
y += offset_p.y
}
offset_cache.x = x
offset_cache.y = y
}
return offset_cache
}
get_node_point(node) {
var view_data = node._data.view
var offset_p = this.get_node_offset(node)
var p = {}
p.x = offset_p.x + (view_data.width * (node._data.layout.direction - 1)) / 2
p.y = offset_p.y - view_data.height / 2
return p
}
get_node_point_in(node) {
var p = this.get_node_offset(node)
return p
}
get_node_point_out(node) {
var layout_data = node._data.layout
var pout_cache = null
if ('_pout_' in layout_data && this.cache_valid) {
pout_cache = layout_data._pout_
} else {
pout_cache = { x: -1, y: -1 }
layout_data._pout_ = pout_cache
}
if (pout_cache.x == -1 || pout_cache.y == -1) {
if (node.isroot) {
pout_cache.x = 0
pout_cache.y = 0
} else {
var view_data = node._data.view
var offset_p = this.get_node_offset(node)
pout_cache.x =
offset_p.x + (view_data.width + this.opts.pspace) * node._data.layout.direction
pout_cache.y = offset_p.y
}
}
return pout_cache
}
get_expander_point(node) {
var p = this.get_node_point_out(node)
var ex_p = {}
if (node._data.layout.direction == Direction.right) {
ex_p.x = p.x - this.opts.pspace
} else {
ex_p.x = p.x
}
ex_p.y = p.y - Math.ceil(this.opts.pspace / 2)
return ex_p
}
get_min_size() {
var nodes = this.jm.mind.nodes
var node = null
var pout = null
for (var node_id in nodes) {
node = nodes[node_id]
pout = this.get_node_point_out(node)
if (pout.x > this.bounds.e) {
this.bounds.e = pout.x
}
if (pout.x < this.bounds.w) {
this.bounds.w = pout.x
}
}
return {
w: this.bounds.e - this.bounds.w,
h: this.bounds.s - this.bounds.n,
}
}
toggle_node(node) {
if (node.isroot) {
return
}
if (node.expanded) {
this.collapse_node(node)
} else {
this.expand_node(node)
}
}
expand_node(node) {
node.expanded = true
this.part_layout(node)
this.set_visible(node.children, true)
this.jm.invoke_event_handle(EventType.show, {
evt: 'expand_node',
data: [],
node: node.id,
})
}
collapse_node(node) {
node.expanded = false
this.part_layout(node)
this.set_visible(node.children, false)
this.jm.invoke_event_handle(EventType.show, {
evt: 'collapse_node',
data: [],
node: node.id,
})
}
expand_all() {
var nodes = this.jm.mind.nodes
var c = 0
var node
for (var node_id in nodes) {
node = nodes[node_id]
if (!node.expanded) {
node.expanded = true
c++
}
}
if (c > 0) {
var root = this.jm.mind.root
this.part_layout(root)
this.set_visible(root.children, true)
}
}
collapse_all() {
var nodes = this.jm.mind.nodes
var c = 0
var node
for (var node_id in nodes) {
node = nodes[node_id]
if (node.expanded && !node.isroot) {
node.expanded = false
c++
}
}
if (c > 0) {
var root = this.jm.mind.root
this.part_layout(root)
this.set_visible(root.children, true)
}
}
expand_to_depth(target_depth, curr_nodes, curr_depth) {
if (target_depth < 1) {
return
}
var nodes = curr_nodes || this.jm.mind.root.children
var depth = curr_depth || 1
var i = nodes.length
var node = null
while (i--) {
node = nodes[i]
if (depth < target_depth) {
if (!node.expanded) {
this.expand_node(node)
}
this.expand_to_depth(target_depth, node.children, depth + 1)
}
if (depth == target_depth) {
if (node.expanded) {
this.collapse_node(node)
}
}
}
}
part_layout(node) {
var root = this.jm.mind.root
if (!!root) {
var root_layout_data = root._data.layout
if (node.isroot) {
root_layout_data.outer_height_right = this._layout_offset_subnodes_height(
root_layout_data.right_nodes
)
root_layout_data.outer_height_left = this._layout_offset_subnodes_height(
root_layout_data.left_nodes
)
} else {
if (node._data.layout.direction == Direction.right) {
root_layout_data.outer_height_right = this._layout_offset_subnodes_height(
root_layout_data.right_nodes
)
} else {
root_layout_data.outer_height_left = this._layout_offset_subnodes_height(
root_layout_data.left_nodes
)
}
}
this.bounds.s = Math.max(
root_layout_data.outer_height_left,
root_layout_data.outer_height_right
)
this.cache_valid = false
} else {
logger.warn('can not found root node')
}
}
set_visible(nodes, visible) {
var i = nodes.length
var node = null
var layout_data = null
while (i--) {
node = nodes[i]
layout_data = node._data.layout
if (node.expanded) {
this.set_visible(node.children, visible)
} else {
this.set_visible(node.children, false)
}
if (!node.isroot) {
node._data.layout.visible = visible
}
}
}
is_expand(node) {
return node.expanded
}
is_visible(node) {
var layout_data = node._data.layout
if ('visible' in layout_data && !layout_data.visible) {
return false
} else {
return true
}
}
}