@progress/kendo-charts
Version:
Kendo UI platform-independent Charts library
339 lines (275 loc) • 8.4 kB
JavaScript
import {
geometry as geom,
drawing as draw
} from '@progress/kendo-drawing';
import {
defined,
addClass,
setDefaultOptions,
deepExtend,
getSpacing,
isObject,
defaultErrorHandler
} from '../common';
import {
Box,
TextBox
} from '../core';
import { Encodings } from './encodings/main';
import { surfaceSize } from './surface-size';
const DEFAULT_BARCODE_WIDTH = 300;
const DEFAULT_BARCODE_HEIGHT = 100;
class Barcode {
constructor(element, options, errorHandler = defaultErrorHandler) {
this.options = deepExtend({}, this.options, options);
this.element = element;
this.onError = errorHandler;
this._initElement();
this._initSurface();
this._setOptions(options);
if (options && defined(options.value)) {
this.redraw();
}
}
destroy() {
this._destroySurface();
}
_initElement() {
addClass(this.element, "k-barcode");
}
_initSurface() {
const { options, surface } = this;
if (!surface || surface.options.type !== options.renderAs) {
this._destroySurface();
this._initSurfaceElement();
this.surface = this._createSurface();
}
}
_createSurface() {
return draw.Surface.create(this.surfaceElement, {
type: this.options.renderAs
});
}
_destroySurface() {
if (this.surface) {
this.surface.destroy();
this.surface = null;
this._destroySurfaceElement();
}
}
_initSurfaceElement() {
if (!this.surfaceElement) {
this.surfaceElement = document.createElement('div');
this.surfaceElement.style.position = 'relative';
this.element.appendChild(this.surfaceElement);
}
}
_destroySurfaceElement() {
if (this.surfaceElement && this.surfaceElement.parentNode) {
this.surfaceElement.parentNode.removeChild(this.surfaceElement);
this.surfaceElement = null;
}
}
setOptions(options) {
this._setOptions(options);
this._initSurface();
this.redraw();
}
redraw() {
let size = this._getSize();
this.surface.clear();
this.surface.setSize({
width: size.width,
height: size.height
});
this.createVisual();
this.surface.draw(this.visual);
}
getSize() {
return {
width: this.element.offsetWidth,
height: this.element.offsetHeight
};
}
_resize() {
this.redraw();
}
createVisual() {
this.visual = this._render();
}
_render() {
const options = this.options;
const value = options.value;
const textOptions = options.text;
const textMargin = getSpacing(textOptions.margin);
const size = this._getSize();
const border = options.border || {};
const encoding = this.encoding;
const contentBox = new Box(0, 0, size.width, size.height).unpad(border.width).unpad(options.padding);
let barHeight = contentBox.height();
let encodedValue;
let textToDisplay;
let textHeight;
const visual = new draw.Group();
this.contentBox = contentBox;
visual.append(this._getBackground(size));
if (textOptions.visible) {
textHeight = draw.util.measureText(value, { font: textOptions.font }).height;
barHeight -= textHeight + textMargin.top + textMargin.bottom;
}
try {
encodedValue = encoding.encode(value, contentBox.width(), barHeight);
} catch (error) {
this.onError(error);
return visual;
}
if (textOptions.visible) {
textToDisplay = value;
if (options.checksum && defined(encoding.checksum)) {
textToDisplay += " " + encoding.checksum;
}
visual.append(this._getText(textToDisplay));
}
this.barHeight = barHeight;
this._bandsGroup = this._getBands(encodedValue.pattern, encodedValue.baseUnit);
visual.append(this._bandsGroup);
return visual;
}
exportVisual() {
return this._render();
}
_getSize() {
const element = this.element;
const elementSize = surfaceSize(element, this.options.renderAs);
const size = new geom.Size(DEFAULT_BARCODE_WIDTH, DEFAULT_BARCODE_HEIGHT);
if (elementSize.width > 0) {
size.width = elementSize.width;
}
if (elementSize.height > 0) {
size.height = elementSize.height;
}
if (this.options.width) {
size.width = this.options.width;
}
if (this.options.height) {
size.height = this.options.height;
}
return size;
}
value(value) {
if (!defined(value)) {
return this.options.value;
}
this.options.value = String(value);
this.redraw();
}
_getBands(pattern, baseUnit) {
const contentBox = this.contentBox;
let offsetX = contentBox.x1;
let stepX;
let patternItem;
const group = new draw.Group();
for (let i = 0; i < pattern.length; i++) {
patternItem = isObject(pattern[i]) ? pattern[i] : {
width: pattern[i],
y1: 0,
y2: this.barHeight
};
stepX = patternItem.width * baseUnit;
if (i % 2) {
const rect = geom.Rect.fromPoints(
new geom.Point(offsetX, patternItem.y1 + contentBox.y1),
new geom.Point(offsetX + stepX, patternItem.y2 + contentBox.y1)
);
const path = draw.Path.fromRect(rect, {
fill: {
color: this.options.color
},
stroke: null
});
group.append(path);
}
offsetX += stepX;
}
return group;
}
_getBackground(size) {
const options = this.options;
const border = options.border || {};
const box = new Box(0, 0, size.width, size.height).unpad(border.width / 2);
const path = draw.Path.fromRect(box.toRect(), {
fill: {
color: options.background
},
stroke: {
color: border.width ? border.color : "",
width: border.width,
dashType: border.dashType
}
});
return path;
}
_getText(value) {
const textOptions = this.options.text;
const text = this._textbox = new TextBox(value, {
font: textOptions.font,
color: textOptions.color,
align: "center",
vAlign: "bottom",
margin: textOptions.margin
});
text.reflow(this.contentBox);
text.renderVisual();
return text.visual;
}
_setOptions(options) {
this.type = (options.type || this.options.type).toLowerCase();
if (this.type === "upca") {
this.type = "ean13";
options.value = '0' + options.value;
}
if (this.type === "upce") {
this.type = "ean8";
options.value = '0' + options.value;
}
if (!Encodings[this.type]) {
throw new Error(`Encoding '${this.type}' is not supported.`);
}
this.encoding = new Encodings[this.type]();
this.options = deepExtend({}, this.options, options);
}
}
setDefaultOptions(Barcode, {
name: "Barcode",
renderAs: "svg",
value: "",
type: "code39",
checksum: false,
width: 0,
height: 0,
color: "black",
background: "white",
text: {
visible: true,
font: "16px Consolas, Monaco, Sans Mono, monospace, sans-serif",
color: "black",
margin: {
top: 0,
bottom: 0,
left: 0,
right: 0
}
},
border: {
width: 0,
dashType: "solid",
color: "black"
},
padding: {
top: 0,
bottom: 0,
left: 0,
right: 0
}
});
export default Barcode;