[DOCS] Docs builder

This commit is contained in:
Prateeksha Singh 2018-04-30 04:59:49 +05:30
parent d474d583ec
commit b4d098d1e8
15 changed files with 900 additions and 489 deletions

View File

@ -18,6 +18,9 @@ $.create = (tag, o) => {
ref.parentNode.insertBefore(element, ref); ref.parentNode.insertBefore(element, ref);
element.appendChild(ref); element.appendChild(ref);
} else if (i === "onClick" ) {
element.addEventListener('click', val);
} else if (i === "styles") { } else if (i === "styles") {
if(typeof val === "object") { if(typeof val === "object") {
Object.keys(val).map(prop => { Object.keys(val).map(prop => {
@ -298,10 +301,6 @@ class SvgTip {
} }
} }
/**
* Returns the value of a number upto 2 decimal places.
* @param {Number} d Any number
*/
function floatTwo(d) { function floatTwo(d) {
return parseFloat(d.toFixed(2)); return parseFloat(d.toFixed(2));
} }
@ -3677,7 +3676,6 @@ class AxisChart extends BaseChart {
// removeDataPoint(index = 0) {} // removeDataPoint(index = 0) {}
} }
// import MultiAxisChart from './charts/MultiAxisChart';
const chartTypes = { const chartTypes = {
bar: AxisChart, bar: AxisChart,
line: AxisChart, line: AxisChart,

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -46,6 +46,14 @@ header .lead-text {
section { section {
margin: 4em 0; /* SAME 1 */ margin: 4em 0; /* SAME 1 */
} }
section figure {
border: 1px solid #ddd; /* SAME 3 */
border-radius: 3px;
overflow: auto;
}
section code {
margin-top: 1rem; /* SAME 2 */
}
h1 { h1 {
font-size: 3.5rem; font-size: 3.5rem;
margin-bottom: 1.5rem; margin-bottom: 1.5rem;
@ -79,16 +87,19 @@ a, a:focus, a:hover {
/* BaseCSS */ /* BaseCSS */
.margin-top { .mt1 {
margin-top: 1rem; /* SAME 2 */ margin-top: 1rem; /* SAME 2 */
} }
.mv1 { .mv1 {
margin: 2em 0 1em 0; margin: 2em 0 1em 0;
} }
.border { .border {
border: 1px solid #ddd; border: 1px solid #ddd; /* SAME 3 */
border-radius: 3px; border-radius: 3px;
} }
.text-center {
text-align: center;
}
/* Moon images */ /* Moon images */
@ -103,8 +114,3 @@ a, a:focus, a:hover {
margin-bottom: 5px; margin-bottom: 5px;
font-size: 12px; font-size: 12px;
} }
.text-center {
text-align: center;
}

View File

@ -1,4 +1,6 @@
import { MONTH_NAMES_SHORT } from '../../../src/js/utils/date-utils'; import { MONTH_NAMES_SHORT, SEC_IN_DAY, clone, timestampToMidnight,
timestampSec, addDays } from '../../../src/js/utils/date-utils';
import { shuffle, getRandomBias } from '../../../src/js/utils/helpers';
// Composite Chart // Composite Chart
// ================================================================================ // ================================================================================
@ -177,3 +179,29 @@ export const moonData = {
// ================================================================================ // ================================================================================
let today = new Date();
let start = clone(today);
addDays(start, 4);
let end = clone(start);
start.setFullYear( start.getFullYear() - 2 );
end.setFullYear( end.getFullYear() - 1 );
let dataPoints = {};
let startTs = timestampSec(start);
let endTs = timestampSec(end);
startTs = timestampToMidnight(startTs);
endTs = timestampToMidnight(endTs, true);
while (startTs < endTs) {
dataPoints[parseInt(startTs)] = Math.floor(getRandomBias(0, 5, 0.2, 1));
startTs += SEC_IN_DAY;
}
export const heatmapData = {
dataPoints: dataPoints,
start: start,
end: end
};

View File

@ -1,9 +1,9 @@
import { lineCompositeData, barCompositeData } from './data'; import { lineCompositeData, barCompositeData, trendsData, heatmapData } from './data';
import { HEATMAP_COLORS_YELLOW, HEATMAP_COLORS_BLUE } from '../../../src/js/utils/constants';
export default { export default {
lineComposite: { lineComposite: {
elementID: "#chart-composite-1", config: {
options: {
title: "Fireball/Bolide Events - Yearly (reported)", title: "Fireball/Bolide Events - Yearly (reported)",
data: lineCompositeData, data: lineCompositeData,
type: "line", type: "line",
@ -19,8 +19,7 @@ export default {
}, },
barComposite: { barComposite: {
elementID: "#chart-composite-2", config: {
options: {
data: barCompositeData, data: barCompositeData,
type: "bar", type: "bar",
height: 210, height: 210,
@ -35,20 +34,195 @@ export default {
} }
}, },
demoMain: { demoMain: {
elementID: "", title: "Creating a Chart",
options: { contentBlocks: [
title: "My Awesome Chart", {
data: "typeData", type: "text",
type: "axis-mixed", content: `Booga wooga wooga Booga Booga wooga`,
height: 300, },
colors: ["purple", "magenta", "light-blue"], {
maxSlices: 10, type: "code",
lang: "html",
content: ` &lt!--HTML--&gt;
&lt;figure id="frost-chart"&gt;&lt;/figure&gt;`,
},
{
type: "code",
lang: "javascript",
content: ` // Javascript
let chart = new frappe.Chart( "#frost-chart", { // or DOM element
data: {
labels: ["12am-3am", "3am-6am", "6am-9am", "9am-12pm",
"12pm-3pm", "3pm-6pm", "6pm-9pm", "9pm-12am"],
tooltipOptions: { datasets: [
formatTooltipX: d => (d + '').toUpperCase(), {
formatTooltipY: d => d + ' pts', name: "Some Data", chartType: 'bar',
} values: [25, 40, 30, 35, 8, 52, 17, -4]
} },
} {
} name: "Another Set", chartType: 'bar',
values: [25, 50, -10, 15, 18, 32, 27, 14]
},
{
name: "Yet Another", chartType: 'line',
values: [15, 20, -3, -15, 58, 12, -17, 37]
}
],
yMarkers: [{ label: "Marker", value: 70,
options: { labelPos: 'left' }}],
yRegions: [{ label: "Region", start: -10, end: 50,
options: { labelPos: 'right' }}]
},
title: "My Awesome Chart",
type: 'axis-mixed', // or 'bar', 'line', 'pie', 'percentage'
height: 300,
colors: ['purple', '#ffa3ef', 'light-blue'],
tooltipOptions: {
formatTooltipX: d => (d + '').toUpperCase(),
formatTooltipY: d => d + ' pts',
}
});
chart.export();`,
},
{
type: "demo",
config: {
title: "My Awesome Chart",
data: "typeData",
type: "axis-mixed",
height: 300,
colors: ["purple", "magenta", "light-blue"],
maxSlices: 10,
tooltipOptions: {
formatTooltipX: d => (d + '').toUpperCase(),
formatTooltipY: d => d + ' pts',
}
},
sideContent: {},
options: [
{
name: "lineOptions",
path: ["lineOptions"],
type: "map",
mapKeys: ['hideLine', 'hideDots', 'heatline', 'regionFill'],
states: {
"Line": [0, 1, 0, 0],
"Dots": [1, 0, 0, 0],
"HeatLine": [0, 1, 1, 0],
"Region": [0, 1, 0, 1]
},
activeState: "HeatLine"
}
],
actions: [{ name: "Export ...", fn: "export", args: [] }],
}
]
},
updateValues: { },
trendsPlot: {
title: "Plot Trends",
contentBlocks: [
{
type: "demo",
config: {
title: "Mean Total Sunspot Count - Yearly",
data: trendsData,
type: 'line',
height: 300,
colors: ['#238e38'],
axisOptions: {
xAxisMode: 'tick',
yAxisMode: 'span',
xIsSeries: 1
}
},
options: [
{
name: "lineOptions",
path: ["lineOptions"],
type: "map",
mapKeys: ['hideLine', 'hideDots', 'heatline', 'regionFill'],
states: {
"Line": [0, 1, 0, 0],
"Dots": [1, 0, 0, 0],
"HeatLine": [0, 1, 1, 0],
"Region": [0, 1, 0, 1]
},
activeState: "HeatLine"
}
],
actions: [{ name: "Export ...", fn: "export", args: [] }]
}
],
},
stateChange: {},
heatmap: {
title: "And a Month-wise Heatmap",
contentBlocks: [
{
type: "demo",
config: {
title: "Monthly Distribution",
data: heatmapData,
type: 'heatmap',
discreteDomains: 1,
countLabel: 'Level',
colors: HEATMAP_COLORS_BLUE,
legendScale: [0, 1, 2, 4, 5]
},
options: [
{
name: "Discrete domains",
path: ["discreteDomains"],
type: 'boolean',
// boolNames: ["Continuous", "Discrete"],
states: { "Discrete": 1, "Continuous": 0 }
},
{
name: "Colors",
path: ["colors"],
type: "object",
states: {
"Green (Default)": [],
"Blue": HEATMAP_COLORS_BLUE,
"GitHub's Halloween": HEATMAP_COLORS_YELLOW
}
}
],
actions: [{ name: "Export ...", fn: "export", args: [] }]
},
{
type: "code",
lang: "javascript",
content: ` let heatmap = new frappe.Chart("#heatmap", {
type: 'heatmap',
title: "Monthly Distribution",
data: {
dataPoints: {'1524064033': 8, /* ... */},
// object with timestamp-value pairs
start: startDate
end: endDate // Date objects
},
countLabel: 'Level',
discreteDomains: 0 // default: 1
colors: ['#ebedf0', '#c0ddf9', '#73b3f3', '#3886e1', '#17459e'],
// Set of five incremental colors,
// preferably with a low-saturation color for zero data;
// def: ['#ebedf0', '#c6e48b', '#7bc96f', '#239a3b', '#196127']
});`,
}
],
}
}

View File

@ -0,0 +1,108 @@
import { $ } from '../../../src/js/utils/dom';
export class docSectionBuilder {
constructor(LIB_OBJ) {
this.LIB_OBJ = LIB_OBJ;
}
setParent(parent) {
// this.parent = parent;
this.section = parent;
}
setSys(sys) {
this.sys = sys;
this.blockMap = {};
}
make() {
// const section = document.querySelector(this.section);
let s = this.sys;
$.create('h6', { inside: this.section, innerHTML: s.title });
s.contentBlocks.forEach((blockConf, index) => {
this.blockMap[index] = this.getBlock(blockConf);
});
}
getBlock(blockConf) {
let block;
let type = blockConf.type;
if(type === "text") {
block = this.getText(blockConf);
} else if(type === "code") {
block = this.getCode(blockConf);
} else {
block = this.getDemo(blockConf);
}
}
getText(blockConf) {}
getCode(blockConf) {
let pre = $.create('pre', { inside: this.section });
let code = $.create('code', {
inside: pre,
className: `hljs ${blockConf.lang}`,
innerHTML: blockConf.content
});
}
getDemo(blockConf) {
let args = blockConf.config;
let figure = $.create('figure', { inside: this.section });
this.libObj = new this.LIB_OBJ(figure, args);
this.getDemoOptions(blockConf.options, args, figure);
this.getDemoActions(blockConf.actions, args);
}
getDemoOptions(options, args, figure) {
options.forEach(o => {
const btnGroup = $.create('div', {
inside: this.section,
className: `btn-group ${o.name}`
});
const mapKeys = o.mapKeys;
if(o.type === "map") {
args[o.path[0]] = {};
}
Object.keys(o.states).forEach(key => {
let state = o.states[key];
let activeClass = key === o.activeState ? 'active' : '';
let button = $.create('button', {
inside: btnGroup,
className: `btn btn-sm btn-secondary ${activeClass}`,
innerHTML: key,
onClick: (e) => {
// map
if(o.type === "map") {
mapKeys.forEach((attr, i) => {
args[o.path[0]][attr] = state[i];
})
} else {
// boolean, number, object
args[o.path[0]] = state;
}
this.libObj = new this.LIB_OBJ(figure, args);
}
});
if(activeClass) { button.click(); }
});
});
}
getDemoActions(actions, args) {
actions.forEach(o => {
let args = o.args || [];
$.create('button', {
inside: this.section,
className: `btn btn-sm btn-secondary`,
innerHTML: o.name,
onClick: () => {this.libObj[o.fn](...o.args);}
});
});
}
}

File diff suppressed because one or more lines are too long

View File

@ -1,23 +1,19 @@
import { $ } from '../../../src/js/utils/dom';
import { shuffle, getRandomBias } from '../../../src/js/utils/helpers'; import { shuffle, getRandomBias } from '../../../src/js/utils/helpers';
import { HEATMAP_COLORS_YELLOW, HEATMAP_COLORS_BLUE } from '../../../src/js/utils/constants';
import { SEC_IN_DAY, clone, timestampToMidnight, timestampSec, addDays } from '../../../src/js/utils/date-utils';
import { fireballOver25, fireball_2_5, fireball_5_25, lineCompositeData, import { fireballOver25, fireball_2_5, fireball_5_25, lineCompositeData,
barCompositeData, typeData, trendsData, moonData } from './data'; barCompositeData, typeData, trendsData, moonData } from './data';
import demoConfig from './demoConfig'; import dc from './demoConfig';
// import { lineComposite, barComposite } from './demoConfig'; import { docSectionBuilder } from './docSectionBuilder';
// ================================================================================
let Chart = frappe.Chart; // eslint-disable-line no-undef let Chart = frappe.Chart; // eslint-disable-line no-undef
let dcb = new docSectionBuilder(Chart);
let lc = demoConfig.lineComposite; let lineComposite = new Chart("#line-composite-1", dc.lineComposite.config);
let lineCompositeChart = new Chart (lc.elementID, lc.options); let barComposite = new Chart("#bar-composite-1", dc.barComposite.config);
let bc = demoConfig.barComposite; lineComposite.parent.addEventListener('data-select', (e) => {
let barCompositeChart = new Chart (bc.elementID, bc.options);
lineCompositeChart.parent.addEventListener('data-select', (e) => {
let i = e.index; let i = e.index;
barCompositeChart.updateDatasets([ barComposite.updateDatasets([
fireballOver25[i], fireball_5_25[i], fireball_2_5[i] fireballOver25[i], fireball_5_25[i], fireball_2_5[i]
]); ]);
}); });
@ -31,6 +27,7 @@ let typeChartArgs = {
type: 'axis-mixed', type: 'axis-mixed',
height: 300, height: 300,
colors: customColors, colors: customColors,
valuesOverPoints: 1,
// maxLegendPoints: 6, // maxLegendPoints: 6,
maxSlices: 10, maxSlices: 10,
@ -174,59 +171,10 @@ document.querySelector('.export-update').addEventListener('click', () => {
// Trends Chart // Trends Chart
// ================================================================================ // ================================================================================
let plotChartArgs = { let section = document.querySelector('.trends-plot');
title: "Mean Total Sunspot Count - Yearly", dcb.setParent(section);
data: trendsData, dcb.setSys(dc.trendsPlot);
type: 'line', dcb.make();
height: 300,
colors: ['#238e38'],
lineOptions: {
hideDots: 1,
heatline: 1,
},
axisOptions: {
xAxisMode: 'tick',
yAxisMode: 'span',
xIsSeries: 1
}
};
let trendsChart = new Chart("#chart-trends", plotChartArgs);
Array.prototype.slice.call(
document.querySelectorAll('.chart-plot-buttons button')
).map(el => {
el.addEventListener('click', (e) => {
let btn = e.target;
let type = btn.getAttribute('data-type');
let config = {};
config[type] = 1;
if(['regionFill', 'heatline'].includes(type)) {
config.hideDots = 1;
}
// plotChartArgs.init = false;
plotChartArgs.lineOptions = config;
new Chart("#chart-trends", plotChartArgs);
Array.prototype.slice.call(
btn.parentNode.querySelectorAll('button')).map(el => {
el.classList.remove('active');
});
btn.classList.add('active');
});
});
document.querySelector('.export-trends').addEventListener('click', () => {
trendsChart.export();
});
// Event chart
// ================================================================================
let eventsData = { let eventsData = {
@ -262,112 +210,7 @@ eventsChart.parent.addEventListener('data-select', (e) => {
// Heatmap // Heatmap
// ================================================================================ // ================================================================================
let today = new Date(); section = document.querySelector('.heatmap');
let start = clone(today); dcb.setParent(section);
addDays(start, 4); dcb.setSys(dc.heatmap);
let end = clone(start); dcb.make();
start.setFullYear( start.getFullYear() - 2 );
end.setFullYear( end.getFullYear() - 1 );
let dataPoints = {};
let startTs = timestampSec(start);
let endTs = timestampSec(end);
startTs = timestampToMidnight(startTs);
endTs = timestampToMidnight(endTs, true);
while (startTs < endTs) {
dataPoints[parseInt(startTs)] = Math.floor(getRandomBias(0, 5, 0.2, 1));
startTs += SEC_IN_DAY;
}
const heatmapData = {
dataPoints: dataPoints,
start: start,
end: end
};
let heatmapArgs = {
title: "Monthly Distribution",
data: heatmapData,
type: 'heatmap',
discreteDomains: 1,
countLabel: 'Level',
colors: HEATMAP_COLORS_BLUE,
legendScale: [0, 1, 2, 4, 5]
};
let heatmapChart = new Chart("#chart-heatmap", heatmapArgs);
Array.prototype.slice.call(
document.querySelectorAll('.heatmap-mode-buttons button')
).map(el => {
el.addEventListener('click', (e) => {
let btn = e.target;
let mode = btn.getAttribute('data-mode');
let discreteDomains = 0;
if(mode === 'discrete') {
discreteDomains = 1;
}
let colors = [];
let colors_mode = document
.querySelector('.heatmap-color-buttons .active')
.getAttribute('data-color');
if(colors_mode === 'halloween') {
colors = HEATMAP_COLORS_YELLOW;
} else if (colors_mode === 'blue') {
colors = HEATMAP_COLORS_BLUE;
}
heatmapArgs.discreteDomains = discreteDomains;
heatmapArgs.colors = colors;
new Chart("#chart-heatmap", heatmapArgs);
Array.prototype.slice.call(
btn.parentNode.querySelectorAll('button')).map(el => {
el.classList.remove('active');
});
btn.classList.add('active');
});
});
Array.prototype.slice.call(
document.querySelectorAll('.heatmap-color-buttons button')
).map(el => {
el.addEventListener('click', (e) => {
let btn = e.target;
let colors_mode = btn.getAttribute('data-color');
let colors = [];
if(colors_mode === 'halloween') {
colors = HEATMAP_COLORS_YELLOW;
} else if (colors_mode === 'blue') {
colors = HEATMAP_COLORS_BLUE;
}
let discreteDomains = 1;
let view_mode = document
.querySelector('.heatmap-mode-buttons .active')
.getAttribute('data-mode');
if(view_mode === 'continuous') {
discreteDomains = 0;
}
heatmapArgs.discreteDomains = discreteDomains;
heatmapArgs.colors = colors;
new Chart("#chart-heatmap", heatmapArgs);
Array.prototype.slice.call(
btn.parentNode.querySelectorAll('button')).map(el => {
el.classList.remove('active');
});
btn.classList.add('active');
});
});
document.querySelector('.export-heatmap').addEventListener('click', () => {
heatmapChart.export();
});

View File

@ -28,6 +28,171 @@ function __$styleInject(css, ref) {
} }
} }
var asyncGenerator = function () {
function AwaitValue(value) {
this.value = value;
}
function AsyncGenerator(gen) {
var front, back;
function send(key, arg) {
return new Promise(function (resolve, reject) {
var request = {
key: key,
arg: arg,
resolve: resolve,
reject: reject,
next: null
};
if (back) {
back = back.next = request;
} else {
front = back = request;
resume(key, arg);
}
});
}
function resume(key, arg) {
try {
var result = gen[key](arg);
var value = result.value;
if (value instanceof AwaitValue) {
Promise.resolve(value.value).then(function (arg) {
resume("next", arg);
}, function (arg) {
resume("throw", arg);
});
} else {
settle(result.done ? "return" : "normal", result.value);
}
} catch (err) {
settle("throw", err);
}
}
function settle(type, value) {
switch (type) {
case "return":
front.resolve({
value: value,
done: true
});
break;
case "throw":
front.reject(value);
break;
default:
front.resolve({
value: value,
done: false
});
break;
}
front = front.next;
if (front) {
resume(front.key, front.arg);
} else {
back = null;
}
}
this._invoke = send;
if (typeof gen.return !== "function") {
this.return = undefined;
}
}
if (typeof Symbol === "function" && Symbol.asyncIterator) {
AsyncGenerator.prototype[Symbol.asyncIterator] = function () {
return this;
};
}
AsyncGenerator.prototype.next = function (arg) {
return this._invoke("next", arg);
};
AsyncGenerator.prototype.throw = function (arg) {
return this._invoke("throw", arg);
};
AsyncGenerator.prototype.return = function (arg) {
return this._invoke("return", arg);
};
return {
wrap: function (fn) {
return function () {
return new AsyncGenerator(fn.apply(this, arguments));
};
},
await: function (value) {
return new AwaitValue(value);
}
};
}();
var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; };
function $(expr, con) {
return typeof expr === "string" ? (con || document).querySelector(expr) : expr || null;
}
$.create = function (tag, o) {
var element = document.createElement(tag);
for (var i in o) {
var val = o[i];
if (i === "inside") {
$(val).appendChild(element);
} else if (i === "around") {
var ref = $(val);
ref.parentNode.insertBefore(element, ref);
element.appendChild(ref);
} else if (i === "onClick") {
element.addEventListener('click', val);
} else if (i === "styles") {
if ((typeof val === "undefined" ? "undefined" : _typeof(val)) === "object") {
Object.keys(val).map(function (prop) {
element.style[prop] = val[prop];
});
}
} else if (i in element) {
element[i] = val;
} else {
element.setAttribute(i, val);
}
}
return element;
};
// https://css-tricks.com/snippets/javascript/loop-queryselectorall-matches/
// Fixed 5-color theme, // Fixed 5-color theme,
// More colors are difficult to parse visually // More colors are difficult to parse visually
@ -267,74 +432,342 @@ var moonData = {
// ================================================================================ // ================================================================================
var demoConfig = { var today = new Date();
lineComposite: { var start = clone(today);
elementID: "#chart-composite-1", addDays(start, 4);
options: { var end = clone(start);
title: "Fireball/Bolide Events - Yearly (reported)", start.setFullYear(start.getFullYear() - 2);
data: lineCompositeData, end.setFullYear(end.getFullYear() - 1);
type: "line",
height: 190,
colors: ["green"],
isNavigable: 1,
valuesOverPoints: 1,
lineOptions: { var dataPoints = {};
dotSize: 8
}
}
},
barComposite: { var startTs = timestampSec(start);
elementID: "#chart-composite-2", var endTs = timestampSec(end);
options: {
data: barCompositeData,
type: "bar",
height: 210,
colors: ["violet", "light-blue", "#46a9f9"],
valuesOverPoints: 1,
axisOptions: {
xAxisMode: "tick"
},
barOptions: {
stacked: 1
}
}
},
demoMain: { startTs = timestampToMidnight(startTs);
elementID: "", endTs = timestampToMidnight(endTs, true);
options: {
title: "My Awesome Chart",
data: "typeData",
type: "axis-mixed",
height: 300,
colors: ["purple", "magenta", "light-blue"],
maxSlices: 10,
tooltipOptions: { while (startTs < endTs) {
formatTooltipX: function formatTooltipX(d) { dataPoints[parseInt(startTs)] = Math.floor(getRandomBias(0, 5, 0.2, 1));
return (d + '').toUpperCase(); startTs += SEC_IN_DAY;
}, }
formatTooltipY: function formatTooltipY(d) {
return d + ' pts'; var heatmapData = {
} dataPoints: dataPoints,
} start: start,
} end: end
}
}; };
var dc = {
lineComposite: {
config: {
title: "Fireball/Bolide Events - Yearly (reported)",
data: lineCompositeData,
type: "line",
height: 190,
colors: ["green"],
isNavigable: 1,
valuesOverPoints: 1,
lineOptions: {
dotSize: 8
}
}
},
barComposite: {
config: {
data: barCompositeData,
type: "bar",
height: 210,
colors: ["violet", "light-blue", "#46a9f9"],
valuesOverPoints: 1,
axisOptions: {
xAxisMode: "tick"
},
barOptions: {
stacked: 1
}
}
},
demoMain: {
title: "Creating a Chart",
contentBlocks: [{
type: "text",
content: 'Booga wooga wooga Booga Booga wooga'
}, {
type: "code",
lang: "html",
content: ' &lt!--HTML--&gt;\n &lt;figure id="frost-chart"&gt;&lt;/figure&gt;'
}, {
type: "code",
lang: "javascript",
content: ' // Javascript\n let chart = new frappe.Chart( "#frost-chart", { // or DOM element\n data: {\n labels: ["12am-3am", "3am-6am", "6am-9am", "9am-12pm",\n "12pm-3pm", "3pm-6pm", "6pm-9pm", "9pm-12am"],\n\n datasets: [\n {\n name: "Some Data", chartType: \'bar\',\n values: [25, 40, 30, 35, 8, 52, 17, -4]\n },\n {\n name: "Another Set", chartType: \'bar\',\n values: [25, 50, -10, 15, 18, 32, 27, 14]\n },\n {\n name: "Yet Another", chartType: \'line\',\n values: [15, 20, -3, -15, 58, 12, -17, 37]\n }\n ],\n\n yMarkers: [{ label: "Marker", value: 70,\n options: { labelPos: \'left\' }}],\n yRegions: [{ label: "Region", start: -10, end: 50,\n options: { labelPos: \'right\' }}]\n },\n\n title: "My Awesome Chart",\n type: \'axis-mixed\', // or \'bar\', \'line\', \'pie\', \'percentage\'\n height: 300,\n colors: [\'purple\', \'#ffa3ef\', \'light-blue\'],\n\n tooltipOptions: {\n formatTooltipX: d => (d + \'\').toUpperCase(),\n formatTooltipY: d => d + \' pts\',\n }\n });\n\n chart.export();'
}, {
type: "demo",
config: {
title: "My Awesome Chart",
data: "typeData",
type: "axis-mixed",
height: 300,
colors: ["purple", "magenta", "light-blue"],
maxSlices: 10,
tooltipOptions: {
formatTooltipX: function formatTooltipX(d) {
return (d + '').toUpperCase();
},
formatTooltipY: function formatTooltipY(d) {
return d + ' pts';
}
}
},
sideContent: {},
options: [{
name: "lineOptions",
path: ["lineOptions"],
type: "map",
mapKeys: ['hideLine', 'hideDots', 'heatline', 'regionFill'],
states: {
"Line": [0, 1, 0, 0],
"Dots": [1, 0, 0, 0],
"HeatLine": [0, 1, 1, 0],
"Region": [0, 1, 0, 1]
},
activeState: "HeatLine"
}],
actions: [{ name: "Export ...", fn: "export", args: [] }]
}]
},
updateValues: {},
trendsPlot: {
title: "Plot Trends",
contentBlocks: [{
type: "demo",
config: {
title: "Mean Total Sunspot Count - Yearly",
data: trendsData,
type: 'line',
height: 300,
colors: ['#238e38'],
axisOptions: {
xAxisMode: 'tick',
yAxisMode: 'span',
xIsSeries: 1
}
},
options: [{
name: "lineOptions",
path: ["lineOptions"],
type: "map",
mapKeys: ['hideLine', 'hideDots', 'heatline', 'regionFill'],
states: {
"Line": [0, 1, 0, 0],
"Dots": [1, 0, 0, 0],
"HeatLine": [0, 1, 1, 0],
"Region": [0, 1, 0, 1]
},
activeState: "HeatLine"
}],
actions: [{ name: "Export ...", fn: "export", args: [] }]
}]
},
stateChange: {},
heatmap: {
title: "And a Month-wise Heatmap",
contentBlocks: [{
type: "demo",
config: {
title: "Monthly Distribution",
data: heatmapData,
type: 'heatmap',
discreteDomains: 1,
countLabel: 'Level',
colors: HEATMAP_COLORS_BLUE,
legendScale: [0, 1, 2, 4, 5]
},
options: [{
name: "Discrete domains",
path: ["discreteDomains"],
type: 'boolean',
// boolNames: ["Continuous", "Discrete"],
states: { "Discrete": 1, "Continuous": 0 }
}, {
name: "Colors",
path: ["colors"],
type: "object",
states: {
"Green (Default)": [],
"Blue": HEATMAP_COLORS_BLUE,
"GitHub's Halloween": HEATMAP_COLORS_YELLOW
}
}],
actions: [{ name: "Export ...", fn: "export", args: [] }]
}, {
type: "code",
lang: "javascript",
content: ' let heatmap = new frappe.Chart("#heatmap", {\n type: \'heatmap\',\n title: "Monthly Distribution",\n data: {\n dataPoints: {\'1524064033\': 8, /* ... */},\n // object with timestamp-value pairs\n start: startDate\n end: endDate // Date objects\n },\n countLabel: \'Level\',\n discreteDomains: 0 // default: 1\n colors: [\'#ebedf0\', \'#c0ddf9\', \'#73b3f3\', \'#3886e1\', \'#17459e\'],\n // Set of five incremental colors,\n // preferably with a low-saturation color for zero data;\n // def: [\'#ebedf0\', \'#c6e48b\', \'#7bc96f\', \'#239a3b\', \'#196127\']\n });'
}]
}
};
var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } }
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
var docSectionBuilder = function () {
function docSectionBuilder(LIB_OBJ) {
_classCallCheck(this, docSectionBuilder);
this.LIB_OBJ = LIB_OBJ;
}
_createClass(docSectionBuilder, [{
key: 'setParent',
value: function setParent(parent) {
// this.parent = parent;
this.section = parent;
}
}, {
key: 'setSys',
value: function setSys(sys) {
this.sys = sys;
this.blockMap = {};
}
}, {
key: 'make',
value: function make() {
var _this = this;
// const section = document.querySelector(this.section);
var s = this.sys;
$.create('h6', { inside: this.section, innerHTML: s.title });
s.contentBlocks.forEach(function (blockConf, index) {
_this.blockMap[index] = _this.getBlock(blockConf);
});
}
}, {
key: 'getBlock',
value: function getBlock(blockConf) {
var block = void 0;
var type = blockConf.type;
if (type === "text") {
block = this.getText(blockConf);
} else if (type === "code") {
block = this.getCode(blockConf);
} else {
block = this.getDemo(blockConf);
}
}
}, {
key: 'getText',
value: function getText(blockConf) {}
}, {
key: 'getCode',
value: function getCode(blockConf) {
var pre = $.create('pre', { inside: this.section });
var code = $.create('code', {
inside: pre,
className: 'hljs ' + blockConf.lang,
innerHTML: blockConf.content
});
}
}, {
key: 'getDemo',
value: function getDemo(blockConf) {
var args = blockConf.config;
var figure = $.create('figure', { inside: this.section });
this.libObj = new this.LIB_OBJ(figure, args);
this.getDemoOptions(blockConf.options, args, figure);
this.getDemoActions(blockConf.actions, args);
}
}, {
key: 'getDemoOptions',
value: function getDemoOptions(options, args, figure) {
var _this2 = this;
options.forEach(function (o) {
var btnGroup = $.create('div', {
inside: _this2.section,
className: 'btn-group ' + o.name
});
var mapKeys = o.mapKeys;
if (o.type === "map") {
args[o.path[0]] = {};
}
Object.keys(o.states).forEach(function (key) {
var state = o.states[key];
var activeClass = key === o.activeState ? 'active' : '';
var button = $.create('button', {
inside: btnGroup,
className: 'btn btn-sm btn-secondary ' + activeClass,
innerHTML: key,
onClick: function onClick(e) {
// map
if (o.type === "map") {
mapKeys.forEach(function (attr, i) {
args[o.path[0]][attr] = state[i];
});
} else {
// boolean, number, object
args[o.path[0]] = state;
}
_this2.libObj = new _this2.LIB_OBJ(figure, args);
}
});
if (activeClass) {
button.click();
}
});
});
}
}, {
key: 'getDemoActions',
value: function getDemoActions(actions, args) {
var _this3 = this;
actions.forEach(function (o) {
$.create('button', {
inside: _this3.section,
className: 'btn btn-sm btn-secondary',
innerHTML: o.name,
onClick: function onClick() {
var _libObj;
(_libObj = _this3.libObj)[o.fn].apply(_libObj, _toConsumableArray(o.args));
}
});
});
}
}]);
return docSectionBuilder;
}();
var Chart = frappe.Chart; // eslint-disable-line no-undef var Chart = frappe.Chart; // eslint-disable-line no-undef
var dcb = new docSectionBuilder(Chart);
var lc = demoConfig.lineComposite; var lineComposite = new Chart("#line-composite-1", dc.lineComposite.config);
var lineCompositeChart = new Chart(lc.elementID, lc.options); var barComposite = new Chart("#bar-composite-1", dc.barComposite.config);
var bc = demoConfig.barComposite; lineComposite.parent.addEventListener('data-select', function (e) {
var barCompositeChart = new Chart(bc.elementID, bc.options);
lineCompositeChart.parent.addEventListener('data-select', function (e) {
var i = e.index; var i = e.index;
barCompositeChart.updateDatasets([fireballOver25[i], fireball_5_25[i], fireball_2_5[i]]); barComposite.updateDatasets([fireballOver25[i], fireball_5_25[i], fireball_2_5[i]]);
}); });
// ================================================================================ // ================================================================================
@ -346,6 +779,7 @@ var typeChartArgs = {
type: 'axis-mixed', type: 'axis-mixed',
height: 300, height: 300,
colors: customColors, colors: customColors,
valuesOverPoints: 1,
// maxLegendPoints: 6, // maxLegendPoints: 6,
maxSlices: 10, maxSlices: 10,
@ -486,55 +920,10 @@ document.querySelector('.export-update').addEventListener('click', function () {
// Trends Chart // Trends Chart
// ================================================================================ // ================================================================================
var plotChartArgs = { var section = document.querySelector('.trends-plot');
title: "Mean Total Sunspot Count - Yearly", dcb.setParent(section);
data: trendsData, dcb.setSys(dc.trendsPlot);
type: 'line', dcb.make();
height: 300,
colors: ['#238e38'],
lineOptions: {
hideDots: 1,
heatline: 1
},
axisOptions: {
xAxisMode: 'tick',
yAxisMode: 'span',
xIsSeries: 1
}
};
var trendsChart = new Chart("#chart-trends", plotChartArgs);
Array.prototype.slice.call(document.querySelectorAll('.chart-plot-buttons button')).map(function (el) {
el.addEventListener('click', function (e) {
var btn = e.target;
var type = btn.getAttribute('data-type');
var config = {};
config[type] = 1;
if (['regionFill', 'heatline'].includes(type)) {
config.hideDots = 1;
}
// plotChartArgs.init = false;
plotChartArgs.lineOptions = config;
new Chart("#chart-trends", plotChartArgs);
Array.prototype.slice.call(btn.parentNode.querySelectorAll('button')).map(function (el) {
el.classList.remove('active');
});
btn.classList.add('active');
});
});
document.querySelector('.export-trends').addEventListener('click', function () {
trendsChart.export();
});
// Event chart
// ================================================================================
var eventsData = { var eventsData = {
labels: ["Ganymede", "Callisto", "Io", "Europa"], labels: ["Ganymede", "Callisto", "Io", "Europa"],
@ -569,104 +958,9 @@ eventsChart.parent.addEventListener('data-select', function (e) {
// Heatmap // Heatmap
// ================================================================================ // ================================================================================
var today = new Date(); section = document.querySelector('.heatmap');
var start = clone(today); dcb.setParent(section);
addDays(start, 4); dcb.setSys(dc.heatmap);
var end = clone(start); dcb.make();
start.setFullYear(start.getFullYear() - 2);
end.setFullYear(end.getFullYear() - 1);
var dataPoints = {};
var startTs = timestampSec(start);
var endTs = timestampSec(end);
startTs = timestampToMidnight(startTs);
endTs = timestampToMidnight(endTs, true);
while (startTs < endTs) {
dataPoints[parseInt(startTs)] = Math.floor(getRandomBias(0, 5, 0.2, 1));
startTs += SEC_IN_DAY;
}
var heatmapData = {
dataPoints: dataPoints,
start: start,
end: end
};
var heatmapArgs = {
title: "Monthly Distribution",
data: heatmapData,
type: 'heatmap',
discreteDomains: 1,
countLabel: 'Level',
colors: HEATMAP_COLORS_BLUE,
legendScale: [0, 1, 2, 4, 5]
};
var heatmapChart = new Chart("#chart-heatmap", heatmapArgs);
Array.prototype.slice.call(document.querySelectorAll('.heatmap-mode-buttons button')).map(function (el) {
el.addEventListener('click', function (e) {
var btn = e.target;
var mode = btn.getAttribute('data-mode');
var discreteDomains = 0;
if (mode === 'discrete') {
discreteDomains = 1;
}
var colors = [];
var colors_mode = document.querySelector('.heatmap-color-buttons .active').getAttribute('data-color');
if (colors_mode === 'halloween') {
colors = HEATMAP_COLORS_YELLOW;
} else if (colors_mode === 'blue') {
colors = HEATMAP_COLORS_BLUE;
}
heatmapArgs.discreteDomains = discreteDomains;
heatmapArgs.colors = colors;
new Chart("#chart-heatmap", heatmapArgs);
Array.prototype.slice.call(btn.parentNode.querySelectorAll('button')).map(function (el) {
el.classList.remove('active');
});
btn.classList.add('active');
});
});
Array.prototype.slice.call(document.querySelectorAll('.heatmap-color-buttons button')).map(function (el) {
el.addEventListener('click', function (e) {
var btn = e.target;
var colors_mode = btn.getAttribute('data-color');
var colors = [];
if (colors_mode === 'halloween') {
colors = HEATMAP_COLORS_YELLOW;
} else if (colors_mode === 'blue') {
colors = HEATMAP_COLORS_BLUE;
}
var discreteDomains = 1;
var view_mode = document.querySelector('.heatmap-mode-buttons .active').getAttribute('data-mode');
if (view_mode === 'continuous') {
discreteDomains = 0;
}
heatmapArgs.discreteDomains = discreteDomains;
heatmapArgs.colors = colors;
new Chart("#chart-heatmap", heatmapArgs);
Array.prototype.slice.call(btn.parentNode.querySelectorAll('button')).map(function (el) {
el.classList.remove('active');
});
btn.classList.add('active');
});
});
document.querySelector('.export-heatmap').addEventListener('click', function () {
heatmapChart.export();
});
}()); }());

View File

@ -4,7 +4,8 @@
<meta charset="UTF-8"> <meta charset="UTF-8">
<title>Frappe Charts</title> <title>Frappe Charts</title>
<meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="keywords" content="open source javascript js charts library svg zero-dependency interactive data visualization beautiful drag resize"> <meta name="keywords" content="open source javascript js charts library svg zero-dependency
interactive data visualization beautiful drag resize">
<meta name="description" content="A simple, responsive, modern charts library for the web."> <meta name="description" content="A simple, responsive, modern charts library for the web.">
<link rel="stylesheet" type="text/css" href="assets/css/reset.css" media="screen"> <link rel="stylesheet" type="text/css" href="assets/css/reset.css" media="screen">
@ -24,18 +25,20 @@
<body> <body>
<header> <header>
<h1>Frappe Charts</h1> <h1>Frappe Charts</h1>
<p class="lead-text">GitHub-inspired simple and modern SVG charts for the web<br>with zero dependencies.</p> <p class="lead-text">GitHub-inspired simple and modern SVG charts for the web
<div id="chart-composite-1" class="border"></div> <br>with zero dependencies.</p>
<figure id="line-composite-1" class="border"></figure>
<p class="demo-tip">Click or use arrow keys to navigate data points</p> <p class="demo-tip">Click or use arrow keys to navigate data points</p>
<div id="chart-composite-2" class="border"></div> <figure id="bar-composite-1" class="border"></figure>
</header> </header>
<section> <section>
<h6>Create a chart</h6> <h6>Create a chart</h6>
<p>Click or use arrow keys to navigate data points</p>
<pre><code class="hljs html"> &lt!--HTML--&gt; <pre><code class="hljs html"> &lt!--HTML--&gt;
&lt;div id="chart"&gt;&lt;/div&gt;</code></pre> &lt;figure id="frost-chart"&gt;&lt;/figure&gt;</code></pre>
<pre><code class="hljs javascript"> // Javascript <pre><code class="hljs javascript"> // Javascript
let chart = new frappe.Chart( "#chart", { // or DOM element let chart = new frappe.Chart( "#frost-chart", { // or DOM element
data: { data: {
labels: ["12am-3am", "3am-6am", "6am-9am", "9am-12pm", labels: ["12am-3am", "3am-6am", "6am-9am", "9am-12pm",
"12pm-3pm", "3pm-6pm", "6pm-9pm", "9pm-12am"], "12pm-3pm", "3pm-6pm", "6pm-9pm", "9pm-12am"],
@ -76,14 +79,13 @@
</code></pre> </code></pre>
<div id="chart-aggr" class="border"></div> <div id="chart-aggr" class="border"></div>
<div class="btn-group aggr-type-buttons margin-top mx-auto" role="group"> <div class="btn-group aggr-type-buttons mt1 mx-auto" role="group">
<button type="button" class="btn btn-sm btn-secondary active" data-type='axis-mixed'>Mixed</button> <button type="button" class="btn btn-sm btn-secondary active" data-type='axis-mixed'>Mixed</button>
<button type="button" class="btn btn-sm btn-secondary" data-type='pie'>Pie Chart</button> <button type="button" class="btn btn-sm btn-secondary" data-type='pie'>Pie Chart</button>
<button type="button" class="btn btn-sm btn-secondary" data-type='percentage'>Percentage Chart</button> <button type="button" class="btn btn-sm btn-secondary" data-type='percentage'>Percentage Chart</button>
</div> </div>
<div class="btn-group export-buttons margin-top mx-auto" role="group"> <button type="button" class="btn btn-sm btn-secondary export-aggr mt1">Export ...</button>
<button type="button" class="btn btn-sm btn-secondary export-aggr">Export ...</button>
</div>
</section> </section>
<section> <section>
@ -98,20 +100,7 @@
</div> </div>
</section> </section>
<section> <section class="trends-plot"></section>
<h6>Plot Trends</h6>
<div id="chart-trends" class="border"></div>
<div class="btn-group chart-plot-buttons mt-1 mx-auto" role="group">
<button type="button" class="btn btn-sm btn-secondary" data-type="hideDots">Line</button>
<button type="button" class="btn btn-sm btn-secondary" data-type="hideLine">Dots</button>
<button type="button" class="btn btn-sm btn-secondary active" data-type="heatline">HeatLine</button>
<button type="button" class="btn btn-sm btn-secondary" data-type="regionFill">Region</button>
</div>
<div class="btn-group export-buttons mt-1 mx-auto" role="group">
<button type="button" class="btn btn-sm btn-secondary export-trends">Export ...</button>
</div>
</section>
<section> <section>
<h6>Listen to state change</h6> <h6>Listen to state change</h6>
@ -123,7 +112,7 @@
<div class="image-container border"> <div class="image-container border">
<img class="moon-image" src="./assets/img/europa.jpg"> <img class="moon-image" src="./assets/img/europa.jpg">
</div> </div>
<div class="content-data margin-top"> <div class="content-data mt1">
<h6 class="moon-name">Europa</h6> <h6 class="moon-name">Europa</h6>
<p>Semi-major-axis: <span class="semi-major-axis">671034</span> km</p> <p>Semi-major-axis: <span class="semi-major-axis">671034</span> km</p>
<p>Mass: <span class="mass">4800000</span> x 10^16 kg</p> <p>Mass: <span class="mass">4800000</span> x 10^16 kg</p>
@ -131,7 +120,7 @@
</div> </div>
</div> </div>
</div> </div>
<pre><code class="hljs javascript margin-top"> ... <pre><code class="hljs javascript mt1"> ...
isNavigable: 1, // Navigate across data points; default 0 isNavigable: 1, // Navigate across data points; default 0
... ...
@ -140,41 +129,7 @@
});</code></pre> });</code></pre>
</section> </section>
<section> <section class="heatmap"></section>
<h6>
And a Month-wise Heatmap
</h6>
<div id="chart-heatmap" class="border"
style="overflow: scroll;"></div>
<div class="heatmap-mode-buttons btn-group mt-1 mx-auto" role="group">
<button type="button" class="btn btn-sm btn-secondary active" data-mode="discrete">Discrete</button>
<button type="button" class="btn btn-sm btn-secondary" data-mode="continuous">Continuous</button>
</div>
<div class="heatmap-color-buttons btn-group mt-1 mx-auto" role="group">
<button type="button" class="btn btn-sm btn-secondary" data-color="default">Green (Default)</button>
<button type="button" class="btn btn-sm btn-secondary active" data-color="blue">Blue</button>
<button type="button" class="btn btn-sm btn-secondary" data-color="halloween">GitHub's Halloween</button>
</div>
<div class="btn-group export-buttons mt-1 mx-auto" role="group">
<button type="button" class="btn btn-sm btn-secondary export-heatmap">Export ...</button>
</div>
<pre><code class="hljs javascript margin-top"> let heatmap = new frappe.Chart("#heatmap", {
type: 'heatmap',
title: "Monthly Distribution",
data: {
dataPoints: {'1524064033': 8, /* ... */},
// object with timestamp-value pairs
start: startDate
end: endDate // Date objects
},
countLabel: 'Level',
discreteDomains: 0 // default: 1
colors: ['#ebedf0', '#c0ddf9', '#73b3f3', '#3886e1', '#17459e'],
// Set of five incremental colors,
// preferably with a low-saturation color for zero data;
// def: ['#ebedf0', '#c6e48b', '#7bc96f', '#239a3b', '#196127']
});</code></pre>
</section>
<section> <section>
<h6>Demo</h6> <h6>Demo</h6>
@ -279,11 +234,11 @@
<section class="text-center"> <section class="text-center">
<!-- Closing --> <!-- Closing -->
<a href="https://github.com/frappe/charts/archive/master.zip"><button class="large blue button btn">Download</button></a> <a href="https://github.com/frappe/charts/archive/master.zip"><button class="large blue button btn">Download</button></a>
<p style="margin-top: 3rem;margin-bottom: 1.5rem;"> <p style="mt1: 3rem;margin-bottom: 1.5rem;">
<!-- <a href="docs.html" style="margin-right: 1rem;" target="_blank">Documentation</a> --> <!-- <a href="docs.html" style="margin-right: 1rem;" target="_blank">Documentation</a> -->
<a href="https://github.com/frappe/charts" target="_blank">View on GitHub</a> <a href="https://github.com/frappe/charts" target="_blank">View on GitHub</a>
</p> </p>
<p style="margin-top: 1rem;"> <p style="mt1: 1rem;">
<a class="github-button" href="https://github.com/frappe/charts" data-icon="octicon-star" data-show-count="true" aria-label="Star frappe/charts on GitHub">Star</a> <a class="github-button" href="https://github.com/frappe/charts" data-icon="octicon-star" data-show-count="true" aria-label="Star frappe/charts on GitHub">Star</a>
</p> </p>
<p>License: MIT</p> <p>License: MIT</p>

View File

@ -26,6 +26,9 @@ $.create = (tag, o) => {
ref.parentNode.insertBefore(element, ref); ref.parentNode.insertBefore(element, ref);
element.appendChild(ref); element.appendChild(ref);
} else if (i === "onClick" ) {
element.addEventListener('click', val);
} else if (i === "styles") { } else if (i === "styles") {
if(typeof val === "object") { if(typeof val === "object") {
Object.keys(val).map(prop => { Object.keys(val).map(prop => {
@ -118,13 +121,17 @@ export function forEachNode(nodeList, callback, scope) {
} }
} }
export function activate($parent, $child, commonClass, activeClass='active', index = -1) { export function activate($parent, $child, commonSelector, activeClass='active', index = -1) {
let $children = $parent.querySelectorAll(`.${commonClass}.${activeClass}`); let $children = $parent.querySelectorAll(`${commonSelector}.${activeClass}`);
forEachNode($children, (node, i) => { if (typeof $child === 'string') {
$child = $parent.querySelector($child);
}
this.forEachNode($children, (node, i) => {
if(index >= 0 && i <= index) return; if(index >= 0 && i <= index) return;
node.classList.remove(activeClass); node.classList.remove(activeClass);
}); })
$child.classList.add(activeClass); $child.classList.add(activeClass);
} }