You've already forked wakapi-readme-stats
Bar graph added.
This commit is contained in:
27
node_modules/vega-view-transforms/LICENSE
generated
vendored
Normal file
27
node_modules/vega-view-transforms/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
Copyright (c) 2015-2018, University of Washington Interactive Data Lab
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
3. Neither the name of the copyright holder nor the names of its contributors
|
||||
may be used to endorse or promote products derived from this software
|
||||
without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
|
||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
15
node_modules/vega-view-transforms/README.md
generated
vendored
Normal file
15
node_modules/vega-view-transforms/README.md
generated
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
# vega-view-transforms
|
||||
|
||||
View-specific transforms for Vega dataflows.
|
||||
|
||||
This package provides the following Vega data transform:
|
||||
|
||||
- [**Identifier**](https://vega.github.io/vega/docs/transforms/identifier/) [<>](https://github.com/vega/vega/blob/master/packages/vega-view-transforms/src/Identifier.js "Source")
|
||||
|
||||
And provides the following internal transforms:
|
||||
|
||||
- **Bound** [<>](https://github.com/vega/vega/blob/master/packages/vega-view-transforms/src/Bound.js "Source")
|
||||
- **Mark** [<>](https://github.com/vega/vega/blob/master/packages/vega-view-transforms/src/Mark.js "Source")
|
||||
- **Overlap** [<>](https://github.com/vega/vega/blob/master/packages/vega-view-transforms/src/Overlap.js "Source")
|
||||
- **Render** [<>](https://github.com/vega/vega/blob/master/packages/vega-view-transforms/src/Render.js "Source")
|
||||
- **ViewLayout** [<>](https://github.com/vega/vega/blob/master/packages/vega-view-transforms/src/ViewLayout.js "Source")
|
||||
1385
node_modules/vega-view-transforms/build/vega-view-transforms.js
generated
vendored
Normal file
1385
node_modules/vega-view-transforms/build/vega-view-transforms.js
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1
node_modules/vega-view-transforms/build/vega-view-transforms.min.js
generated
vendored
Normal file
1
node_modules/vega-view-transforms/build/vega-view-transforms.min.js
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
6
node_modules/vega-view-transforms/index.js
generated
vendored
Normal file
6
node_modules/vega-view-transforms/index.js
generated
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
export {default as bound} from './src/Bound';
|
||||
export {default as identifier} from './src/Identifier';
|
||||
export {default as mark} from './src/Mark';
|
||||
export {default as overlap} from './src/Overlap';
|
||||
export {default as render} from './src/Render';
|
||||
export {default as viewlayout} from './src/ViewLayout';
|
||||
69
node_modules/vega-view-transforms/package.json
generated
vendored
Normal file
69
node_modules/vega-view-transforms/package.json
generated
vendored
Normal file
@@ -0,0 +1,69 @@
|
||||
{
|
||||
"_from": "vega-view-transforms@~4.5.5",
|
||||
"_id": "vega-view-transforms@4.5.5",
|
||||
"_inBundle": false,
|
||||
"_integrity": "sha512-HFTA6j2zFKRlfBlS6b9tmLLDNt7g78ZoyKFAT9fCm3X0KLT6FTn13PiiB4KppMg40nwgm0c2KUQmjnC6fGgIdQ==",
|
||||
"_location": "/vega-view-transforms",
|
||||
"_phantomChildren": {},
|
||||
"_requested": {
|
||||
"type": "range",
|
||||
"registry": true,
|
||||
"raw": "vega-view-transforms@~4.5.5",
|
||||
"name": "vega-view-transforms",
|
||||
"escapedName": "vega-view-transforms",
|
||||
"rawSpec": "~4.5.5",
|
||||
"saveSpec": null,
|
||||
"fetchSpec": "~4.5.5"
|
||||
},
|
||||
"_requiredBy": [
|
||||
"/vega"
|
||||
],
|
||||
"_resolved": "https://registry.npmjs.org/vega-view-transforms/-/vega-view-transforms-4.5.5.tgz",
|
||||
"_shasum": "b934da0fc1b41c10039300c036cfa38a56c83631",
|
||||
"_spec": "vega-view-transforms@~4.5.5",
|
||||
"_where": "/home/prabhatdev/Documents/opensource/gitHubStats/waka-readme-stats/node_modules/vega",
|
||||
"author": {
|
||||
"name": "Jeffrey Heer",
|
||||
"url": "http://idl.cs.washington.edu"
|
||||
},
|
||||
"bugs": {
|
||||
"url": "https://github.com/vega/vega/issues"
|
||||
},
|
||||
"bundleDependencies": false,
|
||||
"dependencies": {
|
||||
"vega-dataflow": "^5.6.0",
|
||||
"vega-scenegraph": "^4.8.0",
|
||||
"vega-util": "^1.14.0"
|
||||
},
|
||||
"deprecated": false,
|
||||
"description": "View-specific transforms for Vega dataflows.",
|
||||
"devDependencies": {
|
||||
"vega-transforms": "*"
|
||||
},
|
||||
"gitHead": "48c85218f2202242171aa569f2dca0f53cf2b51f",
|
||||
"homepage": "https://github.com/vega/vega#readme",
|
||||
"keywords": [
|
||||
"vega",
|
||||
"view",
|
||||
"layout"
|
||||
],
|
||||
"license": "BSD-3-Clause",
|
||||
"main": "build/vega-view-transforms.js",
|
||||
"module": "index",
|
||||
"name": "vega-view-transforms",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/vega/vega.git"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "yarn rollup",
|
||||
"postbuild": "terser build/vega-view-transforms.js -c -m -o build/vega-view-transforms.min.js",
|
||||
"postpublish": "git push && git push --tags",
|
||||
"prebuild": "rimraf build && mkdir build",
|
||||
"prepublishOnly": "yarn test && yarn build",
|
||||
"pretest": "yarn prebuild && yarn rollup",
|
||||
"rollup": "rollup -g vega-dataflow:vega,vega-scenegraph:vega,vega-util:vega -f umd -n vega.transforms -o build/vega-view-transforms.js -- index.js",
|
||||
"test": "tape 'test/**/*-test.js'"
|
||||
},
|
||||
"version": "4.5.5"
|
||||
}
|
||||
79
node_modules/vega-view-transforms/src/Bound.js
generated
vendored
Normal file
79
node_modules/vega-view-transforms/src/Bound.js
generated
vendored
Normal file
@@ -0,0 +1,79 @@
|
||||
import {AxisRole, Group, LegendRole, TitleRole} from './constants';
|
||||
import {Transform} from 'vega-dataflow';
|
||||
import {Marks, boundClip} from 'vega-scenegraph';
|
||||
import {inherits} from 'vega-util';
|
||||
|
||||
/**
|
||||
* Calculate bounding boxes for scenegraph items.
|
||||
* @constructor
|
||||
* @param {object} params - The parameters for this operator.
|
||||
* @param {object} params.mark - The scenegraph mark instance to bound.
|
||||
*/
|
||||
export default function Bound(params) {
|
||||
Transform.call(this, null, params);
|
||||
}
|
||||
|
||||
var prototype = inherits(Bound, Transform);
|
||||
|
||||
prototype.transform = function(_, pulse) {
|
||||
var view = pulse.dataflow,
|
||||
mark = _.mark,
|
||||
type = mark.marktype,
|
||||
entry = Marks[type],
|
||||
bound = entry.bound,
|
||||
markBounds = mark.bounds, rebound;
|
||||
|
||||
if (entry.nested) {
|
||||
// multi-item marks have a single bounds instance
|
||||
if (mark.items.length) view.dirty(mark.items[0]);
|
||||
markBounds = boundItem(mark, bound);
|
||||
mark.items.forEach(function(item) {
|
||||
item.bounds.clear().union(markBounds);
|
||||
});
|
||||
}
|
||||
|
||||
else if (type === Group || _.modified()) {
|
||||
// operator parameters modified -> re-bound all items
|
||||
// updates group bounds in response to modified group content
|
||||
pulse.visit(pulse.MOD, item => view.dirty(item));
|
||||
markBounds.clear();
|
||||
mark.items.forEach(item => markBounds.union(boundItem(item, bound)));
|
||||
|
||||
// force reflow for axes/legends/titles to propagate any layout changes
|
||||
switch (mark.role) {
|
||||
case AxisRole:
|
||||
case LegendRole:
|
||||
case TitleRole:
|
||||
pulse.reflow();
|
||||
}
|
||||
}
|
||||
|
||||
else {
|
||||
// incrementally update bounds, re-bound mark as needed
|
||||
rebound = pulse.changed(pulse.REM);
|
||||
|
||||
pulse.visit(pulse.ADD, item => {
|
||||
markBounds.union(boundItem(item, bound));
|
||||
});
|
||||
|
||||
pulse.visit(pulse.MOD, item => {
|
||||
rebound = rebound || markBounds.alignsWith(item.bounds);
|
||||
view.dirty(item);
|
||||
markBounds.union(boundItem(item, bound));
|
||||
});
|
||||
|
||||
if (rebound) {
|
||||
markBounds.clear();
|
||||
mark.items.forEach(item => markBounds.union(item.bounds));
|
||||
}
|
||||
}
|
||||
|
||||
// ensure mark bounds do not exceed any clipping region
|
||||
boundClip(mark);
|
||||
|
||||
return pulse.modifies('bounds');
|
||||
};
|
||||
|
||||
function boundItem(item, bound, opt) {
|
||||
return bound(item.bounds.clear(), item, opt);
|
||||
}
|
||||
50
node_modules/vega-view-transforms/src/Identifier.js
generated
vendored
Normal file
50
node_modules/vega-view-transforms/src/Identifier.js
generated
vendored
Normal file
@@ -0,0 +1,50 @@
|
||||
import {Transform} from 'vega-dataflow';
|
||||
import {inherits} from 'vega-util';
|
||||
|
||||
var COUNTER_NAME = ':vega_identifier:';
|
||||
|
||||
/**
|
||||
* Adds a unique identifier to all added tuples.
|
||||
* This transform creates a new signal that serves as an id counter.
|
||||
* As a result, the id counter is shared across all instances of this
|
||||
* transform, generating unique ids across multiple data streams. In
|
||||
* addition, this signal value can be included in a snapshot of the
|
||||
* dataflow state, enabling correct resumption of id allocation.
|
||||
* @constructor
|
||||
* @param {object} params - The parameters for this operator.
|
||||
* @param {string} params.as - The field name for the generated identifier.
|
||||
*/
|
||||
export default function Identifier(params) {
|
||||
Transform.call(this, 0, params);
|
||||
}
|
||||
|
||||
Identifier.Definition = {
|
||||
'type': 'Identifier',
|
||||
'metadata': {'modifies': true},
|
||||
'params': [
|
||||
{ 'name': 'as', 'type': 'string', 'required': true }
|
||||
]
|
||||
};
|
||||
|
||||
var prototype = inherits(Identifier, Transform);
|
||||
|
||||
prototype.transform = function(_, pulse) {
|
||||
var counter = getCounter(pulse.dataflow),
|
||||
id = counter.value,
|
||||
as = _.as;
|
||||
|
||||
pulse.visit(pulse.ADD, t => {
|
||||
if (!t[as]) t[as] = ++id;
|
||||
});
|
||||
|
||||
counter.set(this.value = id);
|
||||
return pulse;
|
||||
};
|
||||
|
||||
function getCounter(view) {
|
||||
var counter = view._signals[COUNTER_NAME];
|
||||
if (!counter) {
|
||||
view._signals[COUNTER_NAME] = (counter = view.add(0));
|
||||
}
|
||||
return counter;
|
||||
}
|
||||
56
node_modules/vega-view-transforms/src/Mark.js
generated
vendored
Normal file
56
node_modules/vega-view-transforms/src/Mark.js
generated
vendored
Normal file
@@ -0,0 +1,56 @@
|
||||
import {Group} from './constants';
|
||||
import {Transform} from 'vega-dataflow';
|
||||
import {GroupItem, Item} from 'vega-scenegraph';
|
||||
import {inherits} from 'vega-util';
|
||||
|
||||
/**
|
||||
* Bind scenegraph items to a scenegraph mark instance.
|
||||
* @constructor
|
||||
* @param {object} params - The parameters for this operator.
|
||||
* @param {object} params.markdef - The mark definition for creating the mark.
|
||||
* This is an object of legal scenegraph mark properties which *must* include
|
||||
* the 'marktype' property.
|
||||
*/
|
||||
export default function Mark(params) {
|
||||
Transform.call(this, null, params);
|
||||
}
|
||||
|
||||
var prototype = inherits(Mark, Transform);
|
||||
|
||||
prototype.transform = function(_, pulse) {
|
||||
var mark = this.value;
|
||||
|
||||
// acquire mark on first invocation, bind context and group
|
||||
if (!mark) {
|
||||
mark = pulse.dataflow.scenegraph().mark(_.markdef, lookup(_), _.index);
|
||||
mark.group.context = _.context;
|
||||
if (!_.context.group) _.context.group = mark.group;
|
||||
mark.source = this.source; // point to upstream collector
|
||||
mark.clip = _.clip;
|
||||
mark.interactive = _.interactive;
|
||||
this.value = mark;
|
||||
}
|
||||
|
||||
// initialize entering items
|
||||
var Init = mark.marktype === Group ? GroupItem : Item;
|
||||
pulse.visit(pulse.ADD, item => Init.call(item, mark));
|
||||
|
||||
// update clipping and/or interactive status
|
||||
if (_.modified('clip') || _.modified('interactive')) {
|
||||
mark.clip = _.clip;
|
||||
mark.interactive = !!_.interactive;
|
||||
mark.zdirty = true; // force scenegraph re-eval
|
||||
pulse.reflow();
|
||||
}
|
||||
|
||||
// bind items array to scenegraph mark
|
||||
mark.items = pulse.source;
|
||||
return pulse;
|
||||
};
|
||||
|
||||
function lookup(_) {
|
||||
var g = _.groups, p = _.parent;
|
||||
return g && g.size === 1 ? g.get(Object.keys(g.object)[0])
|
||||
: g && p ? g.lookup(p)
|
||||
: null;
|
||||
}
|
||||
155
node_modules/vega-view-transforms/src/Overlap.js
generated
vendored
Normal file
155
node_modules/vega-view-transforms/src/Overlap.js
generated
vendored
Normal file
@@ -0,0 +1,155 @@
|
||||
import {Bottom, Top} from './constants';
|
||||
import {Transform} from 'vega-dataflow';
|
||||
import {Bounds} from 'vega-scenegraph';
|
||||
import {inherits, peek} from 'vega-util';
|
||||
|
||||
/**
|
||||
* Analyze items for overlap, changing opacity to hide items with
|
||||
* overlapping bounding boxes. This transform will preserve at least
|
||||
* two items (e.g., first and last) even if overlap persists.
|
||||
* @param {object} params - The parameters for this operator.
|
||||
* @param {function(*,*): number} [params.sort] - A comparator
|
||||
* function for sorting items.
|
||||
* @param {object} [params.method] - The overlap removal method to apply.
|
||||
* One of 'parity' (default, hide every other item until there is no
|
||||
* more overlap) or 'greedy' (sequentially scan and hide and items that
|
||||
* overlap with the last visible item).
|
||||
* @param {object} [params.boundScale] - A scale whose range should be used
|
||||
* to bound the items. Items exceeding the bounds of the scale range
|
||||
* will be treated as overlapping. If null or undefined, no bounds check
|
||||
* will be applied.
|
||||
* @param {object} [params.boundOrient] - The orientation of the scale
|
||||
* (top, bottom, left, or right) used to bound items. This parameter is
|
||||
* ignored if boundScale is null or undefined.
|
||||
* @param {object} [params.boundTolerance] - The tolerance in pixels for
|
||||
* bound inclusion testing (default 1). This specifies by how many pixels
|
||||
* an item's bounds may exceed the scale range bounds and not be culled.
|
||||
* @constructor
|
||||
*/
|
||||
export default function Overlap(params) {
|
||||
Transform.call(this, null, params);
|
||||
}
|
||||
|
||||
var prototype = inherits(Overlap, Transform);
|
||||
|
||||
var methods = {
|
||||
parity: items => {
|
||||
return items.filter((item, i) => i % 2 ? (item.opacity = 0) : 1);
|
||||
},
|
||||
greedy: (items, sep) => {
|
||||
var a;
|
||||
return items.filter((b, i) => {
|
||||
if (!i || !intersect(a.bounds, b.bounds, sep)) {
|
||||
a = b;
|
||||
return 1;
|
||||
} else {
|
||||
return b.opacity = 0;
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// compute bounding box intersection
|
||||
// including padding pixels of separation
|
||||
function intersect(a, b, sep) {
|
||||
return sep > Math.max(
|
||||
b.x1 - a.x2,
|
||||
a.x1 - b.x2,
|
||||
b.y1 - a.y2,
|
||||
a.y1 - b.y2
|
||||
);
|
||||
}
|
||||
|
||||
function hasOverlap(items, pad) {
|
||||
for (var i=1, n=items.length, a=items[0].bounds, b; i<n; a=b, ++i) {
|
||||
if (intersect(a, b = items[i].bounds, pad)) return true;
|
||||
}
|
||||
}
|
||||
|
||||
function hasBounds(item) {
|
||||
var b = item.bounds;
|
||||
return b.width() > 1 && b.height() > 1;
|
||||
}
|
||||
|
||||
function boundTest(scale, orient, tolerance) {
|
||||
var range = scale.range(),
|
||||
b = new Bounds();
|
||||
|
||||
if (orient === Top || orient === Bottom) {
|
||||
b.set(range[0], -Infinity, range[1], +Infinity);
|
||||
} else {
|
||||
b.set(-Infinity, range[0], +Infinity, range[1]);
|
||||
}
|
||||
b.expand(tolerance || 1);
|
||||
|
||||
return item => b.encloses(item.bounds);
|
||||
}
|
||||
|
||||
// reset all items to be fully opaque
|
||||
function reset(source) {
|
||||
source.forEach(item => item.opacity = 1);
|
||||
return source;
|
||||
}
|
||||
|
||||
// add all tuples to mod, fork pulse if parameters were modified
|
||||
// fork prevents cross-stream tuple pollution (e.g., pulse from scale)
|
||||
function reflow(pulse, _) {
|
||||
return pulse.reflow(_.modified()).modifies('opacity');
|
||||
}
|
||||
|
||||
prototype.transform = function(_, pulse) {
|
||||
var reduce = methods[_.method] || methods.parity,
|
||||
source = pulse.materialize(pulse.SOURCE).source,
|
||||
sep = _.separation || 0,
|
||||
items, test, bounds;
|
||||
|
||||
if (!source || !source.length) return;
|
||||
|
||||
if (!_.method) {
|
||||
// early exit if method is falsy
|
||||
if (_.modified('method')) {
|
||||
reset(source);
|
||||
pulse = reflow(pulse, _);
|
||||
}
|
||||
return pulse;
|
||||
}
|
||||
|
||||
// skip labels with no content
|
||||
source = source.filter(hasBounds);
|
||||
|
||||
// early exit, nothing to do
|
||||
if (!source.length) return;
|
||||
|
||||
if (_.sort) {
|
||||
source = source.slice().sort(_.sort);
|
||||
}
|
||||
|
||||
items = reset(source);
|
||||
pulse = reflow(pulse, _);
|
||||
|
||||
if (items.length >= 3 && hasOverlap(items, sep)) {
|
||||
do {
|
||||
items = reduce(items, sep);
|
||||
} while (items.length >= 3 && hasOverlap(items, sep));
|
||||
|
||||
if (items.length < 3 && !peek(source).opacity) {
|
||||
if (items.length > 1) peek(items).opacity = 0;
|
||||
peek(source).opacity = 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (_.boundScale && _.boundTolerance >= 0) {
|
||||
test = boundTest(_.boundScale, _.boundOrient, +_.boundTolerance);
|
||||
source.forEach(item => {
|
||||
if (!test(item)) item.opacity = 0;
|
||||
});
|
||||
}
|
||||
|
||||
// re-calculate mark bounds
|
||||
bounds = items[0].mark.bounds.clear();
|
||||
source.forEach(item => {
|
||||
if (item.opacity) bounds.union(item.bounds);
|
||||
});
|
||||
|
||||
return pulse;
|
||||
};
|
||||
24
node_modules/vega-view-transforms/src/Render.js
generated
vendored
Normal file
24
node_modules/vega-view-transforms/src/Render.js
generated
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
import {Transform} from 'vega-dataflow';
|
||||
import {inherits} from 'vega-util';
|
||||
|
||||
/**
|
||||
* Queue modified scenegraph items for rendering.
|
||||
* @constructor
|
||||
*/
|
||||
export default function Render(params) {
|
||||
Transform.call(this, null, params);
|
||||
}
|
||||
|
||||
var prototype = inherits(Render, Transform);
|
||||
|
||||
prototype.transform = function(_, pulse) {
|
||||
var view = pulse.dataflow;
|
||||
|
||||
pulse.visit(pulse.ALL, item => view.dirty(item));
|
||||
|
||||
// set z-index dirty flag as needed
|
||||
if (pulse.fields && pulse.fields['zindex']) {
|
||||
var item = pulse.source && pulse.source[0];
|
||||
if (item) item.mark.zdirty = true;
|
||||
}
|
||||
};
|
||||
206
node_modules/vega-view-transforms/src/ViewLayout.js
generated
vendored
Normal file
206
node_modules/vega-view-transforms/src/ViewLayout.js
generated
vendored
Normal file
@@ -0,0 +1,206 @@
|
||||
import {
|
||||
AxisRole, Bottom, ColFooter, ColHeader, ColTitle,
|
||||
Fit, FitX, FitY, FrameRole, Left, LegendRole,
|
||||
None, Pad, Padding, Right,
|
||||
RowFooter, RowHeader, RowTitle, ScopeRole, TitleRole, Top
|
||||
} from './constants';
|
||||
|
||||
import {axisLayout, isYAxis} from './layout/axis';
|
||||
import {gridLayout, trellisLayout} from './layout/grid';
|
||||
import {legendLayout, legendParams} from './layout/legend';
|
||||
import {titleLayout} from './layout/title';
|
||||
|
||||
import {Transform} from 'vega-dataflow';
|
||||
import {Bounds} from 'vega-scenegraph';
|
||||
import {inherits} from 'vega-util';
|
||||
|
||||
/**
|
||||
* Layout view elements such as axes and legends.
|
||||
* Also performs size adjustments.
|
||||
* @constructor
|
||||
* @param {object} params - The parameters for this operator.
|
||||
* @param {object} params.mark - Scenegraph mark of groups to layout.
|
||||
*/
|
||||
export default function ViewLayout(params) {
|
||||
Transform.call(this, null, params);
|
||||
}
|
||||
|
||||
var prototype = inherits(ViewLayout, Transform);
|
||||
|
||||
prototype.transform = function(_, pulse) {
|
||||
var view = pulse.dataflow;
|
||||
_.mark.items.forEach(group => {
|
||||
if (_.layout) trellisLayout(view, group, _.layout);
|
||||
layoutGroup(view, group, _);
|
||||
});
|
||||
return shouldReflow(_.mark.group) ? pulse.reflow() : pulse;
|
||||
};
|
||||
|
||||
function shouldReflow(group) {
|
||||
// We typically should reflow if layout is invoked (#2568), as child items
|
||||
// may have resized and reflow ensures group bounds are re-calculated.
|
||||
// However, legend entries have a special exception to avoid instability.
|
||||
// For example, if a selected legend symbol gains a stroke on hover,
|
||||
// we don't want to re-position subsequent elements in the legend.
|
||||
return group && group.mark.role !== 'legend-entry';
|
||||
}
|
||||
|
||||
function layoutGroup(view, group, _) {
|
||||
var items = group.items,
|
||||
width = Math.max(0, group.width || 0),
|
||||
height = Math.max(0, group.height || 0),
|
||||
viewBounds = new Bounds().set(0, 0, width, height),
|
||||
xBounds = viewBounds.clone(),
|
||||
yBounds = viewBounds.clone(),
|
||||
legends = [], title,
|
||||
mark, orient, b, i, n;
|
||||
|
||||
// layout axes, gather legends, collect bounds
|
||||
for (i=0, n=items.length; i<n; ++i) {
|
||||
mark = items[i];
|
||||
switch (mark.role) {
|
||||
case AxisRole:
|
||||
b = isYAxis(mark) ? xBounds : yBounds;
|
||||
b.union(axisLayout(view, mark, width, height));
|
||||
break;
|
||||
case TitleRole:
|
||||
title = mark;
|
||||
break;
|
||||
case LegendRole:
|
||||
legends.push(legendLayout(view, mark));
|
||||
break;
|
||||
case FrameRole:
|
||||
case ScopeRole:
|
||||
case RowHeader:
|
||||
case RowFooter:
|
||||
case RowTitle:
|
||||
case ColHeader:
|
||||
case ColFooter:
|
||||
case ColTitle:
|
||||
xBounds.union(mark.bounds);
|
||||
yBounds.union(mark.bounds);
|
||||
break;
|
||||
default:
|
||||
viewBounds.union(mark.bounds);
|
||||
}
|
||||
}
|
||||
|
||||
// layout legends, adjust viewBounds
|
||||
if (legends.length) {
|
||||
// group legends by orient
|
||||
const l = {};
|
||||
legends.forEach(item => {
|
||||
orient = item.orient || Right;
|
||||
if (orient !== None) (l[orient] || (l[orient] = [])).push(item);
|
||||
});
|
||||
|
||||
// perform grid layout for each orient group
|
||||
for (let orient in l) {
|
||||
const g = l[orient];
|
||||
gridLayout(view, g, legendParams(
|
||||
g, orient, _.legends, xBounds, yBounds, width, height
|
||||
));
|
||||
}
|
||||
|
||||
// update view bounds
|
||||
legends.forEach(item => {
|
||||
const b = item.bounds;
|
||||
|
||||
if (!b.equals(item._bounds)) {
|
||||
item.bounds = item._bounds;
|
||||
view.dirty(item); // dirty previous location
|
||||
item.bounds = b;
|
||||
view.dirty(item);
|
||||
}
|
||||
|
||||
if (_.autosize && _.autosize.type === Fit) {
|
||||
// For autosize fit, incorporate the orthogonal dimension only.
|
||||
// Legends that overrun the chart area will then be clipped;
|
||||
// otherwise the chart area gets reduced to nothing!
|
||||
switch(item.orient) {
|
||||
case Left:
|
||||
case Right:
|
||||
viewBounds.add(b.x1, 0).add(b.x2, 0);
|
||||
break;
|
||||
case Top:
|
||||
case Bottom:
|
||||
viewBounds.add(0, b.y1).add(0, b.y2);
|
||||
}
|
||||
} else {
|
||||
viewBounds.union(b);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// combine bounding boxes
|
||||
viewBounds.union(xBounds).union(yBounds);
|
||||
|
||||
// layout title, adjust bounds
|
||||
if (title) {
|
||||
viewBounds.union(titleLayout(view, title, width, height, viewBounds));
|
||||
}
|
||||
|
||||
// override aggregated view bounds if content is clipped
|
||||
if (group.clip) {
|
||||
viewBounds.set(0, 0, group.width || 0, group.height || 0);
|
||||
}
|
||||
|
||||
// perform size adjustment
|
||||
viewSizeLayout(view, group, viewBounds, _);
|
||||
}
|
||||
|
||||
function viewSizeLayout(view, group, viewBounds, _) {
|
||||
const auto = _.autosize || {},
|
||||
type = auto.type;
|
||||
|
||||
if (view._autosize < 1 || !type) return;
|
||||
|
||||
let viewWidth = view._width,
|
||||
viewHeight = view._height,
|
||||
width = Math.max(0, group.width || 0),
|
||||
left = Math.max(0, Math.ceil(-viewBounds.x1)),
|
||||
right = Math.max(0, Math.ceil(viewBounds.x2 - width)),
|
||||
height = Math.max(0, group.height || 0),
|
||||
top = Math.max(0, Math.ceil(-viewBounds.y1)),
|
||||
bottom = Math.max(0, Math.ceil(viewBounds.y2 - height));
|
||||
|
||||
if (auto.contains === Padding) {
|
||||
const padding = view.padding();
|
||||
viewWidth -= padding.left + padding.right;
|
||||
viewHeight -= padding.top + padding.bottom;
|
||||
}
|
||||
|
||||
if (type === None) {
|
||||
left = 0;
|
||||
top = 0;
|
||||
width = viewWidth;
|
||||
height = viewHeight;
|
||||
}
|
||||
|
||||
else if (type === Fit) {
|
||||
width = Math.max(0, viewWidth - left - right);
|
||||
height = Math.max(0, viewHeight - top - bottom);
|
||||
}
|
||||
|
||||
else if (type === FitX) {
|
||||
width = Math.max(0, viewWidth - left - right);
|
||||
viewHeight = height + top + bottom;
|
||||
}
|
||||
|
||||
else if (type === FitY) {
|
||||
viewWidth = width + left + right;
|
||||
height = Math.max(0, viewHeight - top - bottom);
|
||||
}
|
||||
|
||||
else if (type === Pad) {
|
||||
viewWidth = width + left + right;
|
||||
viewHeight = height + top + bottom;
|
||||
}
|
||||
|
||||
view._resizeView(
|
||||
viewWidth, viewHeight,
|
||||
width, height,
|
||||
[left, top],
|
||||
auto.resize
|
||||
);
|
||||
}
|
||||
48
node_modules/vega-view-transforms/src/constants.js
generated
vendored
Normal file
48
node_modules/vega-view-transforms/src/constants.js
generated
vendored
Normal file
@@ -0,0 +1,48 @@
|
||||
export const Top = 'top';
|
||||
export const Left = 'left';
|
||||
export const Right = 'right';
|
||||
export const Bottom = 'bottom';
|
||||
|
||||
export const TopLeft = 'top-left';
|
||||
export const TopRight = 'top-right';
|
||||
export const BottomLeft = 'bottom-left';
|
||||
export const BottomRight = 'bottom-right';
|
||||
|
||||
export const Start = 'start';
|
||||
export const Middle = 'middle';
|
||||
export const End = 'end';
|
||||
|
||||
export const X = 'x';
|
||||
export const Y = 'y';
|
||||
|
||||
export const Group = 'group';
|
||||
|
||||
export const AxisRole = 'axis';
|
||||
export const TitleRole = 'title';
|
||||
export const FrameRole = 'frame';
|
||||
export const ScopeRole = 'scope';
|
||||
export const LegendRole = 'legend';
|
||||
|
||||
export const RowHeader = 'row-header';
|
||||
export const RowFooter = 'row-footer';
|
||||
export const RowTitle = 'row-title';
|
||||
export const ColHeader = 'column-header';
|
||||
export const ColFooter = 'column-footer';
|
||||
export const ColTitle = 'column-title';
|
||||
|
||||
export const Padding = 'padding';
|
||||
|
||||
export const Symbols = 'symbol';
|
||||
|
||||
export const Fit = 'fit';
|
||||
export const FitX = 'fit-x';
|
||||
export const FitY = 'fit-y';
|
||||
export const Pad = 'pad';
|
||||
export const None = 'none';
|
||||
|
||||
export const All = 'all';
|
||||
export const Each = 'each';
|
||||
export const Flush = 'flush';
|
||||
|
||||
export const Column = 'column';
|
||||
export const Row = 'row';
|
||||
105
node_modules/vega-view-transforms/src/layout/axis.js
generated
vendored
Normal file
105
node_modules/vega-view-transforms/src/layout/axis.js
generated
vendored
Normal file
@@ -0,0 +1,105 @@
|
||||
import {Bottom, Left, Right, Top} from '../constants';
|
||||
import {set, tempBounds} from './util';
|
||||
import {boundStroke, multiLineOffset} from 'vega-scenegraph';
|
||||
|
||||
export function isYAxis(mark) {
|
||||
var orient = mark.items[0].orient;
|
||||
return orient === Left || orient === Right;
|
||||
}
|
||||
|
||||
function axisIndices(datum) {
|
||||
var index = +datum.grid;
|
||||
return [
|
||||
datum.ticks ? index++ : -1, // ticks index
|
||||
datum.labels ? index++ : -1, // labels index
|
||||
index + (+datum.domain) // title index
|
||||
];
|
||||
}
|
||||
|
||||
export function axisLayout(view, axis, width, height) {
|
||||
var item = axis.items[0],
|
||||
datum = item.datum,
|
||||
delta = item.translate != null ? item.translate : 0.5,
|
||||
orient = item.orient,
|
||||
indices = axisIndices(datum),
|
||||
range = item.range,
|
||||
offset = item.offset,
|
||||
position = item.position,
|
||||
minExtent = item.minExtent,
|
||||
maxExtent = item.maxExtent,
|
||||
title = datum.title && item.items[indices[2]].items[0],
|
||||
titlePadding = item.titlePadding,
|
||||
bounds = item.bounds,
|
||||
dl = title && multiLineOffset(title),
|
||||
x = 0, y = 0, i, s;
|
||||
|
||||
tempBounds.clear().union(bounds);
|
||||
bounds.clear();
|
||||
if ((i=indices[0]) > -1) bounds.union(item.items[i].bounds);
|
||||
if ((i=indices[1]) > -1) bounds.union(item.items[i].bounds);
|
||||
|
||||
// position axis group and title
|
||||
switch (orient) {
|
||||
case Top:
|
||||
x = position || 0;
|
||||
y = -offset;
|
||||
s = Math.max(minExtent, Math.min(maxExtent, -bounds.y1));
|
||||
bounds.add(0, -s).add(range, 0);
|
||||
if (title) axisTitleLayout(view, title, s, titlePadding, dl, 0, -1, bounds);
|
||||
break;
|
||||
case Left:
|
||||
x = -offset;
|
||||
y = position || 0;
|
||||
s = Math.max(minExtent, Math.min(maxExtent, -bounds.x1));
|
||||
bounds.add(-s, 0).add(0, range);
|
||||
if (title) axisTitleLayout(view, title, s, titlePadding, dl, 1, -1, bounds);
|
||||
break;
|
||||
case Right:
|
||||
x = width + offset;
|
||||
y = position || 0;
|
||||
s = Math.max(minExtent, Math.min(maxExtent, bounds.x2));
|
||||
bounds.add(0, 0).add(s, range);
|
||||
if (title) axisTitleLayout(view, title, s, titlePadding, dl, 1, 1, bounds);
|
||||
break;
|
||||
case Bottom:
|
||||
x = position || 0;
|
||||
y = height + offset;
|
||||
s = Math.max(minExtent, Math.min(maxExtent, bounds.y2));
|
||||
bounds.add(0, 0).add(range, s);
|
||||
if (title) axisTitleLayout(view, title, s, titlePadding, 0, 0, 1, bounds);
|
||||
break;
|
||||
default:
|
||||
x = item.x;
|
||||
y = item.y;
|
||||
}
|
||||
|
||||
// update bounds
|
||||
boundStroke(bounds.translate(x, y), item);
|
||||
|
||||
if (set(item, 'x', x + delta) | set(item, 'y', y + delta)) {
|
||||
item.bounds = tempBounds;
|
||||
view.dirty(item);
|
||||
item.bounds = bounds;
|
||||
view.dirty(item);
|
||||
}
|
||||
|
||||
return item.mark.bounds.clear().union(bounds);
|
||||
}
|
||||
|
||||
function axisTitleLayout(view, title, offset, pad, dl, isYAxis, sign, bounds) {
|
||||
const b = title.bounds;
|
||||
|
||||
if (title.auto) {
|
||||
const v = sign * (offset + dl + pad);
|
||||
let dx = 0, dy = 0;
|
||||
|
||||
view.dirty(title);
|
||||
isYAxis
|
||||
? dx = (title.x || 0) - (title.x = v)
|
||||
: dy = (title.y || 0) - (title.y = v);
|
||||
title.mark.bounds.clear().union(b.translate(-dx, -dy));
|
||||
view.dirty(title);
|
||||
}
|
||||
|
||||
bounds.union(b);
|
||||
}
|
||||
381
node_modules/vega-view-transforms/src/layout/grid.js
generated
vendored
Normal file
381
node_modules/vega-view-transforms/src/layout/grid.js
generated
vendored
Normal file
@@ -0,0 +1,381 @@
|
||||
import {
|
||||
All, AxisRole, ColFooter, ColHeader, ColTitle, Column, Each, End, Flush,
|
||||
Group, LegendRole, Middle, Row,
|
||||
RowFooter, RowHeader, RowTitle,
|
||||
TitleRole, X, Y
|
||||
} from '../constants';
|
||||
import {tempBounds} from './util';
|
||||
import {Bounds} from 'vega-scenegraph';
|
||||
import {isObject} from 'vega-util';
|
||||
|
||||
// aggregation functions for grid margin determination
|
||||
const min = (a, b) => Math.floor(Math.min(a, b));
|
||||
const max = (a, b) => Math.ceil(Math.max(a, b));
|
||||
|
||||
function gridLayoutGroups(group) {
|
||||
var groups = group.items,
|
||||
n = groups.length,
|
||||
i = 0, mark, items;
|
||||
|
||||
var views = {
|
||||
marks: [],
|
||||
rowheaders: [],
|
||||
rowfooters: [],
|
||||
colheaders: [],
|
||||
colfooters: [],
|
||||
rowtitle: null,
|
||||
coltitle: null
|
||||
};
|
||||
|
||||
// layout axes, gather legends, collect bounds
|
||||
for (; i<n; ++i) {
|
||||
mark = groups[i];
|
||||
items = mark.items;
|
||||
if (mark.marktype === Group) {
|
||||
switch (mark.role) {
|
||||
case AxisRole:
|
||||
case LegendRole:
|
||||
case TitleRole:
|
||||
break;
|
||||
case RowHeader: views.rowheaders.push(...items); break;
|
||||
case RowFooter: views.rowfooters.push(...items); break;
|
||||
case ColHeader: views.colheaders.push(...items); break;
|
||||
case ColFooter: views.colfooters.push(...items); break;
|
||||
case RowTitle: views.rowtitle = items[0]; break;
|
||||
case ColTitle: views.coltitle = items[0]; break;
|
||||
default: views.marks.push(...items);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return views;
|
||||
}
|
||||
|
||||
function bboxFlush(item) {
|
||||
return new Bounds().set(0, 0, item.width || 0, item.height || 0);
|
||||
}
|
||||
|
||||
function bboxFull(item) {
|
||||
var b = item.bounds.clone();
|
||||
return b.empty()
|
||||
? b.set(0, 0, 0, 0)
|
||||
: b.translate(-(item.x || 0), -(item.y || 0));
|
||||
}
|
||||
|
||||
function get(opt, key, d) {
|
||||
var v = isObject(opt) ? opt[key] : opt;
|
||||
return v != null ? v : (d !== undefined ? d : 0);
|
||||
}
|
||||
|
||||
function offsetValue(v) {
|
||||
return v < 0 ? Math.ceil(-v) : 0;
|
||||
}
|
||||
|
||||
export function gridLayout(view, groups, opt) {
|
||||
var dirty = !opt.nodirty,
|
||||
bbox = opt.bounds === Flush ? bboxFlush : bboxFull,
|
||||
bounds = tempBounds.set(0, 0, 0, 0),
|
||||
alignCol = get(opt.align, Column),
|
||||
alignRow = get(opt.align, Row),
|
||||
padCol = get(opt.padding, Column),
|
||||
padRow = get(opt.padding, Row),
|
||||
ncols = opt.columns || groups.length,
|
||||
nrows = ncols <= 0 ? 1 : Math.ceil(groups.length / ncols),
|
||||
n = groups.length,
|
||||
xOffset = Array(n), xExtent = Array(ncols), xMax = 0,
|
||||
yOffset = Array(n), yExtent = Array(nrows), yMax = 0,
|
||||
dx = Array(n), dy = Array(n), boxes = Array(n),
|
||||
m, i, c, r, b, g, px, py, x, y, offset;
|
||||
|
||||
for (i=0; i<ncols; ++i) xExtent[i] = 0;
|
||||
for (i=0; i<nrows; ++i) yExtent[i] = 0;
|
||||
|
||||
// determine offsets for each group
|
||||
for (i=0; i<n; ++i) {
|
||||
g = groups[i];
|
||||
b = boxes[i] = bbox(g);
|
||||
g.x = g.x || 0; dx[i] = 0;
|
||||
g.y = g.y || 0; dy[i] = 0;
|
||||
c = i % ncols;
|
||||
r = ~~(i / ncols);
|
||||
xMax = Math.max(xMax, px = Math.ceil(b.x2));
|
||||
yMax = Math.max(yMax, py = Math.ceil(b.y2));
|
||||
xExtent[c] = Math.max(xExtent[c], px);
|
||||
yExtent[r] = Math.max(yExtent[r], py);
|
||||
xOffset[i] = padCol + offsetValue(b.x1);
|
||||
yOffset[i] = padRow + offsetValue(b.y1);
|
||||
if (dirty) view.dirty(groups[i]);
|
||||
}
|
||||
|
||||
// set initial alignment offsets
|
||||
for (i=0; i<n; ++i) {
|
||||
if (i % ncols === 0) xOffset[i] = 0;
|
||||
if (i < ncols) yOffset[i] = 0;
|
||||
}
|
||||
|
||||
// enforce column alignment constraints
|
||||
if (alignCol === Each) {
|
||||
for (c=1; c<ncols; ++c) {
|
||||
for (offset=0, i=c; i<n; i += ncols) {
|
||||
if (offset < xOffset[i]) offset = xOffset[i];
|
||||
}
|
||||
for (i=c; i<n; i += ncols) {
|
||||
xOffset[i] = offset + xExtent[c-1];
|
||||
}
|
||||
}
|
||||
} else if (alignCol === All) {
|
||||
for (offset=0, i=0; i<n; ++i) {
|
||||
if (i % ncols && offset < xOffset[i]) offset = xOffset[i];
|
||||
}
|
||||
for (i=0; i<n; ++i) {
|
||||
if (i % ncols) xOffset[i] = offset + xMax;
|
||||
}
|
||||
} else {
|
||||
for (alignCol=false, c=1; c<ncols; ++c) {
|
||||
for (i=c; i<n; i += ncols) {
|
||||
xOffset[i] += xExtent[c-1];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// enforce row alignment constraints
|
||||
if (alignRow === Each) {
|
||||
for (r=1; r<nrows; ++r) {
|
||||
for (offset=0, i=r*ncols, m=i+ncols; i<m; ++i) {
|
||||
if (offset < yOffset[i]) offset = yOffset[i];
|
||||
}
|
||||
for (i=r*ncols; i<m; ++i) {
|
||||
yOffset[i] = offset + yExtent[r-1];
|
||||
}
|
||||
}
|
||||
} else if (alignRow === All) {
|
||||
for (offset=0, i=ncols; i<n; ++i) {
|
||||
if (offset < yOffset[i]) offset = yOffset[i];
|
||||
}
|
||||
for (i=ncols; i<n; ++i) {
|
||||
yOffset[i] = offset + yMax;
|
||||
}
|
||||
} else {
|
||||
for (alignRow=false, r=1; r<nrows; ++r) {
|
||||
for (i=r*ncols, m=i+ncols; i<m; ++i) {
|
||||
yOffset[i] += yExtent[r-1];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// perform horizontal grid layout
|
||||
for (x=0, i=0; i<n; ++i) {
|
||||
x = xOffset[i] + (i % ncols ? x : 0);
|
||||
dx[i] += x - groups[i].x;
|
||||
}
|
||||
|
||||
// perform vertical grid layout
|
||||
for (c=0; c<ncols; ++c) {
|
||||
for (y=0, i=c; i<n; i += ncols) {
|
||||
y += yOffset[i];
|
||||
dy[i] += y - groups[i].y;
|
||||
}
|
||||
}
|
||||
|
||||
// perform horizontal centering
|
||||
if (alignCol && get(opt.center, Column) && nrows > 1) {
|
||||
for (i=0; i<n; ++i) {
|
||||
b = alignCol === All ? xMax : xExtent[i % ncols];
|
||||
x = b - boxes[i].x2 - groups[i].x - dx[i];
|
||||
if (x > 0) dx[i] += x / 2;
|
||||
}
|
||||
}
|
||||
|
||||
// perform vertical centering
|
||||
if (alignRow && get(opt.center, Row) && ncols !== 1) {
|
||||
for (i=0; i<n; ++i) {
|
||||
b = alignRow === All ? yMax : yExtent[~~(i / ncols)];
|
||||
y = b - boxes[i].y2 - groups[i].y - dy[i];
|
||||
if (y > 0) dy[i] += y / 2;
|
||||
}
|
||||
}
|
||||
|
||||
// position grid relative to anchor
|
||||
for (i=0; i<n; ++i) {
|
||||
bounds.union(boxes[i].translate(dx[i], dy[i]));
|
||||
}
|
||||
x = get(opt.anchor, X);
|
||||
y = get(opt.anchor, Y);
|
||||
switch (get(opt.anchor, Column)) {
|
||||
case End: x -= bounds.width(); break;
|
||||
case Middle: x -= bounds.width() / 2;
|
||||
}
|
||||
switch (get(opt.anchor, Row)) {
|
||||
case End: y -= bounds.height(); break;
|
||||
case Middle: y -= bounds.height() / 2;
|
||||
}
|
||||
x = Math.round(x);
|
||||
y = Math.round(y);
|
||||
|
||||
// update mark positions, bounds, dirty
|
||||
bounds.clear();
|
||||
for (i=0; i<n; ++i) {
|
||||
groups[i].mark.bounds.clear();
|
||||
}
|
||||
for (i=0; i<n; ++i) {
|
||||
g = groups[i];
|
||||
g.x += (dx[i] += x);
|
||||
g.y += (dy[i] += y);
|
||||
bounds.union(g.mark.bounds.union(g.bounds.translate(dx[i], dy[i])));
|
||||
if (dirty) view.dirty(g);
|
||||
}
|
||||
|
||||
return bounds;
|
||||
}
|
||||
|
||||
export function trellisLayout(view, group, opt) {
|
||||
var views = gridLayoutGroups(group),
|
||||
groups = views.marks,
|
||||
bbox = opt.bounds === Flush ? boundFlush : boundFull,
|
||||
off = opt.offset,
|
||||
ncols = opt.columns || groups.length,
|
||||
nrows = ncols <= 0 ? 1 : Math.ceil(groups.length / ncols),
|
||||
cells = nrows * ncols,
|
||||
x, y, x2, y2, anchor, band, offset;
|
||||
|
||||
// -- initial grid layout
|
||||
const bounds = gridLayout(view, groups, opt);
|
||||
if (bounds.empty()) bounds.set(0, 0, 0, 0); // empty grid
|
||||
|
||||
// -- layout grid headers and footers --
|
||||
|
||||
// perform row header layout
|
||||
if (views.rowheaders) {
|
||||
band = get(opt.headerBand, Row, null);
|
||||
x = layoutHeaders(view, views.rowheaders, groups, ncols, nrows, -get(off, 'rowHeader'), min, 0, bbox, 'x1', 0, ncols, 1, band);
|
||||
}
|
||||
|
||||
// perform column header layout
|
||||
if (views.colheaders) {
|
||||
band = get(opt.headerBand, Column, null);
|
||||
y = layoutHeaders(view, views.colheaders, groups, ncols, ncols, -get(off, 'columnHeader'), min, 1, bbox, 'y1', 0, 1, ncols, band);
|
||||
}
|
||||
|
||||
// perform row footer layout
|
||||
if (views.rowfooters) {
|
||||
band = get(opt.footerBand, Row, null);
|
||||
x2 = layoutHeaders(view, views.rowfooters, groups, ncols, nrows, get(off, 'rowFooter'), max, 0, bbox, 'x2', ncols-1, ncols, 1, band);
|
||||
}
|
||||
|
||||
// perform column footer layout
|
||||
if (views.colfooters) {
|
||||
band = get(opt.footerBand, Column, null);
|
||||
y2 = layoutHeaders(view, views.colfooters, groups, ncols, ncols, get(off, 'columnFooter'), max, 1, bbox, 'y2', cells-ncols, 1, ncols, band);
|
||||
}
|
||||
|
||||
// perform row title layout
|
||||
if (views.rowtitle) {
|
||||
anchor = get(opt.titleAnchor, Row);
|
||||
offset = get(off, 'rowTitle');
|
||||
offset = anchor === End ? x2 + offset : x - offset;
|
||||
band = get(opt.titleBand, Row, 0.5);
|
||||
layoutTitle(view, views.rowtitle, offset, 0, bounds, band);
|
||||
}
|
||||
|
||||
// perform column title layout
|
||||
if (views.coltitle) {
|
||||
anchor = get(opt.titleAnchor, Column);
|
||||
offset = get(off, 'columnTitle');
|
||||
offset = anchor === End ? y2 + offset : y - offset;
|
||||
band = get(opt.titleBand, Column, 0.5);
|
||||
layoutTitle(view, views.coltitle, offset, 1, bounds, band);
|
||||
}
|
||||
}
|
||||
|
||||
function boundFlush(item, field) {
|
||||
return field === 'x1' ? (item.x || 0)
|
||||
: field === 'y1' ? (item.y || 0)
|
||||
: field === 'x2' ? (item.x || 0) + (item.width || 0)
|
||||
: field === 'y2' ? (item.y || 0) + (item.height || 0)
|
||||
: undefined;
|
||||
}
|
||||
|
||||
function boundFull(item, field) {
|
||||
return item.bounds[field];
|
||||
}
|
||||
|
||||
function layoutHeaders(view, headers, groups, ncols, limit, offset, agg, isX, bound, bf, start, stride, back, band) {
|
||||
var n = groups.length,
|
||||
init = 0,
|
||||
edge = 0,
|
||||
i, j, k, m, b, h, g, x, y;
|
||||
|
||||
// if no groups, early exit and return 0
|
||||
if (!n) return init;
|
||||
|
||||
// compute margin
|
||||
for (i=start; i<n; i+=stride) {
|
||||
if (groups[i]) init = agg(init, bound(groups[i], bf));
|
||||
}
|
||||
|
||||
// if no headers, return margin calculation
|
||||
if (!headers.length) return init;
|
||||
|
||||
// check if number of headers exceeds number of rows or columns
|
||||
if (headers.length > limit) {
|
||||
view.warn('Grid headers exceed limit: ' + limit);
|
||||
headers = headers.slice(0, limit);
|
||||
}
|
||||
|
||||
// apply offset
|
||||
init += offset;
|
||||
|
||||
// clear mark bounds for all headers
|
||||
for (j=0, m=headers.length; j<m; ++j) {
|
||||
view.dirty(headers[j]);
|
||||
headers[j].mark.bounds.clear();
|
||||
}
|
||||
|
||||
// layout each header
|
||||
for (i=start, j=0, m=headers.length; j<m; ++j, i+=stride) {
|
||||
h = headers[j];
|
||||
b = h.mark.bounds;
|
||||
|
||||
// search for nearest group to align to
|
||||
// necessary if table has empty cells
|
||||
for (k=i; k >= 0 && (g = groups[k]) == null; k-=back);
|
||||
|
||||
// assign coordinates and update bounds
|
||||
if (isX) {
|
||||
x = band == null ? g.x : Math.round(g.bounds.x1 + band * g.bounds.width());
|
||||
y = init;
|
||||
} else {
|
||||
x = init;
|
||||
y = band == null ? g.y : Math.round(g.bounds.y1 + band * g.bounds.height());
|
||||
}
|
||||
b.union(h.bounds.translate(x - (h.x || 0), y - (h.y || 0)));
|
||||
h.x = x;
|
||||
h.y = y;
|
||||
view.dirty(h);
|
||||
|
||||
// update current edge of layout bounds
|
||||
edge = agg(edge, b[bf]);
|
||||
}
|
||||
|
||||
return edge;
|
||||
}
|
||||
|
||||
function layoutTitle(view, g, offset, isX, bounds, band) {
|
||||
if (!g) return;
|
||||
view.dirty(g);
|
||||
|
||||
// compute title coordinates
|
||||
var x = offset, y = offset;
|
||||
isX
|
||||
? (x = Math.round(bounds.x1 + band * bounds.width()))
|
||||
: (y = Math.round(bounds.y1 + band * bounds.height()));
|
||||
|
||||
// assign coordinates and update bounds
|
||||
g.bounds.translate(x - (g.x || 0), y - (g.y || 0));
|
||||
g.mark.bounds.clear().union(g.bounds);
|
||||
g.x = x;
|
||||
g.y = y;
|
||||
|
||||
// queue title for redraw
|
||||
view.dirty(g);
|
||||
}
|
||||
219
node_modules/vega-view-transforms/src/layout/legend.js
generated
vendored
Normal file
219
node_modules/vega-view-transforms/src/layout/legend.js
generated
vendored
Normal file
@@ -0,0 +1,219 @@
|
||||
import {
|
||||
Bottom, BottomLeft, BottomRight, Each, End, Flush, Left, Middle,
|
||||
None, Right, Start, Symbols, Top,
|
||||
TopLeft, TopRight
|
||||
} from '../constants';
|
||||
import {boundStroke, multiLineOffset} from 'vega-scenegraph';
|
||||
|
||||
// utility for looking up legend layout configuration
|
||||
function lookup(config, orient) {
|
||||
const opt = config[orient] || {};
|
||||
return (key, d) => opt[key] != null ? opt[key]
|
||||
: config[key] != null ? config[key]
|
||||
: d;
|
||||
}
|
||||
|
||||
// if legends specify offset directly, use the maximum specified value
|
||||
function offsets(legends, value) {
|
||||
var max = -Infinity;
|
||||
legends.forEach(item => {
|
||||
if (item.offset != null) max = Math.max(max, item.offset);
|
||||
});
|
||||
return max > -Infinity ? max : value;
|
||||
}
|
||||
|
||||
export function legendParams(g, orient, config, xb, yb, w, h) {
|
||||
const _ = lookup(config, orient),
|
||||
offset = offsets(g, _('offset', 0)),
|
||||
anchor = _('anchor', Start),
|
||||
mult = anchor === End ? 1 : anchor === Middle ? 0.5 : 0;
|
||||
|
||||
const p = {
|
||||
align: Each,
|
||||
bounds: _('bounds', Flush),
|
||||
columns: _('direction') === 'vertical' ? 1 : g.length,
|
||||
padding: _('margin', 8),
|
||||
center: _('center'),
|
||||
nodirty: true
|
||||
};
|
||||
|
||||
switch (orient) {
|
||||
case Left:
|
||||
p.anchor = {
|
||||
x: Math.floor(xb.x1) - offset, column: End,
|
||||
y: mult * (h || xb.height() + 2 * xb.y1), row: anchor
|
||||
};
|
||||
break;
|
||||
case Right:
|
||||
p.anchor = {
|
||||
x: Math.ceil(xb.x2) + offset,
|
||||
y: mult * (h || xb.height() + 2 * xb.y1), row: anchor
|
||||
};
|
||||
break;
|
||||
case Top:
|
||||
p.anchor = {
|
||||
y: Math.floor(yb.y1) - offset, row: End,
|
||||
x: mult * (w || yb.width() + 2 * yb.x1), column: anchor
|
||||
};
|
||||
break;
|
||||
case Bottom:
|
||||
p.anchor = {
|
||||
y: Math.ceil(yb.y2) + offset,
|
||||
x: mult * (w || yb.width() + 2 * yb.x1), column: anchor
|
||||
};
|
||||
break;
|
||||
case TopLeft:
|
||||
p.anchor = {x: offset, y: offset};
|
||||
break;
|
||||
case TopRight:
|
||||
p.anchor = {x: w - offset, y: offset, column: End};
|
||||
break;
|
||||
case BottomLeft:
|
||||
p.anchor = {x: offset, y: h - offset, row: End};
|
||||
break;
|
||||
case BottomRight:
|
||||
p.anchor = {x: w - offset, y: h - offset, column: End, row: End};
|
||||
break;
|
||||
}
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
export function legendLayout(view, legend) {
|
||||
var item = legend.items[0],
|
||||
datum = item.datum,
|
||||
orient = item.orient,
|
||||
bounds = item.bounds,
|
||||
x = item.x, y = item.y, w, h;
|
||||
|
||||
// cache current bounds for later comparison
|
||||
item._bounds
|
||||
? item._bounds.clear().union(bounds)
|
||||
: item._bounds = bounds.clone();
|
||||
bounds.clear();
|
||||
|
||||
// adjust legend to accommodate padding and title
|
||||
legendGroupLayout(view, item, item.items[0].items[0]);
|
||||
|
||||
// aggregate bounds to determine size, and include origin
|
||||
bounds = legendBounds(item, bounds);
|
||||
w = 2 * item.padding;
|
||||
h = 2 * item.padding;
|
||||
if (!bounds.empty()) {
|
||||
w = Math.ceil(bounds.width() + w);
|
||||
h = Math.ceil(bounds.height() + h);
|
||||
}
|
||||
|
||||
if (datum.type === Symbols) {
|
||||
legendEntryLayout(item.items[0].items[0].items[0].items);
|
||||
}
|
||||
|
||||
if (orient !== None) {
|
||||
item.x = x = 0;
|
||||
item.y = y = 0;
|
||||
}
|
||||
item.width = w;
|
||||
item.height = h;
|
||||
boundStroke(bounds.set(x, y, x + w, y + h), item);
|
||||
item.mark.bounds.clear().union(bounds);
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
function legendBounds(item, b) {
|
||||
// aggregate item bounds
|
||||
item.items.forEach(_ => b.union(_.bounds));
|
||||
|
||||
// anchor to legend origin
|
||||
b.x1 = item.padding;
|
||||
b.y1 = item.padding;
|
||||
|
||||
return b;
|
||||
}
|
||||
|
||||
function legendGroupLayout(view, item, entry) {
|
||||
var pad = item.padding,
|
||||
ex = pad - entry.x,
|
||||
ey = pad - entry.y;
|
||||
|
||||
if (!item.datum.title) {
|
||||
if (ex || ey) translate(view, entry, ex, ey);
|
||||
} else {
|
||||
var title = item.items[1].items[0],
|
||||
anchor = title.anchor,
|
||||
tpad = item.titlePadding || 0,
|
||||
tx = pad - title.x,
|
||||
ty = pad - title.y;
|
||||
|
||||
switch (title.orient) {
|
||||
case Left:
|
||||
ex += Math.ceil(title.bounds.width()) + tpad;
|
||||
break;
|
||||
case Right:
|
||||
case Bottom:
|
||||
break;
|
||||
default:
|
||||
ey += title.bounds.height() + tpad;
|
||||
}
|
||||
if (ex || ey) translate(view, entry, ex, ey);
|
||||
|
||||
switch (title.orient) {
|
||||
case Left:
|
||||
ty += legendTitleOffset(item, entry, title, anchor, 1, 1);
|
||||
break;
|
||||
case Right:
|
||||
tx += legendTitleOffset(item, entry, title, End, 0, 0) + tpad;
|
||||
ty += legendTitleOffset(item, entry, title, anchor, 1, 1);
|
||||
break;
|
||||
case Bottom:
|
||||
tx += legendTitleOffset(item, entry, title, anchor, 0, 0);
|
||||
ty += legendTitleOffset(item, entry, title, End, -1, 0, 1) + tpad;
|
||||
break;
|
||||
default:
|
||||
tx += legendTitleOffset(item, entry, title, anchor, 0, 0);
|
||||
}
|
||||
if (tx || ty) translate(view, title, tx, ty);
|
||||
|
||||
// translate legend if title pushes into negative coordinates
|
||||
if ((tx = Math.round(title.bounds.x1 - pad)) < 0) {
|
||||
translate(view, entry, -tx, 0);
|
||||
translate(view, title, -tx, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function legendTitleOffset(item, entry, title, anchor, y, lr, noBar) {
|
||||
const grad = item.datum.type !== 'symbol',
|
||||
vgrad = title.datum.vgrad,
|
||||
e = grad && (lr || !vgrad) && !noBar ? entry.items[0] : entry,
|
||||
s = e.bounds[y ? 'y2' : 'x2'] - item.padding,
|
||||
u = vgrad && lr ? s : 0,
|
||||
v = vgrad && lr ? 0 : s,
|
||||
o = y <= 0 ? 0 : multiLineOffset(title);
|
||||
|
||||
return Math.round(anchor === Start ? u
|
||||
: anchor === End ? (v - o)
|
||||
: 0.5 * (s - o));
|
||||
}
|
||||
|
||||
function translate(view, item, dx, dy) {
|
||||
item.x += dx;
|
||||
item.y += dy;
|
||||
item.bounds.translate(dx, dy);
|
||||
item.mark.bounds.translate(dx, dy);
|
||||
view.dirty(item);
|
||||
}
|
||||
|
||||
function legendEntryLayout(entries) {
|
||||
// get max widths for each column
|
||||
var widths = entries.reduce((w, g) => {
|
||||
w[g.column] = Math.max(g.bounds.x2 - g.x, w[g.column] || 0);
|
||||
return w;
|
||||
}, {});
|
||||
|
||||
// set dimensions of legend entry groups
|
||||
entries.forEach(g => {
|
||||
g.width = widths[g.column];
|
||||
g.height = g.bounds.y2 - g.y;
|
||||
});
|
||||
}
|
||||
89
node_modules/vega-view-transforms/src/layout/title.js
generated
vendored
Normal file
89
node_modules/vega-view-transforms/src/layout/title.js
generated
vendored
Normal file
@@ -0,0 +1,89 @@
|
||||
import {Bottom, End, Group, Left, Right, Start, Top} from '../constants';
|
||||
import {set, tempBounds} from './util';
|
||||
|
||||
export function titleLayout(view, mark, width, height, viewBounds) {
|
||||
var group = mark.items[0],
|
||||
frame = group.frame,
|
||||
orient = group.orient,
|
||||
anchor = group.anchor,
|
||||
offset = group.offset,
|
||||
padding = group.padding,
|
||||
title = group.items[0].items[0],
|
||||
subtitle = group.items[1] && group.items[1].items[0],
|
||||
end = (orient === Left || orient === Right) ? height : width,
|
||||
start = 0, x = 0, y = 0, sx = 0, sy = 0, pos;
|
||||
|
||||
if (frame !== Group) {
|
||||
orient === Left ? (start = viewBounds.y2, end = viewBounds.y1)
|
||||
: orient === Right ? (start = viewBounds.y1, end = viewBounds.y2)
|
||||
: (start = viewBounds.x1, end = viewBounds.x2);
|
||||
} else if (orient === Left) {
|
||||
start = height, end = 0;
|
||||
}
|
||||
|
||||
pos = (anchor === Start) ? start
|
||||
: (anchor === End) ? end
|
||||
: (start + end) / 2;
|
||||
|
||||
if (subtitle && subtitle.text) {
|
||||
// position subtitle
|
||||
switch (orient) {
|
||||
case Top:
|
||||
case Bottom:
|
||||
sy = title.bounds.height() + padding;
|
||||
break;
|
||||
case Left:
|
||||
sx = title.bounds.width() + padding;
|
||||
break;
|
||||
case Right:
|
||||
sx = -title.bounds.width() - padding;
|
||||
break;
|
||||
}
|
||||
|
||||
tempBounds.clear().union(subtitle.bounds);
|
||||
tempBounds.translate(sx - (subtitle.x || 0), sy - (subtitle.y || 0));
|
||||
if (set(subtitle, 'x', sx) | set(subtitle, 'y', sy)) {
|
||||
view.dirty(subtitle);
|
||||
subtitle.bounds.clear().union(tempBounds);
|
||||
subtitle.mark.bounds.clear().union(tempBounds);
|
||||
view.dirty(subtitle);
|
||||
}
|
||||
|
||||
tempBounds.clear().union(subtitle.bounds);
|
||||
} else {
|
||||
tempBounds.clear();
|
||||
}
|
||||
tempBounds.union(title.bounds);
|
||||
|
||||
// position title group
|
||||
switch (orient) {
|
||||
case Top:
|
||||
x = pos;
|
||||
y = viewBounds.y1 - tempBounds.height() - offset;
|
||||
break;
|
||||
case Left:
|
||||
x = viewBounds.x1 - tempBounds.width() - offset;
|
||||
y = pos;
|
||||
break;
|
||||
case Right:
|
||||
x = viewBounds.x2 + tempBounds.width() + offset;
|
||||
y = pos;
|
||||
break;
|
||||
case Bottom:
|
||||
x = pos;
|
||||
y = viewBounds.y2 + offset;
|
||||
break;
|
||||
default:
|
||||
x = group.x;
|
||||
y = group.y;
|
||||
}
|
||||
|
||||
if (set(group, 'x', x) | set(group, 'y', y)) {
|
||||
tempBounds.translate(x, y);
|
||||
view.dirty(group);
|
||||
group.bounds.clear().union(tempBounds);
|
||||
mark.bounds.clear().union(tempBounds);
|
||||
view.dirty(group);
|
||||
}
|
||||
return group.bounds;
|
||||
}
|
||||
8
node_modules/vega-view-transforms/src/layout/util.js
generated
vendored
Normal file
8
node_modules/vega-view-transforms/src/layout/util.js
generated
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
import {Bounds} from 'vega-scenegraph';
|
||||
|
||||
export const tempBounds = new Bounds();
|
||||
|
||||
export function set(item, property, value) {
|
||||
return item[property] === value ? 0
|
||||
: (item[property] = value, 1);
|
||||
}
|
||||
Reference in New Issue
Block a user