From d54380f947128ed7ecbb99dfca6369e922ee4229 Mon Sep 17 00:00:00 2001 From: GraceGSy Date: Tue, 26 Feb 2019 18:14:07 +0800 Subject: [PATCH] Axis options (#53) * feat: more classification methods * docs: binning docs * chore: push yarn.lock and package.json * fix: linter * docs: adding missing plottitle docs * Update docs/transform/binning.md Co-Authored-By: GraceGSy * docs: fic documentation error for manual binning * fix: remove uniqueValues binning method * feat: Symbol Mark, see sandbox TestSymbol graph * docs: symbol mark demo component added to docs * docs: filling in explanatory write-up for symbol mark * docs: minor change in example symbol graph * feat: tooltip + links for symbol marks * refactor: scaled symbol size, deprecate vgg-point * refactor: remove tooltip and html linking from symbol * feat: additional axis style options * feat: axis titles with styling options and hjust + vjust * refactor: titleHjust, titleVjust, add tickSize prop * feat: preliminary work on auto positioning for axes * fix: hardcode positioning for when axes are on top/right * feat: automatic positioning of axes in nested sections * feat: option to add extra tick at start of axes * docs: axis docs * refactor: extraTick logic added to tickData() * fix: nested axes now accurately sized and positioned * feat: additional axis positioning options using x, y, x1, x2, y1, y2 * docs: update axis docs, add examples * fix: convert vgg-symbol to vgg-point * feat: rendering of label for extraTick can now be toggled * fix: coordinate tree updates now only apply to branch and its children * chore: minor changes to default behavior in axes and scales * fix: more robust logic for positioning and sizing axes using screen coordinates * docs: updated axis docs, minor changes to axis code * test: change axes to intersect at center as per original * fix: broken SymbolMarkDemo * fix: change != to !== * fix: axes x/x1/x2/w and y/y1/y2/h are now defined in local coordinates --- docs/.vuepress/components/Cartesian.vue | 145 ++++++++++ docs/.vuepress/components/SymbolMarkDemo.vue | 48 ++-- docs/axes/cartesian.md | 119 +++++++- docs/marks/symbol.md | 2 + .../CoordinateTransformation.js | 7 + src/classes/CoordinateTree/CoordinateTree.js | 36 +++ src/components/Core/Section.vue | 9 +- src/components/Guides/XAxis.vue | 149 +++++++++- src/components/Guides/YAxis.vue | 148 +++++++++- src/components/Marks/Label.vue | 18 +- src/mixins/CoordinateSystem.js | 4 +- src/mixins/CoordinateTreeUser.js | 18 +- src/mixins/Guides/BaseAxis.js | 260 +++++++++++++++++- src/mixins/Guides/defaultFormat.js | 4 +- src/mixins/Marks/Mark.js | 2 +- stories/charts/Scatterplot.vue | 29 +- stories/sandbox/Areas.vue | 31 +-- stories/sandbox/BarChart.vue | 30 +- stories/sandbox/BinningTest.vue | 13 + stories/sandbox/MultiLines.vue | 11 + stories/sandbox/NestedCoordinateSystem.vue | 2 +- stories/sandbox/PlotLines.vue | 30 +- stories/sandbox/Scatterplot.vue | 44 +-- stories/sandbox/TestCategoricalDomain.vue | 9 +- stories/sandbox/TestSymbol.vue | 31 +-- 25 files changed, 998 insertions(+), 201 deletions(-) create mode 100644 docs/.vuepress/components/Cartesian.vue diff --git a/docs/.vuepress/components/Cartesian.vue b/docs/.vuepress/components/Cartesian.vue new file mode 100644 index 00000000..69e26a4a --- /dev/null +++ b/docs/.vuepress/components/Cartesian.vue @@ -0,0 +1,145 @@ + + + diff --git a/docs/.vuepress/components/SymbolMarkDemo.vue b/docs/.vuepress/components/SymbolMarkDemo.vue index 4bbfbfa9..e897210a 100644 --- a/docs/.vuepress/components/SymbolMarkDemo.vue +++ b/docs/.vuepress/components/SymbolMarkDemo.vue @@ -21,11 +21,23 @@ :size="16" :shape="shape" :stroke="stroke" - :fill="fill" + :fill="fill(row.explanatory)" :stroke-width="1" /> + + + + + - - - -
@@ -100,14 +96,6 @@ export default { }, computed : { - fill () { - if (this.color === 'both' || this.color === 'fill') { - return { scale: { scale: 'viridis', variable: 'explanatory' } } - } else { - return 'none' - } - }, - stroke () { if (this.color === 'both' || this.color === 'stroke') { return 'black' @@ -133,7 +121,15 @@ export default { return newData - } + }, + + fill (value) { + if (this.color === 'both' || this.color === 'fill') { + return { val: value, scale: { type: 'viridis', domain: 'explanatory' } } + } else { + return 'none' + } + }, } } diff --git a/docs/axes/cartesian.md b/docs/axes/cartesian.md index 245505f8..cdf45089 100644 --- a/docs/axes/cartesian.md +++ b/docs/axes/cartesian.md @@ -2,27 +2,116 @@ title: Cartesian Axes --- -# Cartesian Axes +# Component tag -
- -
+`` -The standard axis takes the following props: +`` -Prop | Required | Default | Description -----------|----------|-----------|---------------------------- - | | | - +# Description -
+Axes are used as reference scales for values in the graph. Each axis is typically mapped to a single dimension or variable. -### Axis Domain +# Props -### Multi-axis +| Prop | Required | Regular types | Default | Description | +| ------ | -------- | ----------------------- | --------- | --------------------------------------------------------- | +| scale | true | [Array, String, Object] | undefined | range of values covered by the axis, can be variable name | +| flip | false | [Boolean] | false | direction of tick and axis labels | -### Gridlines +### X Axis Positioning -### Format Tick Values +There are three options for positioning the x axis on the graph. The default position of the x-axis is at `vjust = 'b'` (bottom of parent section). -### Custom Ticks \ No newline at end of file +When using `vjust`, the x axis defaults to a height of 100px in screen coordinates. + +| Prop | Required | Regular types | Default | Description | Unit(s) | +| ---- | -------- | ---------------- | --------- | --------------------------------------- | ---------------------- | +| vjust| false | [Number, String] | 'b' | position of x axis | Number between 0 and 1 | +| y | false | [Number] | undefined | position of x axis | Local Coordinates | +| h | false | [Number] | undefined | height of x axis | Local Coordinates | +| y1 | false | [Number] | undefined | starting y coordinate of x axis | Local Coordinates | +| y2 | false | [Number] | undefined | ending y coordinate of x axis | Local Coordinates | + +By default the x axis spans the entire width of the section. To customize the width of the x axis, it is possible to provide `x1` and `x2` as start and end coordinates. + +| Prop | Required | Regular types | Default | Description | Unit(s) | +| ---- | -------- | ---------------- | --------- | --------------------------------------- | --------------------- | +| x1 | false | [Number] | undefined | starting x coordinate of x axis | Local Coordinates | +| x2 | false | [Number] | undefined | ending x coordinate of x axis | Local Coordinates | + + + +### Y Axis Positioning + +Similar to the x axis, there are three options for positioning the y axis on the graph. The default position of the y-axis is at `hjust = 'l'` (left of parent section). + +When using `hjust`, the y axis defaults to a width of 100px in screen coordinates. + +| Prop | Required | Regular types | Default | Description | Unit(s) | +| ---- | -------- | ---------------- | --------- | --------------------------------------- | ---------------------- | +| hjust| false | [Number, String] | 'l' | position of y axis | Number between 0 and 1 | +| x | false | [Number] | undefined | position of y axis | Local Coordinates | +| w | false | [Number] | undefined | width of y axis | Local Coordinates | +| x1 | false | [Number] | undefined | starting x coordinate of y axis | Local Coordinates | +| x2 | false | [Number] | undefined | ending x coordinate of y axis | Local Coordinates | + +By default the y axis spans the entire height of the section. To customize the height of the y axis, it is possible to provide `y1` and `y2` as start and end coordinates. + +| Prop | Required | Regular types | Default | Description | Unit(s) | +| ---- | -------- | ---------------- | --------- | --------------------------------------- | --------------------- | +| y1 | false | [Number] | undefined | starting y coordinate of y axis | Local Coordinates | +| y2 | false | [Number] | undefined | ending y coordinate of y axis | Local Coordinates | + + + +### Main Line + +| Prop | Required | Regular types | Default | Description | Unit(s) | +| -------------- | -------- | ---------------- | --------- | --------------------------------------- | -------------------------- | +| domain | false | [Boolean] | true | if true render axis main line | | +| domainColor | false | [String] | 'black' | color of main line | Named color, hex, rgb, hsl | +| domainOpacity | false | [Number] | 1 | opacity of main line | Number between 0 and 1 | +| domainWidth | false | [Number] | 1 | stroke width of main line | Screen pixels | + +### Labels + +Note that if a `Function` is passed to the `format` prop to format labels before rendering, the function output must be of type `String` + +| Prop | Required | Regular types | Default | Description | Unit(s) | +| -------------- | -------- | ------------------ | ----------- | --------------------------------------- | -------------------------- | +| labels | false | [Boolean] | true | if true render labels | | +| format | false | [String, Function] | undefined | formatting of axis labels | | +| labelColor | false | [String] | 'black' | color of labels | Named color, hex, rgb, hsl | +| labelFont | false | [String] | 'Helvetica' | font used for axis labels | Named font | +| labelFontSize | false | [Number] | 10 | size of font used for axis labels | Screen pixels | +| labelFontWeight| false | [String, Number] | 'normal' | weight of font used for axis labels | Any valid css font weight | +| labelOpacity | false | [Number] | 1 | opacity of labels | Number between 0 and 1 | +| labelRotate | false | [Boolean] | false | if true rotate labels | Degrees | + +### Ticks + +| Prop | Required | Regular types | Default | Description | Unit(s) | +| -------------- | -------- | ---------------- | ----------- | ---------------------------------------------- | -------------------------- | +| ticks | false | [Boolean] | true | if true render ticks | | +| tickColor | false | [String] | 'black' | color of ticks | Named color, hex, rgb, hsl | +| tickValues | false | [Array] | undefined | custom tick positions | | +| tickCount | false | [Number] | 10 | number of ticks on the axis, equal intervals | | +| tickExtra | false | [Boolean] | true | if true, render extra tick at axis origin | | +| tickOpacity | false | [Number] | 1 | opacity of ticks | Number between 0 and 1 | +| tickSize | false | [Number] | 7 | length of ticks | Screen pixels | +| tickWidth | false | [Number] | 0.5 | stroke width of ticks | Screen pixels | + +### Title + +| Prop | Required | Regular types | Default | Description | Unit(s) | +| --------------- | -------- | ---------------- | ----------- | --------------------------------------- | -------------------------- | +| titleHjust | false | [String, Number] | depends | position of axis title relative to axis; default -0.08 for x-axis; default 'center' for y-axis | Number between 0 and 1 | +| titleVjust | false | [String, Number] | depends | position of axis title relative to axis; default 'center' for x-axis; default 1.05 for y-axis | Number between 0 and 1 | +| title | false | [String] | '' | text to render as axis title | | +| titleAnchorPoint| false | [String] | 'center' | baseline and alignment of title text | | +| titleColor | false | [String] | 'black' | color of title | Named color, hex, rgb, hsl | +| titleFont | false | [String] | 'Helvetica' | font used for axis title | Named font | +| titleFontSize | false | [Number] | 12 | size of font used for axis title | Screen pixels | +| titleFontWeight | false | [String, Number] | 'normal' | weight of font used for axis title | Any valid css font weight | +| titleOpacity | false | [Number] | 1 | opacity of title | Number between 0 and 1 | \ No newline at end of file diff --git a/docs/marks/symbol.md b/docs/marks/symbol.md index b56196f0..87d3292b 100644 --- a/docs/marks/symbol.md +++ b/docs/marks/symbol.md @@ -4,6 +4,8 @@ title: Symbol Mark # Component tag +`` + `` # Description diff --git a/src/classes/CoordinateTree/CoordinateTransformation.js b/src/classes/CoordinateTree/CoordinateTransformation.js index a56129fa..1dc369a8 100644 --- a/src/classes/CoordinateTree/CoordinateTransformation.js +++ b/src/classes/CoordinateTree/CoordinateTransformation.js @@ -72,13 +72,17 @@ export default class CoordinateTransformation { // before we actually use $$transform. This is necessary in a few cases. if (['categorical', 'temporal'].includes(this.domainTypes.x)) { this.getX = x => x.constructor === Number ? x : this.scaleX(x) + this.invertX = x => x.constructor === Number ? x : this.scaleX.invert(x) } else { this.getX = this.scaleX + this.invertX = this.scaleX.invert } if (['categorical', 'temporal'].includes(this.domainTypes.y)) { this.getY = y => y.constructor === Number ? y : this.scaleY(y) + this.invertY = y => y.constructor === Number ? y : this.scaleY.invert(y) } else { this.getY = this.scaleY + this.invertY = this.scaleY.invert } if (options.type === 'scale') { @@ -128,6 +132,9 @@ export default class CoordinateTransformation { this.getX = this.scaleX this.getY = this.scaleY + this.invertX = this.scaleX.invert + this.invertY = this.scaleY.invert + this.transform = ([x, y]) => { return [this.getX(x), this.getY(y)] } diff --git a/src/classes/CoordinateTree/CoordinateTree.js b/src/classes/CoordinateTree/CoordinateTree.js index d98af56a..1d39d97f 100644 --- a/src/classes/CoordinateTree/CoordinateTree.js +++ b/src/classes/CoordinateTree/CoordinateTree.js @@ -73,6 +73,42 @@ export default class CoordinateTree { return transformation.bind(this) } + + getLocalX (id) { + let transformation = function (x) { + let branchParents = this._branchPaths[id] + let result = x + + if (branchParents == undefined) {return result} + + for (let i = 0; i < branchParents.length; i++) { + let currentLocation = this.getBranch(branchParents[i]) + result = currentLocation.invertX(result) + } + + return result + } + + return transformation.bind(this) + } + + getLocalY (id) { + let transformation = function (y) { + let branchParents = this._branchPaths[id] + let result = y + + if (branchParents == undefined) {return result} + + for (let i = 0; i < branchParents.length; i++) { + let currentLocation = this.getBranch(branchParents[i]) + result = currentLocation.invertY(result) + } + + return result + } + + return transformation.bind(this) + } } class Branch { diff --git a/src/components/Core/Section.vue b/src/components/Core/Section.vue index 30b2e902..fa05f15c 100644 --- a/src/components/Core/Section.vue +++ b/src/components/Core/Section.vue @@ -147,11 +147,11 @@ export default { id = '_' + randomID() } return id - } + }, }, watch: { - transformation: 'updateCoordinateTreeBranch' + transformation: 'updateCoordinateTreeBranch', }, beforeDestroy () { @@ -194,8 +194,9 @@ export default { provide () { let $$transform = this.$$coordinateTree.getTotalTransformation(this.coordinateTreeBranchID) let $$coordinateTreeParent = this.coordinateTreeBranchID - - return { $$transform, $$coordinateTreeParent } + let $$getLocalX = this.$$coordinateTree.getLocalX(this.coordinateTreeBranchID) + let $$getLocalY = this.$$coordinateTree.getLocalY(this.coordinateTreeBranchID) + return { $$transform, $$coordinateTreeParent, $$getLocalX, $$getLocalY } }, render (createElement) { diff --git a/src/components/Guides/XAxis.vue b/src/components/Guides/XAxis.vue index 98cf5b2d..7ff72ef1 100644 --- a/src/components/Guides/XAxis.vue +++ b/src/components/Guides/XAxis.vue @@ -11,16 +11,35 @@ + + + @@ -78,6 +108,103 @@ import BaseAxis from '../../mixins/Guides/BaseAxis.js' export default { - mixins: [BaseAxis] + mixins: [BaseAxis], + + props: { + titleHjust: { + default: -0.08 + }, + + titleVjust: { + default: 'center' + } + }, + + computed: { + titlePosX () { + if (this.titleHjust === 'center') { + return 0.5 + } else if (this.titleHjust === 'l') { + return 0 + } else if (this.titleHjust === 'r') { + return 1 + } else { + return this.titleHjust + } + }, + + titlePosY () { + if (this.titleVjust === 'center') { + return 0.5 + } else if (this.titleVjust === 't') { + return 1 + } else if (this.titleVjust === 'b') { + return 0 + } else { + return this.titleVjust + } + }, + + tickMin () { + let localTickSize = this.getLocalY(this.tickSize) - this.getLocalY(0) + let scaledSize = localTickSize / (this.ranges.y2 - this.ranges.y1) + return 0.5 - scaledSize + }, + + tickMax () { + let localTickSize = this.getLocalY(this.tickSize) - this.getLocalY(0) + let scaledSize = localTickSize / (this.ranges.y2 - this.ranges.y1) + return 0.5 + scaledSize + }, + + posY () { + if (this.validY) { + return [this.coords.y1, this.coords.y2] + } + + let yDomain = this.yDomain + + let yDomainMin = Math.min(yDomain[0], yDomain[1]) + let yDomainMax = Math.max(yDomain[0], yDomain[1]) + + let yHeight = this.getLocalY(50) - this.getLocalY(0) + + if (this.vjust.constructor === Number) { + let scaledVal = (yDomainMax - yDomainMin) * this.vjust + yDomainMin + return [scaledVal - yHeight, scaledVal + yHeight] + } else if (this.vjust === 'center') { + let centerVal = (yDomainMax - yDomainMin) / 2 + yDomainMin + return [centerVal - yHeight, centerVal + yHeight] + } else if (this.vjust === 't') { + return [yDomainMax - yHeight, yDomainMax + yHeight] + } else { + return [yDomainMin - yHeight, yDomainMin + yHeight] + } + }, + + ranges () { + let newRange = {} + + newRange.y1 = this.posY[0] + newRange.y2 = this.posY[1] + + if (this.validX) { + newRange.x1 = this.coords.x1 + newRange.x2 = this.coords.x2 + + return newRange + } + + if (this._domainType === 'temporal') { + newRange.x1 = this._domain[0] + newRange.x2 = this._domain[1] + } else { + newRange.x1 = this.xDomain[0] + newRange.x2 = this.xDomain[1] + } + + return newRange + }, + } } diff --git a/src/components/Guides/YAxis.vue b/src/components/Guides/YAxis.vue index 94dbe5fd..be95ffec 100644 --- a/src/components/Guides/YAxis.vue +++ b/src/components/Guides/YAxis.vue @@ -11,12 +11,30 @@ + + + @@ -39,31 +57,42 @@ @@ -79,6 +108,103 @@ import BaseAxis from '../../mixins/Guides/BaseAxis.js' export default { - mixins: [BaseAxis] + mixins: [BaseAxis], + + props: { + titleHjust: { + default: 'center' + }, + + titleVjust: { + default: 1.08 + } + }, + + computed: { + titlePosX () { + if (this.titleHjust === 'center') { + return 0.5 + } else if (this.titleHjust === 'l') { + return 0 + } else if (this.titleHjust === 'r') { + return 1 + } else { + return this.titleHjust + } + }, + + titlePosY () { + if (this.titleVjust === 'center') { + return 0.5 + } else if (this.titleVjust === 't') { + return 1 + } else if (this.titleVjust === 'b') { + return 0 + } else { + return this.titleVjust + } + }, + + tickMin () { + let localTickSize = this.getLocalX(this.tickSize) - this.getLocalX(0) + let scaledSize = localTickSize / (this.ranges.x2 - this.ranges.x1) + return 0.5 - scaledSize + }, + + tickMax () { + let localTickSize = this.getLocalX(this.tickSize) - this.getLocalX(0) + let scaledSize = localTickSize / (this.ranges.x2 - this.ranges.x1) + return 0.5 + scaledSize + }, + + posX () { + if (this.validX) { + return [this.coords.x1, this.coords.x2] + } + + let xDomain = this.xDomain + + let xDomainMin = Math.min(xDomain[0], xDomain[1]) + let xDomainMax = Math.max(xDomain[0], xDomain[1]) + + let xWidth = this.getLocalX(50) - this.getLocalX(0) + + if (this.hjust.constructor === Number) { + let scaledVal = (xDomainMax - xDomainMin) * this.hjust + xDomainMin + return [scaledVal - xWidth, scaledVal + xWidth] + } else if (this.hjust === 'center') { + let centerVal = (xDomainMax - xDomainMin) / 2 + xDomainMin + return [centerVal - xWidth, centerVal + xWidth] + } else if (this.hjust === 'l') { + return [xDomainMin - xWidth, xDomainMin + xWidth] + } else { + return [xDomainMax - xWidth, xDomainMax + xWidth] + } + }, + + ranges () { + let newRange = {} + + newRange.x1 = this.posX[0] + newRange.x2 = this.posX[1] + + if (this.validY) { + newRange.y1 = this.coords.y1 + newRange.y2 = this.coords.y2 + + return newRange + } + + if (this._domainType === 'temporal') { + newRange.y1 = this._domain[0] + newRange.y2 = this._domain[1] + } else { + newRange.y1 = this.yDomain[0] + newRange.y2 = this.yDomain[1] + } + + return newRange + }, + } } diff --git a/src/components/Marks/Label.vue b/src/components/Marks/Label.vue index 6d258cb0..30138cf2 100644 --- a/src/components/Marks/Label.vue +++ b/src/components/Marks/Label.vue @@ -51,11 +51,21 @@ export default { default: undefined }, + fontFamily: { + type: String, + default: 'Helvetica' + }, + fontSize: { type: Number, default: 16 }, + fontWeight: { + type: [String, Number], + default: 'normal' + }, + rotation: { type: Number, default: 0 @@ -91,6 +101,8 @@ export default { let styles = this.createSVGStyle(aesthetics) styles['fontSize'] = aesthetics.fontSize + 'px' + styles['font-family'] = this.fontFamily + styles['font-weight'] = this.fontWeight let el = createElement('text', { attrs: { @@ -110,9 +122,3 @@ export default { } } - - diff --git a/src/mixins/CoordinateSystem.js b/src/mixins/CoordinateSystem.js index e867bfdd..6004a218 100644 --- a/src/mixins/CoordinateSystem.js +++ b/src/mixins/CoordinateSystem.js @@ -61,7 +61,9 @@ export default { let $$coordinateTree = this.coordinateTree let $$transform = this.coordinateTree.getTotalTransformation('root') let $$coordinateTreeParent = 'root' + let $$getLocalX = this.coordinateTree.getLocalX(this.coordinateTreeBranchID) + let $$getLocalY = this.coordinateTree.getLocalY(this.coordinateTreeBranchID) - return { $$coordinateTree, $$transform, $$coordinateTreeParent } + return { $$coordinateTree, $$transform, $$coordinateTreeParent, $$getLocalX, $$getLocalY } } } diff --git a/src/mixins/CoordinateTreeUser.js b/src/mixins/CoordinateTreeUser.js index b173eb9b..893b9d32 100644 --- a/src/mixins/CoordinateTreeUser.js +++ b/src/mixins/CoordinateTreeUser.js @@ -2,14 +2,24 @@ export default { inject: ['$$coordinateTree', '$$coordinateTreeParent'], data () { - return { - parentBranch: this.$$coordinateTree.getBranch(this.$$coordinateTreeParent) - } + return { + parentBranch: this.$$coordinateTree.getBranch(this.$$coordinateTreeParent) + } }, computed: { parentRangeTypes () { return this.parentBranch.domainTypes - } + }, + }, + + watch: { + $$transform: 'getParent' + }, + + methods: { + getParent () { + this.parentBranch = this.$$coordinateTree.getBranch(this.$$coordinateTreeParent) + } } } diff --git a/src/mixins/Guides/BaseAxis.js b/src/mixins/Guides/BaseAxis.js index 5ae5e801..a5d0f8b7 100644 --- a/src/mixins/Guides/BaseAxis.js +++ b/src/mixins/Guides/BaseAxis.js @@ -15,9 +15,116 @@ export default { props: { scale: { type: [Array, String, Object, undefined], + default: undefined, + required: true + }, + + format: { + type: [String, Function, undefined], default: undefined }, + flip: { + type: Boolean, + default: false + }, + + hjust: { + type: [Number, String], + default: 'l', + validator: function (p) { + return (p.constructor === Number) || (['center', 'l', 'r'].includes(p)) + } + }, + + vjust: { + type: [Number, String], + default: 'b', + validator: function (p) { + return (p.constructor === Number) || (['center', 't', 'b'].includes(p)) + } + }, + + // STYLING OPTIONS // + + // If true render axis main line + domain: { + type: Boolean, + default: true + }, + + domainColor: { + type: String, + default: 'black' + }, + + domainOpacity: { + type: Number, + default: 1 + }, + + domainWidth: { + type: Number, + default: 1 + }, + + labels: { + type: Boolean, + default: true + }, + + // hide labels that exceed axis range + // labelBound: { + // type: Boolean, + // default: false + // }, + + labelColor: { + type: String, + default: 'black' + }, + + labelFont: { + type: String, + default: 'Helvetica' + }, + + labelFontSize: { + type: Number, + default: 10 + }, + + labelFontWeight: { + type: [String, Number], + default: 'normal' + }, + + labelOpacity: { + type: Number, + default: 1 + }, + + // Distance between tick and label ** + // labelPadding: { + // type: Number, + // default: 0 + // }, + + labelRotate: { + type: Boolean, + default: false + }, + + ticks: { + type: Boolean, + default: true + }, + + tickColor: { + type: String, + default: 'black' + }, + tickValues: { type: [Array, undefined], default: undefined @@ -28,20 +135,86 @@ export default { default: 10 }, - rotateLabel: { + tickExtra: { + type: Boolean, + default: true + }, + + tickExtraLabel: { type: Boolean, default: false }, - format: { - type: [String, Function, undefined], - default: undefined + tickOpacity: { + type: Number, + default: 1 + }, + + tickSize: { + type: Number, + default: 7 }, - flip: { - type: Boolean, - default: false + tickWidth: { + type: Number, + default: 0.5 + }, + + title: { + type: String, + default: '' + }, + + titleAnchorPoint: { + type: String, + default: 'center', + validator: p => ['center', 'lb', 'lt', 'rt', 'rb', 'l', 'r', 't', 'b'].includes(p) + }, + + titleAngle: { + type: Number, + default: 0 + }, + + titleColor: { + type: String, + default: 'black' + }, + + titleFont: { + type: String, + default: 'Helvetica' + }, + + titleFontSize: { + type: Number, + default: 12 + }, + + titleFontWeight: { + type: Number, + default: 500 + }, + + titleOpacity: { + type: Number, + default: 1 + }, + + titleHjust: { + type: [Number, String], + validator: function (p) { + return (p.constructor === Number) || (['center', 'l', 'r'].includes(p)) + } + }, + + titleVjust: { + type: [Number, String], + validator: function (p) { + return (p.constructor === Number) || (['center', 't', 'b'].includes(p)) + } } + }, computed: { @@ -56,14 +229,46 @@ export default { _domainType () { return this._parsedScalingOptions[1] }, + _scalingOptions () { + return this._parsedScalingOptions[2] + }, + + _parentNodes () { + return this.getParents(this.parentBranch, [this.parentBranch]) + }, + + validX () { + return (this.x1 !== undefined && this.x2 !== undefined) || (this.x !== undefined && this.w !== undefined) + }, + + validY () { + return (this.y1 !== undefined && this.y2 !== undefined) || (this.y !== undefined && this.h !== undefined) + }, - ranges () { - return this.coordinateSpecification + coords () { + if (this.validX || this.validY) {return this.coordinateSpecification} + }, + + xDomain () { + return this.parentBranch.domains.x + }, + + yDomain () { + return this.parentBranch.domains.y }, tickData () { + let firstValue = this._domain[0] + let newTickValues + if (this.tickValues) { - return this.tickValues.map(value => { + newTickValues = this.tickValues + + if (this.tickExtra && this.tickValues[0] !== firstValue) { + newTickValues.unshift(firstValue) + } + + return newTickValues.map(value => { return { value } }) } else { @@ -71,8 +276,18 @@ export default { let format = this.format && this.format.constructor === Function ? this.format : defaultFormat if (this._domainType === 'quantitative') { - ticks = arrayTicks(...this._domain, this.tickCount).map(value => { - return { value, label: format(value) } + newTickValues = arrayTicks(...this._domain, this.tickCount) + + if (this.tickExtra && newTickValues[0] !== firstValue) { + newTickValues.unshift(firstValue) + } + + ticks = newTickValues.map((value, i) => { + if (i === 0 && this.tickExtra && !this.tickExtraLabel) { + return { value, label: '' } + } else { + return { value, label: format(value) } + } }) } @@ -91,9 +306,20 @@ export default { let scale = scaleTime().domain(this._domain) - ticks = scale.ticks(this.tickCount).map(value => { + newTickValues = scale.ticks(this.tickCount) + + if (this.tickExtra && newTickValues[0] !== firstValue) { + newTickValues.unshift(firstValue) + } + + ticks = newTickValues.map((value, i) => { let date = new Date(value) - return { value: date, label: format(date) } + + if (i === 0 && this.tickExtra && !this.tickExtraLabel) { + return { value: date, label: '' } + } else { + return { value: date, label: format(date) } + } }) } @@ -131,6 +357,10 @@ export default { ticks.add(interval[1]) } return Array.from(ticks) - } + }, + + getLocalX (n) { return this.$$getLocalX(n) }, + + getLocalY (n) { return this.$$getLocalY(n) } } } diff --git a/src/mixins/Guides/defaultFormat.js b/src/mixins/Guides/defaultFormat.js index b2c1ca07..28ed9880 100644 --- a/src/mixins/Guides/defaultFormat.js +++ b/src/mixins/Guides/defaultFormat.js @@ -2,7 +2,7 @@ export default function (value) { if (value.constructor === Number) { let stringValue = value.toString() let length = stringValue.length - if (length < 6) { return value } + if (length < 6) { return value.toString() } let nIntegers = stringValue.split('.')[0].length @@ -24,5 +24,5 @@ export default function (value) { function round (value, decimals) { let z = 10 ** decimals - return Math.floor(value * z) / z + return (Math.floor(value * z) / z).toString() } diff --git a/src/mixins/Marks/Mark.js b/src/mixins/Marks/Mark.js index b8807109..7ca71e0b 100644 --- a/src/mixins/Marks/Mark.js +++ b/src/mixins/Marks/Mark.js @@ -4,7 +4,7 @@ import createSVGStyle from './utils/createSVGStyle.js' export default { mixins: [CoordinateTreeUser], - inject: ['$$transform'], + inject: ['$$transform', '$$getLocalX', '$$getLocalY'], props: { interpolate: { diff --git a/stories/charts/Scatterplot.vue b/stories/charts/Scatterplot.vue index f35f2e1e..eca345b8 100644 --- a/stories/charts/Scatterplot.vue +++ b/stories/charts/Scatterplot.vue @@ -24,6 +24,18 @@ + + + + - - - - diff --git a/stories/sandbox/Areas.vue b/stories/sandbox/Areas.vue index a4ae1182..9e0bcfdf 100644 --- a/stories/sandbox/Areas.vue +++ b/stories/sandbox/Areas.vue @@ -4,24 +4,6 @@ :height="600" :data="dummyData"> - - - - - + + + + + diff --git a/stories/sandbox/BarChart.vue b/stories/sandbox/BarChart.vue index a530e3ba..8a35e9bb 100644 --- a/stories/sandbox/BarChart.vue +++ b/stories/sandbox/BarChart.vue @@ -66,24 +66,20 @@ - + - - - + + + diff --git a/stories/sandbox/BinningTest.vue b/stories/sandbox/BinningTest.vue index b15e0957..ff5883ec 100644 --- a/stories/sandbox/BinningTest.vue +++ b/stories/sandbox/BinningTest.vue @@ -47,6 +47,19 @@ + + + + + + diff --git a/stories/sandbox/NestedCoordinateSystem.vue b/stories/sandbox/NestedCoordinateSystem.vue index ce6cff2b..9a38ecdd 100644 --- a/stories/sandbox/NestedCoordinateSystem.vue +++ b/stories/sandbox/NestedCoordinateSystem.vue @@ -33,7 +33,7 @@ :x="p.x" :y="p.y" :fill="cs.color" - :size="8" + :radius="4" /> - - - - - + + + + + diff --git a/stories/sandbox/Scatterplot.vue b/stories/sandbox/Scatterplot.vue index 3ed00ccc..53c468b9 100644 --- a/stories/sandbox/Scatterplot.vue +++ b/stories/sandbox/Scatterplot.vue @@ -26,6 +26,34 @@ + + + + + + + + - - - -
diff --git a/stories/sandbox/TestCategoricalDomain.vue b/stories/sandbox/TestCategoricalDomain.vue index 2ad9fbd8..74a29b92 100644 --- a/stories/sandbox/TestCategoricalDomain.vue +++ b/stories/sandbox/TestCategoricalDomain.vue @@ -36,16 +36,21 @@ ]" /> + + - + /> --> diff --git a/stories/sandbox/TestSymbol.vue b/stories/sandbox/TestSymbol.vue index 9a0ac845..1950392e 100644 --- a/stories/sandbox/TestSymbol.vue +++ b/stories/sandbox/TestSymbol.vue @@ -26,8 +26,21 @@ shape="triangle-left" fill="none" /> + + + + + - - - -