1.2.0
This is the contents of the ngPaneManager
service, which contains various auxiliary functions for working with ngPaneManager layouts and configurations.
Example of how to use the service:
app.controller('SomePanelController', function($scope, ngPaneManager) {
$scope.config = {
layout: {
id: 'some-panel',
title: 'Some Panel',
panel: {
template: '<div>I am some panel!</div>'
}
}
};
$scope.config.layout = ngPaneManager.removeLeafWithId($scope.config.layout, 'some-panel');
});
Finds all the leaf nodes in a layout.
(object)
The node in the layout from which to find leaves.
Array
:
An array of leaf nodes from the given layout.
var layout = {
split: 'vertical',
ratio: 0.5,
children: [
{
split: 'horizontal',
ratio: 0.5,
children: [
{
id: 'panel-1',
title: 'Panel 1',
panel: {
template: '<div>I am panel 1.</div>'
}
},
{
id: 'panel-2',
title: 'Panel 2',
panel: {
template: '<div>I am panel 2.</div>'
}
}
]
},
{
id: 'panel-3',
title: 'Panel 3',
panel: {
template: '<div>I am panel 3.</div>'
}
}
]
};
var leaves = ngPaneManager.findLeaves(layout); // leaves is an array containing nodes 'panel-1', 'panel-2', 'panel-3'
Finds the leaf node in a layout with the given ID.
object
:
If found, the leaf node with the given ID, otherwise null.
var layout = {
split: 'vertical',
ratio: 0.5,
children: [
{
split: 'horizontal',
ratio: 0.5,
children: [
{
id: 'panel-1',
title: 'Panel 1',
panel: {
template: '<div>I am panel 1.</div>'
}
},
{
id: 'panel-2',
title: 'Panel 2',
panel: {
template: '<div>I am panel 2.</div>'
}
}
]
},
{
id: 'panel-3',
title: 'Panel 3',
panel: {
template: '<div>I am panel 3.</div>'
}
}
]
};
var leaf = ngPaneManager.findLeafWithId(layout, 'panel-2'); // returns the second child of the horizontal split
var notfound = ngPaneManager.findLeafWithId(layout, 'nope'); // returns null
Inserts a leaf node into the layout. leaf
must have a gravity
property on it, and all existing leaves
in the given layout must have gravity
defined as well. Set your layout to the return value of this function.
Insertion currently supports four kinds of gravity
: left
, center
, right
, bottom
.
The algorithm tries to recognize where the "left", "center", "right", and "bottom" parts of the layout are. If the panel has left
gravity, then it is placed in the left region of the layout (either with a vertical split, if there is no left region, or
with a tab split). For center gravity, the center region of the layout is identified, and the leaf is inserted either with a
vertical split or a tab split. For right gravity, the right region of the layout is identified, and the leaf is inserted either with a
vertical split or a tab split. For bottom gravity, the bottom region of the layout is identified, and the leaf is inserted either with a
horizontal split or a tab split.
If you want certain leaf nodes to always form a tab split (regardless of gravity), you can use the optional group
property.
When insertLeaf
is called, it will first check if leaf
has a group
property. If it does, it will
search the layout for other nodes with the same group
: if it finds another node with the same group, it will form a tab
split with that node instead of splitting by gravity.
See the test page 3.htm for an interactive demo of how insertLeaf works.
(object)
The root node of the layout.
(object)
The leaf to insert.
(Number)
The ratio of the leaf in its split (only used if a vertical split is created).
object
:
The new root of the layout.
$scope.config = {
layout: {
id: 'center-panel',
title: 'Center Panel',
panel: {
template: '<div>I want to be in the center!</div>'
},
gravity: 'center'
}
};
var leftLeaf = {
id: 'left-panel',
title: 'Left Panel',
panel: {
template: '<div>I want to be at the left!</div>'
},
gravity: 'left'
};
// After this, $scope.config.layout will be
// {
// split: 'vertical',
// ratio: 0.5,
// children: [
// {
// id: 'left-panel',
// title: 'Left Panel',
// panel: {
// template: '<div>I want to be at the left!</div>'
// },
// gravity: 'left'
// },
// {
// id: 'center-panel',
// title: 'Center Panel',
// panel: {
// template: '<div>I want to be in the center!</div>'
// }
// }
// ]
// }
$scope.config.layout = ngPaneManager.insertLeaf($scope.config.layout, leftLeaf, 0.5);
Removes the child of a split. Set your layout to the return value of this function.
(object)
The root node of the layout.
(object)
The split from which to remove the child.
(Number)
The index of the child to remove.
object
:
The new root node.
$scope.config = {
layout: {
split: 'vertical',
ratio: 0.5,
children: [
{
id: 'panel-1',
title: 'Panel 1',
panel: {
template: '<div>I am panel 1</div>'
}
},
{
id: 'panel-2',
title: 'Panel 2',
panel: {
template: '<div>I am panel 2</div>'
}
}
]
}
};
// After this, $scope.config.layout will be
// {
// id: 'panel-2',
// title: 'Panel 2',
// panel: {
// template: '<div>I am panel 2</div>'
// }
// }
$scope.config.layout = ngPaneManager.removeSplitChild($scope.config.layout, $scope.config.layout, 0);
Sets activeTabIndex on all the ancestors of the given node such that the node is visible. This is useful if you want to "focus" onto a panel that may be concealed behind tab splits.
Performs syntax validity checks on the layout (the validity of templates and templateUrls is not checked, however). ngPaneManager automatically call this function when processing the layout, normally you do not need to call it.
(object)
The node in the layout from which to perform validation.
Creates a reference to a value in the config's refs
property.
This function is used to make the layout serializable (e.g. if your application needs to be able to store the panel state in a cookie or local storage). It is not required to use refs for general use (only if you want serializability).
(string)
The name of the ref.
string
:
A placeholder string that will be expanded by ngPaneManager to the value given in the config's
refs
property.
// Example of using a ref in a layout
$scope.config = {
refs: {
SomePanelController: function($scope) {
$scope.something = 'Hello!';
}
},
layout: {
id: 'some-panel',
title: 'Some Panel',
panel: {
template: '<div>{{something}}</div>',
controller: ngPaneManager.ref('SomePanelController')
}
}
};
Expands a ref to the value it refers to in the config's refs
property.
(any)
The value to dereference.
(object)
The configuration object.
string
:
If the value is a reference, the corresponding value in the config's
refs
property. Otherwise, just returns
x
.
var config = {
refs: {
test: function x() {
console.log('Hello World!');
}
}
};
var ref = ngPaneManager.ref('test');
var f = ngPaneManager.deref(ref, config);
f(); // prints "Hello World!"
var num = ngPaneManager.deref(5, config); // 5 is not a ref, so deref just returns it
Gets the node at the given path.
(object)
The root node of the layout.
(any)
(object)
The node to compute the path for.
object
:
The node at the given path.
Computes the path to the node.
(any)
(any)
Array<Number>
:
Path to the node, or null if the node cannot be found
Pattern matching (used for insertLeaf)
The way this works is first we try to match against 'patterns'. Each pattern in 'patterns' is a tree, where each node is either a split or a mapping from possible gravities to simpler gravity (this will be defined shortly). If a match is found, each mapping is used to convert the layouts' gravities into a simpler hierarchy. For example, if insertLeaf is given a layout that looks like this:
|---|---|---| | 1 | 2 | 3 | |---|---|---| L L R
and has the following tree:
vsplit / \ 1(L) vsplit / \ 2(L) 3(R)
(L/R means left/right gravity, the number is the panel id).
This layout will match the pattern
{ split: 'vertical', children: [ [['left', 'right'], 'left'], { split: 'vertical', children: [ [[null, 'left', 'right', 'center'], 'center'], [['left', 'right'], 'right'] ] } ] }
and provide a simpler gravity hierarchy for this tree like this:
|---|---|---| | 1 | 2 | 3 | |---|---|---| L C R
(C means center gravity).
Next we consider the gravity of the panel being inserted. Say we want to insert a panel 4 with center gravity.
We look at the simplified hierarchy. If we find a node with the same gravity as that of the panel being inserted, we will create a tab split with that node. In this case, panel 4 will create a tab split with panel 2.
It's possible that the simpler hierarchy won't have the gravity we're looking for. For example, say we did the following transformation:
|---|---| |---|---| | 1 | 2 | -> | 1 | 2 | |---|---| |---|---| L L L R
If we want to insert a panel 3 with center gravity, we need something to figure out how to perform this insertion, since there is no node we can do a tab split with.
Now we look at the insert strategies for the gravity of the panel being inserted. For center, this is the array insertCenterStrategies.
We find the strategy that matches the simplified hierarchy, in this case that would be:
{ from: { split: 'vertical', children: [ 'left', 'right' ] }, split: 'vertical', index: 1, node: function(node) { return node.children[0]; } }
This strategy says to insert the panel by performing a vertical split with the left node and having the center panel be the second item in the vertical split.
In summary, pattern matching works like this: 1. Match 'patterns', simplify the gravity hierarchy using the match. 2. If the simplified hierarchy contains a panel with the same gravity as that of the panel being inserted, create a tab split with that node, otherwise... 3. Find the appropriate insert strategy to insert the panel into the hierarchy.