tsichart-core 2.0.0 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +1254 -5
- package/dist/index.js +1006 -1416
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1006 -1416
- package/dist/index.mjs.map +1 -1
- package/dist/index.umd.js +1040 -1446
- package/dist/index.umd.js.map +1 -1
- package/dist/styles/index.css +9151 -7048
- package/dist/styles/index.css.map +1 -1
- package/package.json +1 -1
package/dist/index.umd.js
CHANGED
|
@@ -21599,8 +21599,6 @@
|
|
|
21599
21599
|
return voronoi;
|
|
21600
21600
|
}
|
|
21601
21601
|
|
|
21602
|
-
var commonjsGlobal = typeof globalThis !== 'undefined' ? globalThis : typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {};
|
|
21603
|
-
|
|
21604
21602
|
function getDefaultExportFromCjs (x) {
|
|
21605
21603
|
return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, 'default') ? x['default'] : x;
|
|
21606
21604
|
}
|
|
@@ -29015,7 +29013,7 @@
|
|
|
29015
29013
|
swimLaneLabelHeightPadding: 8,
|
|
29016
29014
|
labelLeftPadding: 28
|
|
29017
29015
|
};
|
|
29018
|
-
const CharactersToEscapeForExactSearchInstance = ['"', '`', '\'', '!', '(', ')', '^', '[', '{', ':', '
|
|
29016
|
+
const CharactersToEscapeForExactSearchInstance = ['"', '`', '\'', '!', '(', ')', '^', '[', '{', ':', '}', ']', '~', '/', '\\', '@', '#', '$', '%', '&', '*', ';', '=', '.', '_', '-', '<', '>', ',', '?'];
|
|
29019
29017
|
const NONNUMERICTOPMARGIN = 8;
|
|
29020
29018
|
const LINECHARTTOPPADDING = 16;
|
|
29021
29019
|
const GRIDCONTAINERCLASS = 'tsi-gridContainer';
|
|
@@ -30451,35 +30449,41 @@
|
|
|
30451
30449
|
}
|
|
30452
30450
|
}
|
|
30453
30451
|
|
|
30454
|
-
|
|
30455
|
-
|
|
30452
|
+
/**
|
|
30453
|
+
* Constants for Legend component layout and behavior
|
|
30454
|
+
*/
|
|
30455
|
+
const LEGEND_CONSTANTS = {
|
|
30456
|
+
/** Height in pixels for each numeric split-by item (includes type selector dropdown) */
|
|
30457
|
+
NUMERIC_SPLITBY_HEIGHT: 44,
|
|
30458
|
+
/** Height in pixels for each non-numeric (categorical/events) split-by item */
|
|
30459
|
+
NON_NUMERIC_SPLITBY_HEIGHT: 24,
|
|
30460
|
+
/** Height in pixels for the series name label header */
|
|
30461
|
+
NAME_LABEL_HEIGHT: 24,
|
|
30462
|
+
/** Buffer distance in pixels from scroll edge before triggering "load more" */
|
|
30463
|
+
SCROLL_BUFFER: 40,
|
|
30464
|
+
/** Number of split-by items to load per batch when paginating */
|
|
30465
|
+
BATCH_SIZE: 20,
|
|
30466
|
+
/** Minimum height in pixels for aggregate container */
|
|
30467
|
+
MIN_AGGREGATE_HEIGHT: 201,
|
|
30468
|
+
/** Minimum width in pixels for each series label in compact mode */
|
|
30469
|
+
MIN_SERIES_WIDTH: 124,
|
|
30470
|
+
};
|
|
30456
30471
|
class Legend extends Component {
|
|
30457
30472
|
constructor(drawChart, renderTarget, legendWidth) {
|
|
30458
30473
|
super(renderTarget);
|
|
30459
30474
|
this.renderSplitBys = (aggKey, aggSelection, dataType, noSplitBys) => {
|
|
30460
|
-
|
|
30461
|
-
|
|
30462
|
-
var firstSplitByType = firstSplitBy ? firstSplitBy.visibleType : null;
|
|
30463
|
-
Object.keys(this.chartComponentData.displayState[aggKey].splitBys).reduce((isSame, curr) => {
|
|
30464
|
-
return (firstSplitByType == this.chartComponentData.displayState[aggKey].splitBys[curr].visibleType) && isSame;
|
|
30465
|
-
}, true);
|
|
30466
|
-
let showMoreSplitBys = () => {
|
|
30467
|
-
const oldShownSplitBys = this.chartComponentData.displayState[aggKey].shownSplitBys;
|
|
30468
|
-
this.chartComponentData.displayState[aggKey].shownSplitBys = Math.min(oldShownSplitBys + 20, splitByLabelData.length);
|
|
30469
|
-
if (oldShownSplitBys != this.chartComponentData.displayState[aggKey].shownSplitBys) {
|
|
30470
|
-
this.renderSplitBys(aggKey, aggSelection, dataType, noSplitBys);
|
|
30471
|
-
}
|
|
30472
|
-
};
|
|
30475
|
+
const splitByLabelData = Object.keys(this.chartComponentData.timeArrays[aggKey]);
|
|
30476
|
+
const showMoreSplitBys = () => this.handleShowMoreSplitBys(aggKey, splitByLabelData, aggSelection, dataType, noSplitBys);
|
|
30473
30477
|
let splitByContainer = aggSelection.selectAll(".tsi-splitByContainer").data([aggKey]);
|
|
30474
|
-
|
|
30478
|
+
const splitByContainerEntered = splitByContainer.enter().append("div")
|
|
30475
30479
|
.merge(splitByContainer)
|
|
30476
30480
|
.classed("tsi-splitByContainer", true);
|
|
30477
|
-
|
|
30481
|
+
const splitByLabels = splitByContainerEntered.selectAll('.tsi-splitByLabel')
|
|
30478
30482
|
.data(splitByLabelData.slice(0, this.chartComponentData.displayState[aggKey].shownSplitBys), function (d) {
|
|
30479
30483
|
return d;
|
|
30480
30484
|
});
|
|
30481
|
-
|
|
30482
|
-
|
|
30485
|
+
const self = this;
|
|
30486
|
+
const splitByLabelsEntered = splitByLabels
|
|
30483
30487
|
.enter()
|
|
30484
30488
|
.append("div")
|
|
30485
30489
|
.merge(splitByLabels)
|
|
@@ -30493,135 +30497,60 @@
|
|
|
30493
30497
|
}
|
|
30494
30498
|
})
|
|
30495
30499
|
.on("click", function (event, splitBy) {
|
|
30496
|
-
|
|
30497
|
-
self.toggleSplitByVisible(aggKey, splitBy);
|
|
30498
|
-
}
|
|
30499
|
-
else {
|
|
30500
|
-
self.toggleSticky(aggKey, splitBy);
|
|
30501
|
-
}
|
|
30502
|
-
self.drawChart();
|
|
30500
|
+
self.handleSplitByClick(aggKey, splitBy);
|
|
30503
30501
|
})
|
|
30504
30502
|
.on("mouseover", function (event, splitBy) {
|
|
30505
30503
|
event.stopPropagation();
|
|
30506
|
-
self.
|
|
30504
|
+
self.handleSplitByMouseOver(aggKey, splitBy);
|
|
30507
30505
|
})
|
|
30508
30506
|
.on("mouseout", function (event) {
|
|
30509
30507
|
event.stopPropagation();
|
|
30510
|
-
self.
|
|
30511
|
-
.attr("stroke-opacity", 1)
|
|
30512
|
-
.attr("fill-opacity", 1);
|
|
30513
|
-
self.labelMouseout(self.svgSelection, aggKey);
|
|
30508
|
+
self.handleSplitByMouseOut(aggKey);
|
|
30514
30509
|
})
|
|
30515
30510
|
.attr("class", (splitBy, i) => {
|
|
30516
|
-
|
|
30517
|
-
|
|
30518
|
-
return `tsi-splitByLabel
|
|
30511
|
+
const compact = (dataType !== DataTypes.Numeric) ? 'tsi-splitByLabelCompact' : '';
|
|
30512
|
+
const shown = Utils.getAgVisible(self.chartComponentData.displayState, aggKey, splitBy) ? 'shown' : '';
|
|
30513
|
+
return `tsi-splitByLabel ${compact} ${shown}`;
|
|
30519
30514
|
})
|
|
30520
|
-
.classed("stickied", (splitBy, i) =>
|
|
30521
|
-
|
|
30522
|
-
return aggKey == self.chartComponentData.stickiedKey.aggregateKey && splitBy == self.chartComponentData.stickiedKey.splitBy;
|
|
30523
|
-
}
|
|
30524
|
-
});
|
|
30525
|
-
var colors = Utils.createSplitByColors(self.chartComponentData.displayState, aggKey, self.chartOptions.keepSplitByColor);
|
|
30515
|
+
.classed("stickied", (splitBy, i) => self.isStickied(aggKey, splitBy));
|
|
30516
|
+
// Use helper methods to render each split-by element
|
|
30526
30517
|
splitByLabelsEntered.each(function (splitBy, j) {
|
|
30527
|
-
|
|
30518
|
+
const selection = select(this);
|
|
30519
|
+
// Add color key (conditionally based on data type and legend state)
|
|
30528
30520
|
if (dataType === DataTypes.Numeric || noSplitBys || self.legendState === 'compact') {
|
|
30529
|
-
|
|
30530
|
-
|
|
30531
|
-
.append("div")
|
|
30532
|
-
.attr("class", 'tsi-colorKey')
|
|
30533
|
-
.merge(colorKey);
|
|
30534
|
-
if (dataType === DataTypes.Numeric) {
|
|
30535
|
-
colorKeyEntered.style('background-color', (d) => {
|
|
30536
|
-
return d;
|
|
30537
|
-
});
|
|
30538
|
-
}
|
|
30539
|
-
else {
|
|
30540
|
-
self.createNonNumericColorKey(dataType, colorKeyEntered, aggKey);
|
|
30541
|
-
}
|
|
30542
|
-
select(this).classed('tsi-nonCompactNonNumeric', (dataType === DataTypes.Categorical || dataType === DataTypes.Events) && this.legendState !== 'compact');
|
|
30543
|
-
colorKey.exit().remove();
|
|
30521
|
+
self.addColorKey(selection, aggKey, splitBy, dataType);
|
|
30522
|
+
selection.classed('tsi-nonCompactNonNumeric', (dataType === DataTypes.Categorical || dataType === DataTypes.Events) && self.legendState !== 'compact');
|
|
30544
30523
|
}
|
|
30545
30524
|
else {
|
|
30546
|
-
|
|
30547
|
-
}
|
|
30548
|
-
if (select(this).select('.tsi-eyeIcon').empty()) {
|
|
30549
|
-
select(this).append("button")
|
|
30550
|
-
.attr("class", "tsi-eyeIcon")
|
|
30551
|
-
.attr('aria-label', () => {
|
|
30552
|
-
let showOrHide = self.chartComponentData.displayState[aggKey].splitBys[splitBy].visible ? self.getString('hide series') : self.getString('show series');
|
|
30553
|
-
return `${showOrHide} ${splitBy} ${self.getString('in group')} ${self.chartComponentData.displayState[aggKey].name}`;
|
|
30554
|
-
})
|
|
30555
|
-
.attr('title', () => self.getString('Show/Hide values'))
|
|
30556
|
-
.on("click", function (event) {
|
|
30557
|
-
event.stopPropagation();
|
|
30558
|
-
self.toggleSplitByVisible(aggKey, splitBy);
|
|
30559
|
-
select(this)
|
|
30560
|
-
.classed("shown", Utils.getAgVisible(self.chartComponentData.displayState, aggKey, splitBy));
|
|
30561
|
-
self.drawChart();
|
|
30562
|
-
});
|
|
30563
|
-
}
|
|
30564
|
-
if (select(this).select('.tsi-seriesName').empty()) {
|
|
30565
|
-
let seriesName = select(this)
|
|
30566
|
-
.append('div')
|
|
30567
|
-
.attr('class', 'tsi-seriesName');
|
|
30568
|
-
Utils.appendFormattedElementsFromString(seriesName, noSplitBys ? (self.chartComponentData.displayState[aggKey].name) : splitBy);
|
|
30525
|
+
selection.selectAll('.tsi-colorKey').remove();
|
|
30569
30526
|
}
|
|
30527
|
+
// Add eye icon
|
|
30528
|
+
self.addEyeIcon(selection, aggKey, splitBy);
|
|
30529
|
+
// Add series name
|
|
30530
|
+
self.addSeriesName(selection, aggKey, splitBy);
|
|
30531
|
+
// Add series type selection for numeric data
|
|
30570
30532
|
if (dataType === DataTypes.Numeric) {
|
|
30571
|
-
|
|
30572
|
-
select(this).append("select")
|
|
30573
|
-
.attr('aria-label', `${self.getString("Series type selection for")} ${splitBy} ${self.getString('in group')} ${self.chartComponentData.displayState[aggKey].name}`)
|
|
30574
|
-
.attr('class', 'tsi-seriesTypeSelection')
|
|
30575
|
-
.on("change", function (data) {
|
|
30576
|
-
var seriesType = select(this).property("value");
|
|
30577
|
-
self.chartComponentData.displayState[aggKey].splitBys[splitBy].visibleType = seriesType;
|
|
30578
|
-
self.drawChart();
|
|
30579
|
-
})
|
|
30580
|
-
.on("click", (event) => {
|
|
30581
|
-
event.stopPropagation();
|
|
30582
|
-
});
|
|
30583
|
-
}
|
|
30584
|
-
select(this).select('.tsi-seriesTypeSelection')
|
|
30585
|
-
.each(function (d) {
|
|
30586
|
-
var typeLabels = select(this).selectAll('option')
|
|
30587
|
-
.data(data => self.chartComponentData.displayState[aggKey].splitBys[splitBy].types.map((type) => {
|
|
30588
|
-
return {
|
|
30589
|
-
type: type,
|
|
30590
|
-
aggKey: aggKey,
|
|
30591
|
-
splitBy: splitBy,
|
|
30592
|
-
visibleMeasure: Utils.getAgVisibleMeasure(self.chartComponentData.displayState, aggKey, splitBy)
|
|
30593
|
-
};
|
|
30594
|
-
}));
|
|
30595
|
-
typeLabels
|
|
30596
|
-
.enter()
|
|
30597
|
-
.append("option")
|
|
30598
|
-
.attr("class", "seriesTypeLabel")
|
|
30599
|
-
.merge(typeLabels)
|
|
30600
|
-
.property("selected", (data) => {
|
|
30601
|
-
return ((data.type == Utils.getAgVisibleMeasure(self.chartComponentData.displayState, data.aggKey, data.splitBy)) ?
|
|
30602
|
-
" selected" : "");
|
|
30603
|
-
})
|
|
30604
|
-
.text((data) => data.type);
|
|
30605
|
-
typeLabels.exit().remove();
|
|
30606
|
-
});
|
|
30533
|
+
self.addSeriesTypeSelection(selection, aggKey, splitBy);
|
|
30607
30534
|
}
|
|
30608
30535
|
else {
|
|
30609
|
-
|
|
30536
|
+
selection.selectAll('.tsi-seriesTypeSelection').remove();
|
|
30610
30537
|
}
|
|
30611
30538
|
});
|
|
30612
30539
|
splitByLabels.exit().remove();
|
|
30613
|
-
|
|
30540
|
+
// Show more button
|
|
30541
|
+
const shouldShowMore = self.chartComponentData.displayState[aggKey].shownSplitBys < splitByLabelData.length;
|
|
30614
30542
|
splitByContainerEntered.selectAll('.tsi-legendShowMore').remove();
|
|
30615
30543
|
if (this.legendState === 'shown' && shouldShowMore) {
|
|
30616
30544
|
splitByContainerEntered.append('button')
|
|
30617
30545
|
.text(this.getString('Show more'))
|
|
30618
30546
|
.attr('class', 'tsi-legendShowMore')
|
|
30619
|
-
.style('display',
|
|
30547
|
+
.style('display', 'block')
|
|
30620
30548
|
.on('click', showMoreSplitBys);
|
|
30621
30549
|
}
|
|
30550
|
+
// Scroll handler for infinite scrolling
|
|
30622
30551
|
splitByContainerEntered.on("scroll", function () {
|
|
30623
30552
|
if (self.chartOptions.legend === 'shown') {
|
|
30624
|
-
if (this.scrollTop + this.clientHeight +
|
|
30553
|
+
if (this.scrollTop + this.clientHeight + LEGEND_CONSTANTS.SCROLL_BUFFER > this.scrollHeight) {
|
|
30625
30554
|
showMoreSplitBys();
|
|
30626
30555
|
}
|
|
30627
30556
|
}
|
|
@@ -30646,10 +30575,125 @@
|
|
|
30646
30575
|
};
|
|
30647
30576
|
this.drawChart = drawChart;
|
|
30648
30577
|
this.legendWidth = legendWidth;
|
|
30649
|
-
this.legendElement = select(renderTarget)
|
|
30578
|
+
this.legendElement = select(renderTarget)
|
|
30579
|
+
.insert("div", ":first-child")
|
|
30650
30580
|
.attr("class", "tsi-legend")
|
|
30651
|
-
.style("left", "0px")
|
|
30652
|
-
|
|
30581
|
+
.style("left", "0px");
|
|
30582
|
+
// Note: width is set conditionally in draw() based on legendState
|
|
30583
|
+
// to allow CSS to control width in compact mode
|
|
30584
|
+
}
|
|
30585
|
+
getHeightPerSplitBy(aggKey) {
|
|
30586
|
+
const dataType = this.chartComponentData.displayState[aggKey].dataType;
|
|
30587
|
+
return dataType === DataTypes.Numeric
|
|
30588
|
+
? LEGEND_CONSTANTS.NUMERIC_SPLITBY_HEIGHT
|
|
30589
|
+
: LEGEND_CONSTANTS.NON_NUMERIC_SPLITBY_HEIGHT;
|
|
30590
|
+
}
|
|
30591
|
+
addColorKey(selection, aggKey, splitBy, dataType) {
|
|
30592
|
+
const colors = Utils.createSplitByColors(this.chartComponentData.displayState, aggKey, this.chartOptions.keepSplitByColor);
|
|
30593
|
+
const splitByKeys = Object.keys(this.chartComponentData.timeArrays[aggKey]);
|
|
30594
|
+
const splitByIndex = splitByKeys.indexOf(splitBy);
|
|
30595
|
+
const color = this.chartComponentData.isFromHeatmap
|
|
30596
|
+
? this.chartComponentData.displayState[aggKey].color
|
|
30597
|
+
: colors[splitByIndex];
|
|
30598
|
+
const colorKey = selection.selectAll('.tsi-colorKey').data([color]);
|
|
30599
|
+
const colorKeyEntered = colorKey.enter()
|
|
30600
|
+
.append('div')
|
|
30601
|
+
.attr('class', 'tsi-colorKey')
|
|
30602
|
+
.merge(colorKey);
|
|
30603
|
+
if (dataType === DataTypes.Numeric) {
|
|
30604
|
+
colorKeyEntered.style('background-color', d => d);
|
|
30605
|
+
}
|
|
30606
|
+
else {
|
|
30607
|
+
this.createNonNumericColorKey(dataType, colorKeyEntered, aggKey);
|
|
30608
|
+
}
|
|
30609
|
+
colorKey.exit().remove();
|
|
30610
|
+
}
|
|
30611
|
+
addEyeIcon(selection, aggKey, splitBy) {
|
|
30612
|
+
if (selection.select('.tsi-eyeIcon').empty()) {
|
|
30613
|
+
selection.append('button')
|
|
30614
|
+
.attr('class', 'tsi-eyeIcon')
|
|
30615
|
+
.attr('aria-label', () => {
|
|
30616
|
+
const showOrHide = this.chartComponentData.displayState[aggKey].splitBys[splitBy].visible
|
|
30617
|
+
? this.getString('hide series')
|
|
30618
|
+
: this.getString('show series');
|
|
30619
|
+
return `${showOrHide} ${splitBy} ${this.getString('in group')} ${this.chartComponentData.displayState[aggKey].name}`;
|
|
30620
|
+
})
|
|
30621
|
+
.attr('title', () => this.getString('Show/Hide values'))
|
|
30622
|
+
.on('click', (event) => {
|
|
30623
|
+
event.stopPropagation();
|
|
30624
|
+
this.toggleSplitByVisible(aggKey, splitBy);
|
|
30625
|
+
this.drawChart();
|
|
30626
|
+
});
|
|
30627
|
+
}
|
|
30628
|
+
selection.select('.tsi-eyeIcon')
|
|
30629
|
+
.classed('shown', Utils.getAgVisible(this.chartComponentData.displayState, aggKey, splitBy));
|
|
30630
|
+
}
|
|
30631
|
+
addSeriesName(selection, aggKey, splitBy) {
|
|
30632
|
+
if (selection.select('.tsi-seriesName').empty()) {
|
|
30633
|
+
const seriesName = selection.append('div')
|
|
30634
|
+
.attr('class', 'tsi-seriesName');
|
|
30635
|
+
const noSplitBys = Object.keys(this.chartComponentData.timeArrays[aggKey]).length === 1
|
|
30636
|
+
&& Object.keys(this.chartComponentData.timeArrays[aggKey])[0] === '';
|
|
30637
|
+
const displayText = noSplitBys
|
|
30638
|
+
? this.chartComponentData.displayState[aggKey].name
|
|
30639
|
+
: splitBy;
|
|
30640
|
+
Utils.appendFormattedElementsFromString(seriesName, displayText);
|
|
30641
|
+
}
|
|
30642
|
+
}
|
|
30643
|
+
addSeriesTypeSelection(selection, aggKey, splitBy) {
|
|
30644
|
+
if (selection.select('.tsi-seriesTypeSelection').empty()) {
|
|
30645
|
+
selection.append('select')
|
|
30646
|
+
.attr('aria-label', `${this.getString('Series type selection for')} ${splitBy} ${this.getString('in group')} ${this.chartComponentData.displayState[aggKey].name}`)
|
|
30647
|
+
.attr('class', 'tsi-seriesTypeSelection')
|
|
30648
|
+
.on('change', (event) => {
|
|
30649
|
+
const seriesType = select(event.target).property('value');
|
|
30650
|
+
this.chartComponentData.displayState[aggKey].splitBys[splitBy].visibleType = seriesType;
|
|
30651
|
+
this.drawChart();
|
|
30652
|
+
})
|
|
30653
|
+
.on('click', (event) => {
|
|
30654
|
+
event.stopPropagation();
|
|
30655
|
+
});
|
|
30656
|
+
}
|
|
30657
|
+
selection.select('.tsi-seriesTypeSelection')
|
|
30658
|
+
.each((d, i, nodes) => {
|
|
30659
|
+
const typeLabels = select(nodes[i])
|
|
30660
|
+
.selectAll('option')
|
|
30661
|
+
.data(this.chartComponentData.displayState[aggKey].splitBys[splitBy].types.map(type => ({
|
|
30662
|
+
type,
|
|
30663
|
+
aggKey,
|
|
30664
|
+
splitBy,
|
|
30665
|
+
visibleMeasure: Utils.getAgVisibleMeasure(this.chartComponentData.displayState, aggKey, splitBy)
|
|
30666
|
+
})));
|
|
30667
|
+
typeLabels.enter()
|
|
30668
|
+
.append('option')
|
|
30669
|
+
.attr('class', 'seriesTypeLabel')
|
|
30670
|
+
.merge(typeLabels)
|
|
30671
|
+
.property('selected', (data) => data.type === Utils.getAgVisibleMeasure(this.chartComponentData.displayState, data.aggKey, data.splitBy))
|
|
30672
|
+
.text((data) => data.type);
|
|
30673
|
+
typeLabels.exit().remove();
|
|
30674
|
+
});
|
|
30675
|
+
}
|
|
30676
|
+
handleSplitByClick(aggKey, splitBy) {
|
|
30677
|
+
if (this.legendState === 'compact') {
|
|
30678
|
+
this.toggleSplitByVisible(aggKey, splitBy);
|
|
30679
|
+
}
|
|
30680
|
+
else {
|
|
30681
|
+
this.toggleSticky(aggKey, splitBy);
|
|
30682
|
+
}
|
|
30683
|
+
this.drawChart();
|
|
30684
|
+
}
|
|
30685
|
+
handleSplitByMouseOver(aggKey, splitBy) {
|
|
30686
|
+
this.labelMouseover(aggKey, splitBy);
|
|
30687
|
+
}
|
|
30688
|
+
handleSplitByMouseOut(aggKey) {
|
|
30689
|
+
this.svgSelection.selectAll(".tsi-valueElement")
|
|
30690
|
+
.attr("stroke-opacity", 1)
|
|
30691
|
+
.attr("fill-opacity", 1);
|
|
30692
|
+
this.labelMouseout(this.svgSelection, aggKey);
|
|
30693
|
+
}
|
|
30694
|
+
isStickied(aggKey, splitBy) {
|
|
30695
|
+
const stickied = this.chartComponentData.stickiedKey;
|
|
30696
|
+
return stickied?.aggregateKey === aggKey && stickied?.splitBy === splitBy;
|
|
30653
30697
|
}
|
|
30654
30698
|
labelMouseoutWrapper(labelMouseout, svgSelection, event) {
|
|
30655
30699
|
return (svgSelection, aggKey) => {
|
|
@@ -30691,14 +30735,11 @@
|
|
|
30691
30735
|
return d == aggKey;
|
|
30692
30736
|
}).node();
|
|
30693
30737
|
var prospectiveScrollTop = Math.max((indexOfSplitBy - 1) * this.getHeightPerSplitBy(aggKey), 0);
|
|
30694
|
-
if (splitByNode.scrollTop < prospectiveScrollTop - (splitByNode.clientHeight -
|
|
30738
|
+
if (splitByNode.scrollTop < prospectiveScrollTop - (splitByNode.clientHeight - LEGEND_CONSTANTS.SCROLL_BUFFER) || splitByNode.scrollTop > prospectiveScrollTop) {
|
|
30695
30739
|
splitByNode.scrollTop = prospectiveScrollTop;
|
|
30696
30740
|
}
|
|
30697
30741
|
}
|
|
30698
30742
|
}
|
|
30699
|
-
getHeightPerSplitBy(aggKey) {
|
|
30700
|
-
return (this.chartComponentData.displayState[aggKey].dataType === DataTypes.Numeric ? NUMERICSPLITBYHEIGHT : NONNUMERICSPLITBYHEIGHT);
|
|
30701
|
-
}
|
|
30702
30743
|
createGradient(gradientKey, svg, values) {
|
|
30703
30744
|
let gradient = svg.append('defs').append('linearGradient')
|
|
30704
30745
|
.attr('id', gradientKey).attr('x1', '0%').attr('x2', '0%').attr('y1', '0%').attr('y2', '100%');
|
|
@@ -30717,10 +30758,6 @@
|
|
|
30717
30758
|
.attr("stop-opacity", 1);
|
|
30718
30759
|
});
|
|
30719
30760
|
}
|
|
30720
|
-
isNonNumeric(aggKey) {
|
|
30721
|
-
let dataType = this.chartComponentData.displayState[aggKey].dataType;
|
|
30722
|
-
return (dataType === DataTypes.Categorical || dataType === DataTypes.Events);
|
|
30723
|
-
}
|
|
30724
30761
|
createNonNumericColorKey(dataType, colorKey, aggKey) {
|
|
30725
30762
|
if (dataType === DataTypes.Categorical) {
|
|
30726
30763
|
this.createCategoricalColorKey(colorKey, aggKey);
|
|
@@ -30776,6 +30813,13 @@
|
|
|
30776
30813
|
rect.attr('fill', "url(#" + gradientKey + ")");
|
|
30777
30814
|
}
|
|
30778
30815
|
}
|
|
30816
|
+
handleShowMoreSplitBys(aggKey, splitByLabelData, aggSelection, dataType, noSplitBys) {
|
|
30817
|
+
const oldShownSplitBys = this.chartComponentData.displayState[aggKey].shownSplitBys;
|
|
30818
|
+
this.chartComponentData.displayState[aggKey].shownSplitBys = Math.min(oldShownSplitBys + LEGEND_CONSTANTS.BATCH_SIZE, splitByLabelData.length);
|
|
30819
|
+
if (oldShownSplitBys !== this.chartComponentData.displayState[aggKey].shownSplitBys) {
|
|
30820
|
+
this.renderSplitBys(aggKey, aggSelection, dataType, noSplitBys);
|
|
30821
|
+
}
|
|
30822
|
+
}
|
|
30779
30823
|
draw(legendState, chartComponentData, labelMouseover, svgSelection, options, labelMouseoutAction = null, stickySeriesAction = null, event) {
|
|
30780
30824
|
this.chartOptions.setOptions(options);
|
|
30781
30825
|
this.chartComponentData = chartComponentData;
|
|
@@ -30790,6 +30834,13 @@
|
|
|
30790
30834
|
legend.style('visibility', this.legendState != 'hidden')
|
|
30791
30835
|
.classed('compact', this.legendState == 'compact')
|
|
30792
30836
|
.classed('hidden', this.legendState == 'hidden');
|
|
30837
|
+
// Set width conditionally - let CSS handle compact mode width
|
|
30838
|
+
if (this.legendState !== 'compact') {
|
|
30839
|
+
legend.style('width', `${this.legendWidth}px`);
|
|
30840
|
+
}
|
|
30841
|
+
else {
|
|
30842
|
+
legend.style('width', null); // Remove inline width style in compact mode
|
|
30843
|
+
}
|
|
30793
30844
|
let seriesNames = Object.keys(this.chartComponentData.displayState);
|
|
30794
30845
|
var seriesLabels = legend.selectAll(".tsi-seriesLabel")
|
|
30795
30846
|
.data(seriesNames, d => d);
|
|
@@ -30800,7 +30851,7 @@
|
|
|
30800
30851
|
return "tsi-seriesLabel " + (this.chartComponentData.displayState[d]["visible"] ? " shown" : "");
|
|
30801
30852
|
})
|
|
30802
30853
|
.style("min-width", () => {
|
|
30803
|
-
return Math.min(
|
|
30854
|
+
return Math.min(LEGEND_CONSTANTS.MIN_SERIES_WIDTH, this.legendElement.node().clientWidth / seriesNames.length) + 'px';
|
|
30804
30855
|
})
|
|
30805
30856
|
.style("border-color", function (d, i) {
|
|
30806
30857
|
if (select(this).classed("shown"))
|
|
@@ -30808,9 +30859,8 @@
|
|
|
30808
30859
|
return "lightgray";
|
|
30809
30860
|
});
|
|
30810
30861
|
var self = this;
|
|
30811
|
-
const heightPerNameLabel = 25;
|
|
30812
30862
|
const usableLegendHeight = legend.node().clientHeight;
|
|
30813
|
-
var prospectiveAggregateHeight = Math.ceil(Math.max(
|
|
30863
|
+
var prospectiveAggregateHeight = Math.ceil(Math.max(LEGEND_CONSTANTS.MIN_AGGREGATE_HEIGHT, (usableLegendHeight / seriesLabelsEntered.size())));
|
|
30814
30864
|
var contentHeight = 0;
|
|
30815
30865
|
seriesLabelsEntered.each(function (aggKey, i) {
|
|
30816
30866
|
let heightPerSplitBy = self.getHeightPerSplitBy(aggKey);
|
|
@@ -30866,12 +30916,12 @@
|
|
|
30866
30916
|
seriesNameLabel.exit().remove();
|
|
30867
30917
|
var splitByContainerHeight;
|
|
30868
30918
|
if (splitByLabelData.length > (prospectiveAggregateHeight / heightPerSplitBy)) {
|
|
30869
|
-
splitByContainerHeight = prospectiveAggregateHeight -
|
|
30870
|
-
contentHeight += splitByContainerHeight +
|
|
30919
|
+
splitByContainerHeight = prospectiveAggregateHeight - LEGEND_CONSTANTS.NAME_LABEL_HEIGHT;
|
|
30920
|
+
contentHeight += splitByContainerHeight + LEGEND_CONSTANTS.NAME_LABEL_HEIGHT;
|
|
30871
30921
|
}
|
|
30872
30922
|
else if (splitByLabelData.length > 1 || (splitByLabelData.length === 1 && splitByLabelData[0] !== "")) {
|
|
30873
|
-
splitByContainerHeight = splitByLabelData.length * heightPerSplitBy +
|
|
30874
|
-
contentHeight += splitByContainerHeight +
|
|
30923
|
+
splitByContainerHeight = splitByLabelData.length * heightPerSplitBy + LEGEND_CONSTANTS.NAME_LABEL_HEIGHT;
|
|
30924
|
+
contentHeight += splitByContainerHeight + LEGEND_CONSTANTS.NAME_LABEL_HEIGHT;
|
|
30875
30925
|
}
|
|
30876
30926
|
else {
|
|
30877
30927
|
splitByContainerHeight = heightPerSplitBy;
|
|
@@ -30884,43 +30934,28 @@
|
|
|
30884
30934
|
select(this).style("height", "unset");
|
|
30885
30935
|
}
|
|
30886
30936
|
var splitByContainer = select(this).selectAll(".tsi-splitByContainer").data([aggKey]);
|
|
30887
|
-
|
|
30937
|
+
splitByContainer.enter().append("div")
|
|
30888
30938
|
.merge(splitByContainer)
|
|
30889
30939
|
.classed("tsi-splitByContainer", true);
|
|
30890
30940
|
let aggSelection = select(this);
|
|
30891
30941
|
self.renderSplitBys(aggKey, aggSelection, dataType, noSplitBys);
|
|
30892
|
-
|
|
30893
|
-
if (self.chartOptions.legend == "shown") {
|
|
30894
|
-
if (this.scrollTop + this.clientHeight + 40 > this.scrollHeight) {
|
|
30895
|
-
const oldShownSplitBys = self.chartComponentData.displayState[aggKey].shownSplitBys;
|
|
30896
|
-
self.chartComponentData.displayState[aggKey].shownSplitBys = Math.min(oldShownSplitBys + 20, splitByLabelData.length);
|
|
30897
|
-
if (oldShownSplitBys != self.chartComponentData.displayState[aggKey].shownSplitBys) {
|
|
30898
|
-
self.renderSplitBys(aggKey, aggSelection, dataType, noSplitBys);
|
|
30899
|
-
}
|
|
30900
|
-
}
|
|
30901
|
-
}
|
|
30902
|
-
});
|
|
30942
|
+
// Compact mode horizontal scroll handler
|
|
30903
30943
|
select(this).on('scroll', function () {
|
|
30904
30944
|
if (self.chartOptions.legend == "compact") {
|
|
30905
|
-
if (this.scrollLeft + this.clientWidth +
|
|
30906
|
-
|
|
30907
|
-
self.chartComponentData.displayState[aggKey].shownSplitBys = Math.min(oldShownSplitBys + 20, splitByLabelData.length);
|
|
30908
|
-
if (oldShownSplitBys != self.chartComponentData.displayState[aggKey].shownSplitBys) {
|
|
30909
|
-
this.renderSplitBys(dataType);
|
|
30910
|
-
}
|
|
30945
|
+
if (this.scrollLeft + this.clientWidth + LEGEND_CONSTANTS.SCROLL_BUFFER > this.scrollWidth) {
|
|
30946
|
+
self.handleShowMoreSplitBys(aggKey, splitByLabelData, aggSelection, dataType, noSplitBys);
|
|
30911
30947
|
}
|
|
30912
30948
|
}
|
|
30913
30949
|
});
|
|
30914
30950
|
splitByContainer.exit().remove();
|
|
30915
30951
|
});
|
|
30916
30952
|
if (this.chartOptions.legend == 'shown') {
|
|
30917
|
-
legend.node().clientHeight;
|
|
30918
30953
|
//minSplitBysForFlexGrow: the minimum number of split bys for flex-grow to be triggered
|
|
30919
30954
|
if (contentHeight < usableLegendHeight) {
|
|
30920
30955
|
this.legendElement.classed("tsi-flexLegend", true);
|
|
30921
30956
|
seriesLabelsEntered.each(function (d) {
|
|
30922
30957
|
let heightPerSplitBy = self.getHeightPerSplitBy(d);
|
|
30923
|
-
var minSplitByForFlexGrow = (prospectiveAggregateHeight -
|
|
30958
|
+
var minSplitByForFlexGrow = (prospectiveAggregateHeight - LEGEND_CONSTANTS.NAME_LABEL_HEIGHT) / heightPerSplitBy;
|
|
30924
30959
|
var splitBysCount = Object.keys(self.chartComponentData.displayState[String(select(this).data()[0])].splitBys).length;
|
|
30925
30960
|
if (splitBysCount > minSplitByForFlexGrow) {
|
|
30926
30961
|
select(this).style("flex-grow", 1);
|
|
@@ -30933,6 +30968,12 @@
|
|
|
30933
30968
|
}
|
|
30934
30969
|
seriesLabels.exit().remove();
|
|
30935
30970
|
}
|
|
30971
|
+
destroy() {
|
|
30972
|
+
this.legendElement.remove();
|
|
30973
|
+
// Note: Virtual list cleanup will be added when virtual scrolling is implemented
|
|
30974
|
+
// this.virtualLists.forEach(list => list.destroy());
|
|
30975
|
+
// this.virtualLists.clear();
|
|
30976
|
+
}
|
|
30936
30977
|
}
|
|
30937
30978
|
|
|
30938
30979
|
class ChartComponentData {
|
|
@@ -31579,9 +31620,9 @@
|
|
|
31579
31620
|
// maintainable code, while at the same time manually optimizing for tiny minified file size,
|
|
31580
31621
|
// browser compatibility without additional requirements
|
|
31581
31622
|
// and very few assumptions about the user's page layout.
|
|
31582
|
-
var global
|
|
31583
|
-
var ssr = global
|
|
31584
|
-
var document$1 = !ssr ? global
|
|
31623
|
+
var global = typeof window !== 'undefined' ? window : null;
|
|
31624
|
+
var ssr = global === null;
|
|
31625
|
+
var document$1 = !ssr ? global.document : undefined;
|
|
31585
31626
|
|
|
31586
31627
|
// Save a couple long function names that are used frequently.
|
|
31587
31628
|
// This optimization saves around 400 bytes.
|
|
@@ -32047,11 +32088,11 @@
|
|
|
32047
32088
|
self.dragging = false;
|
|
32048
32089
|
|
|
32049
32090
|
// Remove the stored event listeners. This is why we store them.
|
|
32050
|
-
global
|
|
32051
|
-
global
|
|
32052
|
-
global
|
|
32053
|
-
global
|
|
32054
|
-
global
|
|
32091
|
+
global[removeEventListener]('mouseup', self.stop);
|
|
32092
|
+
global[removeEventListener]('touchend', self.stop);
|
|
32093
|
+
global[removeEventListener]('touchcancel', self.stop);
|
|
32094
|
+
global[removeEventListener]('mousemove', self.move);
|
|
32095
|
+
global[removeEventListener]('touchmove', self.move);
|
|
32055
32096
|
|
|
32056
32097
|
// Clear bound function references
|
|
32057
32098
|
self.stop = null;
|
|
@@ -32108,11 +32149,11 @@
|
|
|
32108
32149
|
self.stop = stopDragging.bind(self);
|
|
32109
32150
|
|
|
32110
32151
|
// All the binding. `window` gets the stop events in case we drag out of the elements.
|
|
32111
|
-
global
|
|
32112
|
-
global
|
|
32113
|
-
global
|
|
32114
|
-
global
|
|
32115
|
-
global
|
|
32152
|
+
global[addEventListener]('mouseup', self.stop);
|
|
32153
|
+
global[addEventListener]('touchend', self.stop);
|
|
32154
|
+
global[addEventListener]('touchcancel', self.stop);
|
|
32155
|
+
global[addEventListener]('mousemove', self.move);
|
|
32156
|
+
global[addEventListener]('touchmove', self.move);
|
|
32116
32157
|
|
|
32117
32158
|
// Disable selection. Disable!
|
|
32118
32159
|
a[addEventListener]('selectstart', NOOP);
|
|
@@ -36749,6 +36790,8 @@
|
|
|
36749
36790
|
.append("text")
|
|
36750
36791
|
.attr("class", (d) => `tsi-swimLaneLabel-${lane} tsi-swimLaneLabel ${onClickPresentAndValid(d) ? 'tsi-boldOnHover' : ''}`)
|
|
36751
36792
|
.attr("role", "heading")
|
|
36793
|
+
.attr("aria-roledescription", this.getString("Swimlane label"))
|
|
36794
|
+
.attr("aria-label", d => d.label)
|
|
36752
36795
|
.attr("aria-level", "3")
|
|
36753
36796
|
.merge(label)
|
|
36754
36797
|
.style("text-anchor", "middle")
|
|
@@ -37290,1220 +37333,27 @@
|
|
|
37290
37333
|
var momentExports = requireMoment();
|
|
37291
37334
|
var moment = /*@__PURE__*/getDefaultExportFromCjs(momentExports);
|
|
37292
37335
|
|
|
37293
|
-
|
|
37294
|
-
|
|
37295
|
-
|
|
37296
|
-
|
|
37297
|
-
|
|
37298
|
-
|
|
37299
|
-
|
|
37300
|
-
|
|
37301
|
-
|
|
37302
|
-
|
|
37303
|
-
|
|
37304
|
-
|
|
37305
|
-
|
|
37306
|
-
|
|
37307
|
-
|
|
37308
|
-
|
|
37309
|
-
|
|
37310
|
-
|
|
37311
|
-
|
|
37312
|
-
|
|
37313
|
-
|
|
37314
|
-
try { moment = requireMoment(); } catch (e) { moment = (typeof window !== 'undefined' && window.moment) || undefined; }
|
|
37315
|
-
module.exports = factory(moment);
|
|
37316
|
-
}
|
|
37317
|
-
}(typeof self !== 'undefined' ? self :
|
|
37318
|
-
typeof window !== 'undefined' ? window :
|
|
37319
|
-
typeof commonjsGlobal !== 'undefined' ? commonjsGlobal :
|
|
37320
|
-
pikaday, function (moment) {
|
|
37321
|
-
|
|
37322
|
-
/**
|
|
37323
|
-
* feature detection and helper functions
|
|
37324
|
-
*/
|
|
37325
|
-
var hasMoment = typeof moment === 'function' || (moment && typeof moment.version === 'string'),
|
|
37326
|
-
|
|
37327
|
-
hasEventListeners = !!window.addEventListener,
|
|
37328
|
-
|
|
37329
|
-
document = window.document,
|
|
37330
|
-
|
|
37331
|
-
sto = window.setTimeout,
|
|
37332
|
-
|
|
37333
|
-
addEvent = function (el, e, callback, capture) {
|
|
37334
|
-
if (hasEventListeners) {
|
|
37335
|
-
el.addEventListener(e, callback, !!capture);
|
|
37336
|
-
} else {
|
|
37337
|
-
el.attachEvent('on' + e, callback);
|
|
37338
|
-
}
|
|
37339
|
-
},
|
|
37340
|
-
|
|
37341
|
-
removeEvent = function (el, e, callback, capture) {
|
|
37342
|
-
if (hasEventListeners) {
|
|
37343
|
-
el.removeEventListener(e, callback, !!capture);
|
|
37344
|
-
} else {
|
|
37345
|
-
el.detachEvent('on' + e, callback);
|
|
37346
|
-
}
|
|
37347
|
-
},
|
|
37348
|
-
|
|
37349
|
-
trim = function (str) {
|
|
37350
|
-
return str.trim ? str.trim() : str.replace(/^\s+|\s+$/g, '');
|
|
37351
|
-
},
|
|
37352
|
-
|
|
37353
|
-
hasClass = function (el, cn) {
|
|
37354
|
-
return (' ' + el.className + ' ').indexOf(' ' + cn + ' ') !== -1;
|
|
37355
|
-
},
|
|
37356
|
-
|
|
37357
|
-
addClass = function (el, cn) {
|
|
37358
|
-
if (!hasClass(el, cn)) {
|
|
37359
|
-
el.className = (el.className === '') ? cn : el.className + ' ' + cn;
|
|
37360
|
-
}
|
|
37361
|
-
},
|
|
37362
|
-
|
|
37363
|
-
removeClass = function (el, cn) {
|
|
37364
|
-
el.className = trim((' ' + el.className + ' ').replace(' ' + cn + ' ', ' '));
|
|
37365
|
-
},
|
|
37366
|
-
|
|
37367
|
-
isArray = function (obj) {
|
|
37368
|
-
return (/Array/).test(Object.prototype.toString.call(obj));
|
|
37369
|
-
},
|
|
37370
|
-
|
|
37371
|
-
isDate = function (obj) {
|
|
37372
|
-
return (/Date/).test(Object.prototype.toString.call(obj)) && !isNaN(obj.getTime());
|
|
37373
|
-
},
|
|
37374
|
-
|
|
37375
|
-
isWeekend = function (date) {
|
|
37376
|
-
var day = date.getDay();
|
|
37377
|
-
return day === 0 || day === 6;
|
|
37378
|
-
},
|
|
37379
|
-
|
|
37380
|
-
isLeapYear = function (year) {
|
|
37381
|
-
// solution by Matti Virkkunen: http://stackoverflow.com/a/4881951
|
|
37382
|
-
return year % 4 === 0 && year % 100 !== 0 || year % 400 === 0;
|
|
37383
|
-
},
|
|
37384
|
-
|
|
37385
|
-
getDaysInMonth = function (year, month) {
|
|
37386
|
-
return [31, isLeapYear(year) ? 29 : 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31][month];
|
|
37387
|
-
},
|
|
37388
|
-
|
|
37389
|
-
setToStartOfDay = function (date) {
|
|
37390
|
-
if (isDate(date)) date.setHours(0, 0, 0, 0);
|
|
37391
|
-
},
|
|
37392
|
-
|
|
37393
|
-
compareDates = function (a, b) {
|
|
37394
|
-
// weak date comparison (use setToStartOfDay(date) to ensure correct result)
|
|
37395
|
-
return a.getTime() === b.getTime();
|
|
37396
|
-
},
|
|
37397
|
-
|
|
37398
|
-
extend = function (to, from, overwrite) {
|
|
37399
|
-
var prop, hasProp;
|
|
37400
|
-
for (prop in from) {
|
|
37401
|
-
hasProp = to[prop] !== undefined;
|
|
37402
|
-
if (hasProp && typeof from[prop] === 'object' && from[prop] !== null && from[prop].nodeName === undefined) {
|
|
37403
|
-
if (isDate(from[prop])) {
|
|
37404
|
-
if (overwrite) {
|
|
37405
|
-
to[prop] = new Date(from[prop].getTime());
|
|
37406
|
-
}
|
|
37407
|
-
}
|
|
37408
|
-
else if (isArray(from[prop])) {
|
|
37409
|
-
if (overwrite) {
|
|
37410
|
-
to[prop] = from[prop].slice(0);
|
|
37411
|
-
}
|
|
37412
|
-
} else {
|
|
37413
|
-
to[prop] = extend({}, from[prop], overwrite);
|
|
37414
|
-
}
|
|
37415
|
-
} else if (overwrite || !hasProp) {
|
|
37416
|
-
to[prop] = from[prop];
|
|
37417
|
-
}
|
|
37418
|
-
}
|
|
37419
|
-
return to;
|
|
37420
|
-
},
|
|
37421
|
-
|
|
37422
|
-
fireEvent = function (el, eventName, data) {
|
|
37423
|
-
var ev;
|
|
37424
|
-
|
|
37425
|
-
if (document.createEvent) {
|
|
37426
|
-
ev = document.createEvent('HTMLEvents');
|
|
37427
|
-
ev.initEvent(eventName, true, false);
|
|
37428
|
-
ev = extend(ev, data);
|
|
37429
|
-
el.dispatchEvent(ev);
|
|
37430
|
-
} else if (document.createEventObject) {
|
|
37431
|
-
ev = document.createEventObject();
|
|
37432
|
-
ev = extend(ev, data);
|
|
37433
|
-
el.fireEvent('on' + eventName, ev);
|
|
37434
|
-
}
|
|
37435
|
-
},
|
|
37436
|
-
|
|
37437
|
-
adjustCalendar = function (calendar) {
|
|
37438
|
-
if (calendar.month < 0) {
|
|
37439
|
-
calendar.year -= Math.ceil(Math.abs(calendar.month) / 12);
|
|
37440
|
-
calendar.month += 12;
|
|
37441
|
-
}
|
|
37442
|
-
if (calendar.month > 11) {
|
|
37443
|
-
calendar.year += Math.floor(Math.abs(calendar.month) / 12);
|
|
37444
|
-
calendar.month -= 12;
|
|
37445
|
-
}
|
|
37446
|
-
return calendar;
|
|
37447
|
-
},
|
|
37448
|
-
|
|
37449
|
-
/**
|
|
37450
|
-
* defaults and localisation
|
|
37451
|
-
*/
|
|
37452
|
-
defaults = {
|
|
37453
|
-
|
|
37454
|
-
// bind the picker to a form field
|
|
37455
|
-
field: null,
|
|
37456
|
-
|
|
37457
|
-
// automatically show/hide the picker on `field` focus (default `true` if `field` is set)
|
|
37458
|
-
bound: undefined,
|
|
37459
|
-
|
|
37460
|
-
// position of the datepicker, relative to the field (default to bottom & left)
|
|
37461
|
-
// ('bottom' & 'left' keywords are not used, 'top' & 'right' are modifier on the bottom/left position)
|
|
37462
|
-
position: 'bottom left',
|
|
37463
|
-
|
|
37464
|
-
// automatically fit in the viewport even if it means repositioning from the position option
|
|
37465
|
-
reposition: true,
|
|
37466
|
-
|
|
37467
|
-
// the default output format for `.toString()` and `field` value
|
|
37468
|
-
format: 'YYYY-MM-DD',
|
|
37469
|
-
|
|
37470
|
-
// the toString function which gets passed a current date object and format
|
|
37471
|
-
// and returns a string
|
|
37472
|
-
toString: null,
|
|
37473
|
-
|
|
37474
|
-
// used to create date object from current input string
|
|
37475
|
-
parse: null,
|
|
37476
|
-
|
|
37477
|
-
// the initial date to view when first opened
|
|
37478
|
-
defaultDate: null,
|
|
37479
|
-
|
|
37480
|
-
// make the `defaultDate` the initial selected value
|
|
37481
|
-
setDefaultDate: false,
|
|
37482
|
-
|
|
37483
|
-
// first day of week (0: Sunday, 1: Monday etc)
|
|
37484
|
-
firstDay: 0,
|
|
37485
|
-
|
|
37486
|
-
// the default flag for moment's strict date parsing
|
|
37487
|
-
formatStrict: false,
|
|
37488
|
-
|
|
37489
|
-
// the minimum/earliest date that can be selected
|
|
37490
|
-
minDate: null,
|
|
37491
|
-
// the maximum/latest date that can be selected
|
|
37492
|
-
maxDate: null,
|
|
37493
|
-
|
|
37494
|
-
// number of years either side, or array of upper/lower range
|
|
37495
|
-
yearRange: 10,
|
|
37496
|
-
|
|
37497
|
-
// show week numbers at head of row
|
|
37498
|
-
showWeekNumber: false,
|
|
37499
|
-
|
|
37500
|
-
// Week picker mode
|
|
37501
|
-
pickWholeWeek: false,
|
|
37502
|
-
|
|
37503
|
-
// used internally (don't config outside)
|
|
37504
|
-
minYear: 0,
|
|
37505
|
-
maxYear: 9999,
|
|
37506
|
-
minMonth: undefined,
|
|
37507
|
-
maxMonth: undefined,
|
|
37508
|
-
|
|
37509
|
-
startRange: null,
|
|
37510
|
-
endRange: null,
|
|
37511
|
-
|
|
37512
|
-
isRTL: false,
|
|
37513
|
-
|
|
37514
|
-
// Additional text to append to the year in the calendar title
|
|
37515
|
-
yearSuffix: '',
|
|
37516
|
-
|
|
37517
|
-
// Render the month after year in the calendar title
|
|
37518
|
-
showMonthAfterYear: false,
|
|
37519
|
-
|
|
37520
|
-
// Render days of the calendar grid that fall in the next or previous month
|
|
37521
|
-
showDaysInNextAndPreviousMonths: false,
|
|
37522
|
-
|
|
37523
|
-
// Allows user to select days that fall in the next or previous month
|
|
37524
|
-
enableSelectionDaysInNextAndPreviousMonths: false,
|
|
37525
|
-
|
|
37526
|
-
// how many months are visible
|
|
37527
|
-
numberOfMonths: 1,
|
|
37528
|
-
|
|
37529
|
-
// when numberOfMonths is used, this will help you to choose where the main calendar will be (default `left`, can be set to `right`)
|
|
37530
|
-
// only used for the first display or when a selected date is not visible
|
|
37531
|
-
mainCalendar: 'left',
|
|
37532
|
-
|
|
37533
|
-
// Specify a DOM element to render the calendar in
|
|
37534
|
-
container: undefined,
|
|
37535
|
-
|
|
37536
|
-
// Blur field when date is selected
|
|
37537
|
-
blurFieldOnSelect: true,
|
|
37538
|
-
|
|
37539
|
-
// internationalization
|
|
37540
|
-
i18n: {
|
|
37541
|
-
previousMonth: 'Previous Month',
|
|
37542
|
-
nextMonth: 'Next Month',
|
|
37543
|
-
months: ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'],
|
|
37544
|
-
weekdays: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'],
|
|
37545
|
-
weekdaysShort: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat']
|
|
37546
|
-
},
|
|
37547
|
-
|
|
37548
|
-
// Theme Classname
|
|
37549
|
-
theme: null,
|
|
37550
|
-
|
|
37551
|
-
// events array
|
|
37552
|
-
events: [],
|
|
37553
|
-
|
|
37554
|
-
// callback function
|
|
37555
|
-
onSelect: null,
|
|
37556
|
-
onOpen: null,
|
|
37557
|
-
onClose: null,
|
|
37558
|
-
onDraw: null,
|
|
37559
|
-
|
|
37560
|
-
// Enable keyboard input
|
|
37561
|
-
keyboardInput: true
|
|
37562
|
-
},
|
|
37563
|
-
|
|
37564
|
-
|
|
37565
|
-
/**
|
|
37566
|
-
* templating functions to abstract HTML rendering
|
|
37567
|
-
*/
|
|
37568
|
-
renderDayName = function (opts, day, abbr) {
|
|
37569
|
-
day += opts.firstDay;
|
|
37570
|
-
while (day >= 7) {
|
|
37571
|
-
day -= 7;
|
|
37572
|
-
}
|
|
37573
|
-
return abbr ? opts.i18n.weekdaysShort[day] : opts.i18n.weekdays[day];
|
|
37574
|
-
},
|
|
37575
|
-
|
|
37576
|
-
renderDay = function (opts) {
|
|
37577
|
-
var arr = [];
|
|
37578
|
-
var ariaSelected = 'false';
|
|
37579
|
-
if (opts.isEmpty) {
|
|
37580
|
-
if (opts.showDaysInNextAndPreviousMonths) {
|
|
37581
|
-
arr.push('is-outside-current-month');
|
|
37582
|
-
|
|
37583
|
-
if (!opts.enableSelectionDaysInNextAndPreviousMonths) {
|
|
37584
|
-
arr.push('is-selection-disabled');
|
|
37585
|
-
}
|
|
37586
|
-
|
|
37587
|
-
} else {
|
|
37588
|
-
return '<td class="is-empty"></td>';
|
|
37589
|
-
}
|
|
37590
|
-
}
|
|
37591
|
-
if (opts.isDisabled) {
|
|
37592
|
-
arr.push('is-disabled');
|
|
37593
|
-
}
|
|
37594
|
-
if (opts.isToday) {
|
|
37595
|
-
arr.push('is-today');
|
|
37596
|
-
}
|
|
37597
|
-
if (opts.isSelected) {
|
|
37598
|
-
arr.push('is-selected');
|
|
37599
|
-
ariaSelected = 'true';
|
|
37600
|
-
}
|
|
37601
|
-
if (opts.hasEvent) {
|
|
37602
|
-
arr.push('has-event');
|
|
37603
|
-
}
|
|
37604
|
-
if (opts.isInRange) {
|
|
37605
|
-
arr.push('is-inrange');
|
|
37606
|
-
}
|
|
37607
|
-
if (opts.isStartRange) {
|
|
37608
|
-
arr.push('is-startrange');
|
|
37609
|
-
}
|
|
37610
|
-
if (opts.isEndRange) {
|
|
37611
|
-
arr.push('is-endrange');
|
|
37612
|
-
}
|
|
37613
|
-
return '<td data-day="' + opts.day + '" class="' + arr.join(' ') + '" aria-selected="' + ariaSelected + '">' +
|
|
37614
|
-
'<button tabIndex="-1" class="pika-button pika-day" type="button" ' +
|
|
37615
|
-
'data-pika-year="' + opts.year + '" data-pika-month="' + opts.month + '" data-pika-day="' + opts.day + '">' +
|
|
37616
|
-
opts.day +
|
|
37617
|
-
'</button>' +
|
|
37618
|
-
'</td>';
|
|
37619
|
-
},
|
|
37620
|
-
|
|
37621
|
-
renderWeek = function (d, m, y) {
|
|
37622
|
-
// Lifted from http://javascript.about.com/library/blweekyear.htm, lightly modified.
|
|
37623
|
-
var onejan = new Date(y, 0, 1),
|
|
37624
|
-
weekNum = Math.ceil((((new Date(y, m, d) - onejan) / 86400000) + onejan.getDay() + 1) / 7);
|
|
37625
|
-
return '<td class="pika-week">' + weekNum + '</td>';
|
|
37626
|
-
},
|
|
37627
|
-
|
|
37628
|
-
renderRow = function (days, isRTL, pickWholeWeek, isRowSelected) {
|
|
37629
|
-
return '<tr class="pika-row' + (pickWholeWeek ? ' pick-whole-week' : '') + (isRowSelected ? ' is-selected' : '') + '">' + (isRTL ? days.reverse() : days).join('') + '</tr>';
|
|
37630
|
-
},
|
|
37631
|
-
|
|
37632
|
-
renderBody = function (rows) {
|
|
37633
|
-
return '<tbody>' + rows.join('') + '</tbody>';
|
|
37634
|
-
},
|
|
37635
|
-
|
|
37636
|
-
renderHead = function (opts) {
|
|
37637
|
-
var i, arr = [];
|
|
37638
|
-
if (opts.showWeekNumber) {
|
|
37639
|
-
arr.push('<th></th>');
|
|
37640
|
-
}
|
|
37641
|
-
for (i = 0; i < 7; i++) {
|
|
37642
|
-
arr.push('<th scope="col"><abbr title="' + renderDayName(opts, i) + '">' + renderDayName(opts, i, true) + '</abbr></th>');
|
|
37643
|
-
}
|
|
37644
|
-
return '<thead><tr>' + (opts.isRTL ? arr.reverse() : arr).join('') + '</tr></thead>';
|
|
37645
|
-
},
|
|
37646
|
-
|
|
37647
|
-
renderTitle = function (instance, c, year, month, refYear, randId) {
|
|
37648
|
-
var i, j, arr,
|
|
37649
|
-
opts = instance._o,
|
|
37650
|
-
isMinYear = year === opts.minYear,
|
|
37651
|
-
isMaxYear = year === opts.maxYear,
|
|
37652
|
-
html = '<div id="' + randId + '" class="pika-title">',
|
|
37653
|
-
monthHtml,
|
|
37654
|
-
yearHtml,
|
|
37655
|
-
prev = true,
|
|
37656
|
-
next = true;
|
|
37657
|
-
|
|
37658
|
-
for (arr = [], i = 0; i < 12; i++) {
|
|
37659
|
-
arr.push('<option value="' + (year === refYear ? i - c : 12 + i - c) + '"' +
|
|
37660
|
-
(i === month ? ' selected="selected"' : '') +
|
|
37661
|
-
((isMinYear && i < opts.minMonth) || (isMaxYear && i > opts.maxMonth) ? 'disabled="disabled"' : '') + '>' +
|
|
37662
|
-
opts.i18n.months[i] + '</option>');
|
|
37663
|
-
}
|
|
37664
|
-
|
|
37665
|
-
monthHtml = '<div class="pika-label">' + opts.i18n.months[month] + '<select aria-label="select month" class="pika-select pika-select-month" tabindex="-1">' + arr.join('') + '</select></div>';
|
|
37666
|
-
|
|
37667
|
-
if (isArray(opts.yearRange)) {
|
|
37668
|
-
i = opts.yearRange[0];
|
|
37669
|
-
j = opts.yearRange[1] + 1;
|
|
37670
|
-
} else {
|
|
37671
|
-
i = year - opts.yearRange;
|
|
37672
|
-
j = 1 + year + opts.yearRange;
|
|
37673
|
-
}
|
|
37674
|
-
|
|
37675
|
-
for (arr = []; i < j && i <= opts.maxYear; i++) {
|
|
37676
|
-
if (i >= opts.minYear) {
|
|
37677
|
-
arr.push('<option value="' + i + '"' + (i === year ? ' selected="selected"' : '') + '>' + (i) + '</option>');
|
|
37678
|
-
}
|
|
37679
|
-
}
|
|
37680
|
-
yearHtml = '<div class="pika-label">' + year + opts.yearSuffix + '<select aria-label="select year" class="pika-select pika-select-year" tabindex="-1">' + arr.join('') + '</select></div>';
|
|
37681
|
-
|
|
37682
|
-
if (opts.showMonthAfterYear) {
|
|
37683
|
-
html += yearHtml + monthHtml;
|
|
37684
|
-
} else {
|
|
37685
|
-
html += monthHtml + yearHtml;
|
|
37686
|
-
}
|
|
37687
|
-
|
|
37688
|
-
if (isMinYear && (month === 0 || opts.minMonth >= month)) {
|
|
37689
|
-
prev = false;
|
|
37690
|
-
}
|
|
37691
|
-
|
|
37692
|
-
if (isMaxYear && (month === 11 || opts.maxMonth <= month)) {
|
|
37693
|
-
next = false;
|
|
37694
|
-
}
|
|
37695
|
-
|
|
37696
|
-
if (c === 0) {
|
|
37697
|
-
html += '<button tabIndex="-1" class="pika-prev' + (prev ? '' : ' is-disabled') + '" type="button">' + opts.i18n.previousMonth + '</button>';
|
|
37698
|
-
}
|
|
37699
|
-
if (c === (instance._o.numberOfMonths - 1)) {
|
|
37700
|
-
html += '<button tabIndex="-1" class="pika-next' + (next ? '' : ' is-disabled') + '" type="button">' + opts.i18n.nextMonth + '</button>';
|
|
37701
|
-
}
|
|
37702
|
-
|
|
37703
|
-
return html += '</div>';
|
|
37704
|
-
},
|
|
37705
|
-
|
|
37706
|
-
renderTable = function (opts, data, randId) {
|
|
37707
|
-
return '<table cellpadding="0" cellspacing="0" class="pika-table" role="grid" aria-labelledby="' + randId + '">' + renderHead(opts) + renderBody(data) + '</table>';
|
|
37708
|
-
},
|
|
37709
|
-
|
|
37710
|
-
|
|
37711
|
-
/**
|
|
37712
|
-
* Pikaday constructor
|
|
37713
|
-
*/
|
|
37714
|
-
Pikaday = function (options) {
|
|
37715
|
-
var self = this,
|
|
37716
|
-
opts = self.config(options);
|
|
37717
|
-
|
|
37718
|
-
self._onMouseDown = function (e) {
|
|
37719
|
-
if (!self._v) {
|
|
37720
|
-
return;
|
|
37721
|
-
}
|
|
37722
|
-
e = e || window.event;
|
|
37723
|
-
var target = e.target || e.srcElement;
|
|
37724
|
-
if (!target) {
|
|
37725
|
-
return;
|
|
37726
|
-
}
|
|
37727
|
-
|
|
37728
|
-
if (!hasClass(target, 'is-disabled')) {
|
|
37729
|
-
if (hasClass(target, 'pika-button') && !hasClass(target, 'is-empty') && !hasClass(target.parentNode, 'is-disabled')) {
|
|
37730
|
-
self.setDate(new Date(target.getAttribute('data-pika-year'), target.getAttribute('data-pika-month'), target.getAttribute('data-pika-day')));
|
|
37731
|
-
if (opts.bound) {
|
|
37732
|
-
sto(function () {
|
|
37733
|
-
self.hide();
|
|
37734
|
-
if (opts.blurFieldOnSelect && opts.field) {
|
|
37735
|
-
opts.field.blur();
|
|
37736
|
-
}
|
|
37737
|
-
}, 100);
|
|
37738
|
-
}
|
|
37739
|
-
}
|
|
37740
|
-
else if (hasClass(target, 'pika-prev')) {
|
|
37741
|
-
self.prevMonth();
|
|
37742
|
-
}
|
|
37743
|
-
else if (hasClass(target, 'pika-next')) {
|
|
37744
|
-
self.nextMonth();
|
|
37745
|
-
}
|
|
37746
|
-
}
|
|
37747
|
-
if (!hasClass(target, 'pika-select')) {
|
|
37748
|
-
// if this is touch event prevent mouse events emulation
|
|
37749
|
-
if (e.preventDefault) {
|
|
37750
|
-
e.preventDefault();
|
|
37751
|
-
} else {
|
|
37752
|
-
e.returnValue = false;
|
|
37753
|
-
return false;
|
|
37754
|
-
}
|
|
37755
|
-
} else {
|
|
37756
|
-
self._c = true;
|
|
37757
|
-
}
|
|
37758
|
-
};
|
|
37759
|
-
|
|
37760
|
-
self._onChange = function (e) {
|
|
37761
|
-
e = e || window.event;
|
|
37762
|
-
var target = e.target || e.srcElement;
|
|
37763
|
-
if (!target) {
|
|
37764
|
-
return;
|
|
37765
|
-
}
|
|
37766
|
-
if (hasClass(target, 'pika-select-month')) {
|
|
37767
|
-
self.gotoMonth(target.value);
|
|
37768
|
-
}
|
|
37769
|
-
else if (hasClass(target, 'pika-select-year')) {
|
|
37770
|
-
self.gotoYear(target.value);
|
|
37771
|
-
}
|
|
37772
|
-
};
|
|
37773
|
-
|
|
37774
|
-
self._onKeyChange = function (e) {
|
|
37775
|
-
e = e || window.event;
|
|
37776
|
-
// ignore if event comes from input box
|
|
37777
|
-
if (self.isVisible() && e.target && e.target.type !== 'text') {
|
|
37778
|
-
|
|
37779
|
-
switch (e.keyCode) {
|
|
37780
|
-
case 13:
|
|
37781
|
-
case 27:
|
|
37782
|
-
if (opts.field) {
|
|
37783
|
-
opts.field.blur();
|
|
37784
|
-
}
|
|
37785
|
-
break;
|
|
37786
|
-
case 37:
|
|
37787
|
-
e.preventDefault();
|
|
37788
|
-
self.adjustDate('subtract', 1);
|
|
37789
|
-
break;
|
|
37790
|
-
case 38:
|
|
37791
|
-
self.adjustDate('subtract', 7);
|
|
37792
|
-
break;
|
|
37793
|
-
case 39:
|
|
37794
|
-
self.adjustDate('add', 1);
|
|
37795
|
-
break;
|
|
37796
|
-
case 40:
|
|
37797
|
-
self.adjustDate('add', 7);
|
|
37798
|
-
break;
|
|
37799
|
-
}
|
|
37800
|
-
}
|
|
37801
|
-
};
|
|
37802
|
-
|
|
37803
|
-
self._onInputChange = function (e) {
|
|
37804
|
-
var date;
|
|
37805
|
-
|
|
37806
|
-
if (e.firedBy === self) {
|
|
37807
|
-
return;
|
|
37808
|
-
}
|
|
37809
|
-
if (opts.parse) {
|
|
37810
|
-
date = opts.parse(opts.field.value, opts.format);
|
|
37811
|
-
} else if (hasMoment) {
|
|
37812
|
-
date = moment(opts.field.value, opts.format, opts.formatStrict);
|
|
37813
|
-
date = (date && date.isValid()) ? date.toDate() : null;
|
|
37814
|
-
}
|
|
37815
|
-
else {
|
|
37816
|
-
date = new Date(Date.parse(opts.field.value));
|
|
37817
|
-
}
|
|
37818
|
-
// if (isDate(date)) {
|
|
37819
|
-
// self.setDate(date);
|
|
37820
|
-
// }
|
|
37821
|
-
// if (!self._v) {
|
|
37822
|
-
// self.show();
|
|
37823
|
-
// }
|
|
37824
|
-
};
|
|
37825
|
-
|
|
37826
|
-
self._onInputFocus = function () {
|
|
37827
|
-
self.show();
|
|
37828
|
-
};
|
|
37829
|
-
|
|
37830
|
-
self._onInputClick = function () {
|
|
37831
|
-
self.show();
|
|
37832
|
-
};
|
|
37833
|
-
|
|
37834
|
-
self._onInputBlur = function () {
|
|
37835
|
-
// IE allows pika div to gain focus; catch blur the input field
|
|
37836
|
-
var pEl = document.activeElement;
|
|
37837
|
-
do {
|
|
37838
|
-
if (hasClass(pEl, 'pika-single')) {
|
|
37839
|
-
return;
|
|
37840
|
-
}
|
|
37841
|
-
}
|
|
37842
|
-
while ((pEl = pEl.parentNode));
|
|
37843
|
-
|
|
37844
|
-
if (!self._c) {
|
|
37845
|
-
self._b = sto(function () {
|
|
37846
|
-
self.hide();
|
|
37847
|
-
}, 50);
|
|
37848
|
-
}
|
|
37849
|
-
self._c = false;
|
|
37850
|
-
};
|
|
37851
|
-
|
|
37852
|
-
self._onClick = function (e) {
|
|
37853
|
-
e = e || window.event;
|
|
37854
|
-
var target = e.target || e.srcElement,
|
|
37855
|
-
pEl = target;
|
|
37856
|
-
if (!target) {
|
|
37857
|
-
return;
|
|
37858
|
-
}
|
|
37859
|
-
if (!hasEventListeners && hasClass(target, 'pika-select')) {
|
|
37860
|
-
if (!target.onchange) {
|
|
37861
|
-
target.setAttribute('onchange', 'return;');
|
|
37862
|
-
addEvent(target, 'change', self._onChange);
|
|
37863
|
-
}
|
|
37864
|
-
}
|
|
37865
|
-
do {
|
|
37866
|
-
if (hasClass(pEl, 'pika-single') || pEl === opts.trigger) {
|
|
37867
|
-
return;
|
|
37868
|
-
}
|
|
37869
|
-
}
|
|
37870
|
-
while ((pEl = pEl.parentNode));
|
|
37871
|
-
if (self._v && target !== opts.trigger && pEl !== opts.trigger) {
|
|
37872
|
-
self.hide();
|
|
37873
|
-
}
|
|
37874
|
-
};
|
|
37875
|
-
|
|
37876
|
-
self.el = document.createElement('div');
|
|
37877
|
-
self.el.className = 'pika-single' + (opts.isRTL ? ' is-rtl' : '') + (opts.theme ? ' ' + opts.theme : '');
|
|
37878
|
-
|
|
37879
|
-
addEvent(self.el, 'mousedown', self._onMouseDown, true);
|
|
37880
|
-
addEvent(self.el, 'touchend', self._onMouseDown, true);
|
|
37881
|
-
addEvent(self.el, 'change', self._onChange);
|
|
37882
|
-
|
|
37883
|
-
if (opts.keyboardInput) {
|
|
37884
|
-
addEvent(document, 'keydown', self._onKeyChange);
|
|
37885
|
-
}
|
|
37886
|
-
|
|
37887
|
-
if (opts.field) {
|
|
37888
|
-
if (opts.container) {
|
|
37889
|
-
opts.container.appendChild(self.el);
|
|
37890
|
-
} else if (opts.bound) {
|
|
37891
|
-
document.body.appendChild(self.el);
|
|
37892
|
-
} else {
|
|
37893
|
-
opts.field.parentNode.insertBefore(self.el, opts.field.nextSibling);
|
|
37894
|
-
}
|
|
37895
|
-
addEvent(opts.field, 'change', self._onInputChange);
|
|
37896
|
-
|
|
37897
|
-
if (!opts.defaultDate) {
|
|
37898
|
-
if (hasMoment && opts.field.value) {
|
|
37899
|
-
opts.defaultDate = moment(opts.field.value, opts.format).toDate();
|
|
37900
|
-
} else {
|
|
37901
|
-
opts.defaultDate = new Date(Date.parse(opts.field.value));
|
|
37902
|
-
}
|
|
37903
|
-
opts.setDefaultDate = true;
|
|
37904
|
-
}
|
|
37905
|
-
}
|
|
37906
|
-
|
|
37907
|
-
var defDate = opts.defaultDate;
|
|
37908
|
-
|
|
37909
|
-
if (isDate(defDate)) {
|
|
37910
|
-
if (opts.setDefaultDate) {
|
|
37911
|
-
self.setDate(defDate, true);
|
|
37912
|
-
} else {
|
|
37913
|
-
self.gotoDate(defDate);
|
|
37914
|
-
}
|
|
37915
|
-
} else {
|
|
37916
|
-
self.gotoDate(new Date());
|
|
37917
|
-
}
|
|
37918
|
-
|
|
37919
|
-
if (opts.bound) {
|
|
37920
|
-
this.hide();
|
|
37921
|
-
self.el.className += ' is-bound';
|
|
37922
|
-
addEvent(opts.trigger, 'click', self._onInputClick);
|
|
37923
|
-
addEvent(opts.trigger, 'focus', self._onInputFocus);
|
|
37924
|
-
addEvent(opts.trigger, 'blur', self._onInputBlur);
|
|
37925
|
-
} else {
|
|
37926
|
-
this.show();
|
|
37927
|
-
}
|
|
37928
|
-
};
|
|
37929
|
-
|
|
37930
|
-
|
|
37931
|
-
/**
|
|
37932
|
-
* public Pikaday API
|
|
37933
|
-
*/
|
|
37934
|
-
Pikaday.prototype = {
|
|
37935
|
-
|
|
37936
|
-
|
|
37937
|
-
/**
|
|
37938
|
-
* configure functionality
|
|
37939
|
-
*/
|
|
37940
|
-
config: function (options) {
|
|
37941
|
-
if (!this._o) {
|
|
37942
|
-
this._o = extend({}, defaults, true);
|
|
37943
|
-
}
|
|
37944
|
-
|
|
37945
|
-
var opts = extend(this._o, options, true);
|
|
37946
|
-
|
|
37947
|
-
opts.isRTL = !!opts.isRTL;
|
|
37948
|
-
|
|
37949
|
-
opts.field = (opts.field && opts.field.nodeName) ? opts.field : null;
|
|
37950
|
-
|
|
37951
|
-
opts.theme = (typeof opts.theme) === 'string' && opts.theme ? opts.theme : null;
|
|
37952
|
-
|
|
37953
|
-
opts.bound = !!(opts.bound !== undefined ? opts.field && opts.bound : opts.field);
|
|
37954
|
-
|
|
37955
|
-
opts.trigger = (opts.trigger && opts.trigger.nodeName) ? opts.trigger : opts.field;
|
|
37956
|
-
|
|
37957
|
-
opts.disableWeekends = !!opts.disableWeekends;
|
|
37958
|
-
|
|
37959
|
-
opts.disableDayFn = (typeof opts.disableDayFn) === 'function' ? opts.disableDayFn : null;
|
|
37960
|
-
|
|
37961
|
-
var nom = parseInt(opts.numberOfMonths, 10) || 1;
|
|
37962
|
-
opts.numberOfMonths = nom > 4 ? 4 : nom;
|
|
37963
|
-
|
|
37964
|
-
if (!isDate(opts.minDate)) {
|
|
37965
|
-
opts.minDate = false;
|
|
37966
|
-
}
|
|
37967
|
-
if (!isDate(opts.maxDate)) {
|
|
37968
|
-
opts.maxDate = false;
|
|
37969
|
-
}
|
|
37970
|
-
if ((opts.minDate && opts.maxDate) && opts.maxDate < opts.minDate) {
|
|
37971
|
-
opts.maxDate = opts.minDate = false;
|
|
37972
|
-
}
|
|
37973
|
-
if (opts.minDate) {
|
|
37974
|
-
this.setMinDate(opts.minDate);
|
|
37975
|
-
}
|
|
37976
|
-
if (opts.maxDate) {
|
|
37977
|
-
this.setMaxDate(opts.maxDate);
|
|
37978
|
-
}
|
|
37979
|
-
|
|
37980
|
-
if (isArray(opts.yearRange)) {
|
|
37981
|
-
var fallback = new Date().getFullYear() - 10;
|
|
37982
|
-
opts.yearRange[0] = parseInt(opts.yearRange[0], 10) || fallback;
|
|
37983
|
-
opts.yearRange[1] = parseInt(opts.yearRange[1], 10) || fallback;
|
|
37984
|
-
} else {
|
|
37985
|
-
opts.yearRange = Math.abs(parseInt(opts.yearRange, 10)) || defaults.yearRange;
|
|
37986
|
-
if (opts.yearRange > 100) {
|
|
37987
|
-
opts.yearRange = 100;
|
|
37988
|
-
}
|
|
37989
|
-
}
|
|
37990
|
-
|
|
37991
|
-
return opts;
|
|
37992
|
-
},
|
|
37993
|
-
|
|
37994
|
-
/**
|
|
37995
|
-
* return a formatted string of the current selection (using Moment.js if available)
|
|
37996
|
-
*/
|
|
37997
|
-
toString: function (format) {
|
|
37998
|
-
format = format || this._o.format;
|
|
37999
|
-
if (!isDate(this._d)) {
|
|
38000
|
-
return '';
|
|
38001
|
-
}
|
|
38002
|
-
if (this._o.toString) {
|
|
38003
|
-
return this._o.toString(this._d, format);
|
|
38004
|
-
}
|
|
38005
|
-
if (hasMoment) {
|
|
38006
|
-
return moment(this._d).format(format);
|
|
38007
|
-
}
|
|
38008
|
-
return this._d.toDateString();
|
|
38009
|
-
},
|
|
38010
|
-
|
|
38011
|
-
/**
|
|
38012
|
-
* return a Moment.js object of the current selection (if available)
|
|
38013
|
-
*/
|
|
38014
|
-
getMoment: function () {
|
|
38015
|
-
return hasMoment ? moment(this._d) : null;
|
|
38016
|
-
},
|
|
38017
|
-
|
|
38018
|
-
/**
|
|
38019
|
-
* set the current selection from a Moment.js object (if available)
|
|
38020
|
-
*/
|
|
38021
|
-
setMoment: function (date, preventOnSelect) {
|
|
38022
|
-
if (hasMoment && moment.isMoment(date)) {
|
|
38023
|
-
this.setDate(date.toDate(), preventOnSelect);
|
|
38024
|
-
}
|
|
38025
|
-
},
|
|
38026
|
-
|
|
38027
|
-
/**
|
|
38028
|
-
* return a Date object of the current selection
|
|
38029
|
-
*/
|
|
38030
|
-
getDate: function () {
|
|
38031
|
-
return isDate(this._d) ? new Date(this._d.getTime()) : null;
|
|
38032
|
-
},
|
|
38033
|
-
|
|
38034
|
-
/**
|
|
38035
|
-
* set the current selection
|
|
38036
|
-
*/
|
|
38037
|
-
setDate: function (date, preventOnSelect) {
|
|
38038
|
-
if (!date) {
|
|
38039
|
-
this._d = null;
|
|
38040
|
-
|
|
38041
|
-
if (this._o.field) {
|
|
38042
|
-
this._o.field.value = '';
|
|
38043
|
-
fireEvent(this._o.field, 'change', { firedBy: this });
|
|
38044
|
-
}
|
|
38045
|
-
|
|
38046
|
-
return this.draw();
|
|
38047
|
-
}
|
|
38048
|
-
if (typeof date === 'string') {
|
|
38049
|
-
date = new Date(Date.parse(date));
|
|
38050
|
-
}
|
|
38051
|
-
if (!isDate(date)) {
|
|
38052
|
-
return;
|
|
38053
|
-
}
|
|
38054
|
-
|
|
38055
|
-
var min = this._o.minDate,
|
|
38056
|
-
max = this._o.maxDate;
|
|
38057
|
-
|
|
38058
|
-
if (isDate(min) && date < min) {
|
|
38059
|
-
date = min;
|
|
38060
|
-
} else if (isDate(max) && date > max) {
|
|
38061
|
-
date = max;
|
|
38062
|
-
}
|
|
38063
|
-
|
|
38064
|
-
this._d = new Date(date.getTime());
|
|
38065
|
-
setToStartOfDay(this._d);
|
|
38066
|
-
this.gotoDate(this._d);
|
|
38067
|
-
|
|
38068
|
-
if (this._o.field) {
|
|
38069
|
-
this._o.field.value = this.toString();
|
|
38070
|
-
fireEvent(this._o.field, 'change', { firedBy: this });
|
|
38071
|
-
}
|
|
38072
|
-
if (!preventOnSelect && typeof this._o.onSelect === 'function') {
|
|
38073
|
-
this._o.onSelect.call(this, this.getDate());
|
|
38074
|
-
}
|
|
38075
|
-
},
|
|
38076
|
-
|
|
38077
|
-
/**
|
|
38078
|
-
* change view to a specific date
|
|
38079
|
-
*/
|
|
38080
|
-
gotoDate: function (date) {
|
|
38081
|
-
var newCalendar = true;
|
|
38082
|
-
|
|
38083
|
-
if (!isDate(date)) {
|
|
38084
|
-
return;
|
|
38085
|
-
}
|
|
38086
|
-
|
|
38087
|
-
if (this.calendars) {
|
|
38088
|
-
var firstVisibleDate = new Date(this.calendars[0].year, this.calendars[0].month, 1),
|
|
38089
|
-
lastVisibleDate = new Date(this.calendars[this.calendars.length - 1].year, this.calendars[this.calendars.length - 1].month, 1),
|
|
38090
|
-
visibleDate = date.getTime();
|
|
38091
|
-
// get the end of the month
|
|
38092
|
-
lastVisibleDate.setMonth(lastVisibleDate.getMonth() + 1);
|
|
38093
|
-
lastVisibleDate.setDate(lastVisibleDate.getDate() - 1);
|
|
38094
|
-
newCalendar = (visibleDate < firstVisibleDate.getTime() || lastVisibleDate.getTime() < visibleDate);
|
|
38095
|
-
}
|
|
38096
|
-
|
|
38097
|
-
if (newCalendar) {
|
|
38098
|
-
this.calendars = [{
|
|
38099
|
-
month: date.getMonth(),
|
|
38100
|
-
year: date.getFullYear()
|
|
38101
|
-
}];
|
|
38102
|
-
if (this._o.mainCalendar === 'right') {
|
|
38103
|
-
this.calendars[0].month += 1 - this._o.numberOfMonths;
|
|
38104
|
-
}
|
|
38105
|
-
}
|
|
38106
|
-
|
|
38107
|
-
this.adjustCalendars();
|
|
38108
|
-
},
|
|
38109
|
-
|
|
38110
|
-
adjustDate: function (sign, days) {
|
|
38111
|
-
|
|
38112
|
-
var day = this.getDate() || new Date();
|
|
38113
|
-
var difference = parseInt(days) * 24 * 60 * 60 * 1000;
|
|
38114
|
-
|
|
38115
|
-
var newDay;
|
|
38116
|
-
|
|
38117
|
-
if (sign === 'add') {
|
|
38118
|
-
newDay = new Date(day.valueOf() + difference);
|
|
38119
|
-
} else if (sign === 'subtract') {
|
|
38120
|
-
newDay = new Date(day.valueOf() - difference);
|
|
38121
|
-
}
|
|
38122
|
-
|
|
38123
|
-
this.setDate(newDay);
|
|
38124
|
-
},
|
|
38125
|
-
|
|
38126
|
-
adjustCalendars: function () {
|
|
38127
|
-
this.calendars[0] = adjustCalendar(this.calendars[0]);
|
|
38128
|
-
for (var c = 1; c < this._o.numberOfMonths; c++) {
|
|
38129
|
-
this.calendars[c] = adjustCalendar({
|
|
38130
|
-
month: this.calendars[0].month + c,
|
|
38131
|
-
year: this.calendars[0].year
|
|
38132
|
-
});
|
|
38133
|
-
}
|
|
38134
|
-
this.draw();
|
|
38135
|
-
},
|
|
38136
|
-
|
|
38137
|
-
gotoToday: function () {
|
|
38138
|
-
this.gotoDate(new Date());
|
|
38139
|
-
},
|
|
38140
|
-
|
|
38141
|
-
/**
|
|
38142
|
-
* change view to a specific month (zero-index, e.g. 0: January)
|
|
38143
|
-
*/
|
|
38144
|
-
gotoMonth: function (month) {
|
|
38145
|
-
if (!isNaN(month)) {
|
|
38146
|
-
this.calendars[0].month = parseInt(month, 10);
|
|
38147
|
-
this.adjustCalendars();
|
|
38148
|
-
}
|
|
38149
|
-
},
|
|
38150
|
-
|
|
38151
|
-
nextMonth: function () {
|
|
38152
|
-
this.calendars[0].month++;
|
|
38153
|
-
this.adjustCalendars();
|
|
38154
|
-
},
|
|
38155
|
-
|
|
38156
|
-
prevMonth: function () {
|
|
38157
|
-
this.calendars[0].month--;
|
|
38158
|
-
this.adjustCalendars();
|
|
38159
|
-
},
|
|
38160
|
-
|
|
38161
|
-
/**
|
|
38162
|
-
* change view to a specific full year (e.g. "2012")
|
|
38163
|
-
*/
|
|
38164
|
-
gotoYear: function (year) {
|
|
38165
|
-
if (!isNaN(year)) {
|
|
38166
|
-
this.calendars[0].year = parseInt(year, 10);
|
|
38167
|
-
this.adjustCalendars();
|
|
38168
|
-
}
|
|
38169
|
-
},
|
|
38170
|
-
|
|
38171
|
-
/**
|
|
38172
|
-
* change the minDate
|
|
38173
|
-
*/
|
|
38174
|
-
setMinDate: function (value) {
|
|
38175
|
-
if (value instanceof Date) {
|
|
38176
|
-
setToStartOfDay(value);
|
|
38177
|
-
this._o.minDate = value;
|
|
38178
|
-
this._o.minYear = value.getFullYear();
|
|
38179
|
-
this._o.minMonth = value.getMonth();
|
|
38180
|
-
} else {
|
|
38181
|
-
this._o.minDate = defaults.minDate;
|
|
38182
|
-
this._o.minYear = defaults.minYear;
|
|
38183
|
-
this._o.minMonth = defaults.minMonth;
|
|
38184
|
-
this._o.startRange = defaults.startRange;
|
|
38185
|
-
}
|
|
38186
|
-
|
|
38187
|
-
this.draw();
|
|
38188
|
-
},
|
|
38189
|
-
|
|
38190
|
-
/**
|
|
38191
|
-
* change the maxDate
|
|
38192
|
-
*/
|
|
38193
|
-
setMaxDate: function (value) {
|
|
38194
|
-
if (value instanceof Date) {
|
|
38195
|
-
setToStartOfDay(value);
|
|
38196
|
-
this._o.maxDate = value;
|
|
38197
|
-
this._o.maxYear = value.getFullYear();
|
|
38198
|
-
this._o.maxMonth = value.getMonth();
|
|
38199
|
-
} else {
|
|
38200
|
-
this._o.maxDate = defaults.maxDate;
|
|
38201
|
-
this._o.maxYear = defaults.maxYear;
|
|
38202
|
-
this._o.maxMonth = defaults.maxMonth;
|
|
38203
|
-
this._o.endRange = defaults.endRange;
|
|
38204
|
-
}
|
|
38205
|
-
|
|
38206
|
-
this.draw();
|
|
38207
|
-
},
|
|
38208
|
-
|
|
38209
|
-
setStartRange: function (value) {
|
|
38210
|
-
this._o.startRange = value;
|
|
38211
|
-
},
|
|
38212
|
-
|
|
38213
|
-
setEndRange: function (value) {
|
|
38214
|
-
this._o.endRange = value;
|
|
38215
|
-
},
|
|
38216
|
-
|
|
38217
|
-
/**
|
|
38218
|
-
* refresh the HTML
|
|
38219
|
-
*/
|
|
38220
|
-
draw: function (force) {
|
|
38221
|
-
if (!this._v && !force) {
|
|
38222
|
-
return;
|
|
38223
|
-
}
|
|
38224
|
-
var opts = this._o,
|
|
38225
|
-
minYear = opts.minYear,
|
|
38226
|
-
maxYear = opts.maxYear,
|
|
38227
|
-
minMonth = opts.minMonth,
|
|
38228
|
-
maxMonth = opts.maxMonth,
|
|
38229
|
-
html = '',
|
|
38230
|
-
randId;
|
|
38231
|
-
|
|
38232
|
-
if (this._y <= minYear) {
|
|
38233
|
-
this._y = minYear;
|
|
38234
|
-
if (!isNaN(minMonth) && this._m < minMonth) {
|
|
38235
|
-
this._m = minMonth;
|
|
38236
|
-
}
|
|
38237
|
-
}
|
|
38238
|
-
if (this._y >= maxYear) {
|
|
38239
|
-
this._y = maxYear;
|
|
38240
|
-
if (!isNaN(maxMonth) && this._m > maxMonth) {
|
|
38241
|
-
this._m = maxMonth;
|
|
38242
|
-
}
|
|
38243
|
-
}
|
|
38244
|
-
|
|
38245
|
-
for (var c = 0; c < opts.numberOfMonths; c++) {
|
|
38246
|
-
randId = 'pika-title-' + Math.random().toString(36).replace(/[^a-z]+/g, '').substr(0, 2);
|
|
38247
|
-
html += '<div class="pika-lendar">' + renderTitle(this, c, this.calendars[c].year, this.calendars[c].month, this.calendars[0].year, randId) + this.render(this.calendars[c].year, this.calendars[c].month, randId) + '</div>';
|
|
38248
|
-
}
|
|
38249
|
-
|
|
38250
|
-
this.el.innerHTML = html;
|
|
38251
|
-
|
|
38252
|
-
if (opts.bound) {
|
|
38253
|
-
if (opts.field.type !== 'hidden') {
|
|
38254
|
-
sto(function () {
|
|
38255
|
-
opts.trigger.focus();
|
|
38256
|
-
}, 1);
|
|
38257
|
-
}
|
|
38258
|
-
}
|
|
38259
|
-
|
|
38260
|
-
if (typeof this._o.onDraw === 'function') {
|
|
38261
|
-
this._o.onDraw(this);
|
|
38262
|
-
}
|
|
38263
|
-
|
|
38264
|
-
if (opts.bound) {
|
|
38265
|
-
// let the screen reader user know to use arrow keys
|
|
38266
|
-
opts.field.setAttribute('aria-label', 'Use the arrow keys to pick a date');
|
|
38267
|
-
}
|
|
38268
|
-
},
|
|
38269
|
-
|
|
38270
|
-
adjustPosition: function () {
|
|
38271
|
-
var field, pEl, width, height, viewportWidth, viewportHeight, scrollTop, left, top, clientRect;
|
|
38272
|
-
|
|
38273
|
-
if (this._o.container) return;
|
|
38274
|
-
|
|
38275
|
-
this.el.style.position = 'absolute';
|
|
38276
|
-
|
|
38277
|
-
field = this._o.trigger;
|
|
38278
|
-
pEl = field;
|
|
38279
|
-
width = this.el.offsetWidth;
|
|
38280
|
-
height = this.el.offsetHeight;
|
|
38281
|
-
viewportWidth = window.innerWidth || document.documentElement.clientWidth;
|
|
38282
|
-
viewportHeight = window.innerHeight || document.documentElement.clientHeight;
|
|
38283
|
-
scrollTop = window.pageYOffset || document.body.scrollTop || document.documentElement.scrollTop;
|
|
38284
|
-
|
|
38285
|
-
if (typeof field.getBoundingClientRect === 'function') {
|
|
38286
|
-
clientRect = field.getBoundingClientRect();
|
|
38287
|
-
left = clientRect.left + window.pageXOffset;
|
|
38288
|
-
top = clientRect.bottom + window.pageYOffset;
|
|
38289
|
-
} else {
|
|
38290
|
-
left = pEl.offsetLeft;
|
|
38291
|
-
top = pEl.offsetTop + pEl.offsetHeight;
|
|
38292
|
-
while ((pEl = pEl.offsetParent)) {
|
|
38293
|
-
left += pEl.offsetLeft;
|
|
38294
|
-
top += pEl.offsetTop;
|
|
38295
|
-
}
|
|
38296
|
-
}
|
|
38297
|
-
|
|
38298
|
-
// default position is bottom & left
|
|
38299
|
-
if ((this._o.reposition && left + width > viewportWidth) ||
|
|
38300
|
-
(
|
|
38301
|
-
this._o.position.indexOf('right') > -1 &&
|
|
38302
|
-
left - width + field.offsetWidth > 0
|
|
38303
|
-
)
|
|
38304
|
-
) {
|
|
38305
|
-
left = left - width + field.offsetWidth;
|
|
38306
|
-
}
|
|
38307
|
-
if ((this._o.reposition && top + height > viewportHeight + scrollTop) ||
|
|
38308
|
-
(
|
|
38309
|
-
this._o.position.indexOf('top') > -1 &&
|
|
38310
|
-
top - height - field.offsetHeight > 0
|
|
38311
|
-
)
|
|
38312
|
-
) {
|
|
38313
|
-
top = top - height - field.offsetHeight;
|
|
38314
|
-
}
|
|
38315
|
-
|
|
38316
|
-
this.el.style.left = left + 'px';
|
|
38317
|
-
this.el.style.top = top + 'px';
|
|
38318
|
-
},
|
|
38319
|
-
|
|
38320
|
-
/**
|
|
38321
|
-
* render HTML for a particular month
|
|
38322
|
-
*/
|
|
38323
|
-
render: function (year, month, randId) {
|
|
38324
|
-
var opts = this._o,
|
|
38325
|
-
now = new Date(),
|
|
38326
|
-
days = getDaysInMonth(year, month),
|
|
38327
|
-
before = new Date(year, month, 1).getDay(),
|
|
38328
|
-
data = [],
|
|
38329
|
-
row = [];
|
|
38330
|
-
setToStartOfDay(now);
|
|
38331
|
-
if (opts.firstDay > 0) {
|
|
38332
|
-
before -= opts.firstDay;
|
|
38333
|
-
if (before < 0) {
|
|
38334
|
-
before += 7;
|
|
38335
|
-
}
|
|
38336
|
-
}
|
|
38337
|
-
var previousMonth = month === 0 ? 11 : month - 1,
|
|
38338
|
-
nextMonth = month === 11 ? 0 : month + 1,
|
|
38339
|
-
yearOfPreviousMonth = month === 0 ? year - 1 : year,
|
|
38340
|
-
yearOfNextMonth = month === 11 ? year + 1 : year,
|
|
38341
|
-
daysInPreviousMonth = getDaysInMonth(yearOfPreviousMonth, previousMonth);
|
|
38342
|
-
var cells = days + before,
|
|
38343
|
-
after = cells;
|
|
38344
|
-
while (after > 7) {
|
|
38345
|
-
after -= 7;
|
|
38346
|
-
}
|
|
38347
|
-
cells += 7 - after;
|
|
38348
|
-
var isWeekSelected = false;
|
|
38349
|
-
for (var i = 0, r = 0; i < cells; i++) {
|
|
38350
|
-
var day = new Date(year, month, 1 + (i - before)),
|
|
38351
|
-
isSelected = isDate(this._d) ? compareDates(day, this._d) : false,
|
|
38352
|
-
isToday = compareDates(day, now),
|
|
38353
|
-
hasEvent = opts.events.indexOf(day.toDateString()) !== -1 ? true : false,
|
|
38354
|
-
isEmpty = i < before || i >= (days + before),
|
|
38355
|
-
dayNumber = 1 + (i - before),
|
|
38356
|
-
monthNumber = month,
|
|
38357
|
-
yearNumber = year,
|
|
38358
|
-
isStartRange = opts.startRange && compareDates(opts.startRange, day),
|
|
38359
|
-
isEndRange = opts.endRange && compareDates(opts.endRange, day),
|
|
38360
|
-
isInRange = opts.startRange && opts.endRange && opts.startRange < day && day < opts.endRange,
|
|
38361
|
-
isDisabled = (opts.minDate && day < opts.minDate) ||
|
|
38362
|
-
(opts.maxDate && day > opts.maxDate) ||
|
|
38363
|
-
(opts.disableWeekends && isWeekend(day)) ||
|
|
38364
|
-
(opts.disableDayFn && opts.disableDayFn(day));
|
|
38365
|
-
|
|
38366
|
-
if (isEmpty) {
|
|
38367
|
-
if (i < before) {
|
|
38368
|
-
dayNumber = daysInPreviousMonth + dayNumber;
|
|
38369
|
-
monthNumber = previousMonth;
|
|
38370
|
-
yearNumber = yearOfPreviousMonth;
|
|
38371
|
-
} else {
|
|
38372
|
-
dayNumber = dayNumber - days;
|
|
38373
|
-
monthNumber = nextMonth;
|
|
38374
|
-
yearNumber = yearOfNextMonth;
|
|
38375
|
-
}
|
|
38376
|
-
}
|
|
38377
|
-
|
|
38378
|
-
var dayConfig = {
|
|
38379
|
-
day: dayNumber,
|
|
38380
|
-
month: monthNumber,
|
|
38381
|
-
year: yearNumber,
|
|
38382
|
-
hasEvent: hasEvent,
|
|
38383
|
-
isSelected: isSelected,
|
|
38384
|
-
isToday: isToday,
|
|
38385
|
-
isDisabled: isDisabled,
|
|
38386
|
-
isEmpty: isEmpty,
|
|
38387
|
-
isStartRange: isStartRange,
|
|
38388
|
-
isEndRange: isEndRange,
|
|
38389
|
-
isInRange: isInRange,
|
|
38390
|
-
showDaysInNextAndPreviousMonths: opts.showDaysInNextAndPreviousMonths,
|
|
38391
|
-
enableSelectionDaysInNextAndPreviousMonths: opts.enableSelectionDaysInNextAndPreviousMonths
|
|
38392
|
-
};
|
|
38393
|
-
|
|
38394
|
-
if (opts.pickWholeWeek && isSelected) {
|
|
38395
|
-
isWeekSelected = true;
|
|
38396
|
-
}
|
|
38397
|
-
|
|
38398
|
-
row.push(renderDay(dayConfig));
|
|
38399
|
-
|
|
38400
|
-
if (++r === 7) {
|
|
38401
|
-
if (opts.showWeekNumber) {
|
|
38402
|
-
row.unshift(renderWeek(i - before, month, year));
|
|
38403
|
-
}
|
|
38404
|
-
data.push(renderRow(row, opts.isRTL, opts.pickWholeWeek, isWeekSelected));
|
|
38405
|
-
row = [];
|
|
38406
|
-
r = 0;
|
|
38407
|
-
isWeekSelected = false;
|
|
38408
|
-
}
|
|
38409
|
-
}
|
|
38410
|
-
return renderTable(opts, data, randId);
|
|
38411
|
-
},
|
|
38412
|
-
|
|
38413
|
-
isVisible: function () {
|
|
38414
|
-
return this._v;
|
|
38415
|
-
},
|
|
38416
|
-
|
|
38417
|
-
show: function () {
|
|
38418
|
-
if (!this.isVisible()) {
|
|
38419
|
-
this._v = true;
|
|
38420
|
-
this.draw();
|
|
38421
|
-
removeClass(this.el, 'is-hidden');
|
|
38422
|
-
if (this._o.bound) {
|
|
38423
|
-
addEvent(document, 'click', this._onClick);
|
|
38424
|
-
this.adjustPosition();
|
|
38425
|
-
}
|
|
38426
|
-
if (typeof this._o.onOpen === 'function') {
|
|
38427
|
-
this._o.onOpen.call(this);
|
|
38428
|
-
}
|
|
38429
|
-
}
|
|
38430
|
-
},
|
|
38431
|
-
|
|
38432
|
-
hide: function () {
|
|
38433
|
-
var v = this._v;
|
|
38434
|
-
if (v !== false) {
|
|
38435
|
-
if (this._o.bound) {
|
|
38436
|
-
removeEvent(document, 'click', this._onClick);
|
|
38437
|
-
}
|
|
38438
|
-
this.el.style.position = 'static'; // reset
|
|
38439
|
-
this.el.style.left = 'auto';
|
|
38440
|
-
this.el.style.top = 'auto';
|
|
38441
|
-
addClass(this.el, 'is-hidden');
|
|
38442
|
-
this._v = false;
|
|
38443
|
-
if (v !== undefined && typeof this._o.onClose === 'function') {
|
|
38444
|
-
this._o.onClose.call(this);
|
|
38445
|
-
}
|
|
38446
|
-
}
|
|
38447
|
-
},
|
|
38448
|
-
|
|
38449
|
-
/**
|
|
38450
|
-
* GAME OVER
|
|
38451
|
-
*/
|
|
38452
|
-
destroy: function () {
|
|
38453
|
-
var opts = this._o;
|
|
38454
|
-
|
|
38455
|
-
this.hide();
|
|
38456
|
-
removeEvent(this.el, 'mousedown', this._onMouseDown, true);
|
|
38457
|
-
removeEvent(this.el, 'touchend', this._onMouseDown, true);
|
|
38458
|
-
removeEvent(this.el, 'change', this._onChange);
|
|
38459
|
-
if (opts.keyboardInput) {
|
|
38460
|
-
removeEvent(document, 'keydown', this._onKeyChange);
|
|
38461
|
-
}
|
|
38462
|
-
if (opts.field) {
|
|
38463
|
-
removeEvent(opts.field, 'change', this._onInputChange);
|
|
38464
|
-
if (opts.bound) {
|
|
38465
|
-
removeEvent(opts.trigger, 'click', this._onInputClick);
|
|
38466
|
-
removeEvent(opts.trigger, 'focus', this._onInputFocus);
|
|
38467
|
-
removeEvent(opts.trigger, 'blur', this._onInputBlur);
|
|
38468
|
-
}
|
|
38469
|
-
}
|
|
38470
|
-
if (this.el.parentNode) {
|
|
38471
|
-
this.el.parentNode.removeChild(this.el);
|
|
38472
|
-
}
|
|
38473
|
-
}
|
|
38474
|
-
|
|
38475
|
-
};
|
|
38476
|
-
|
|
38477
|
-
return Pikaday;
|
|
38478
|
-
}));
|
|
38479
|
-
} (pikaday$1));
|
|
38480
|
-
return pikaday$1.exports;
|
|
38481
|
-
}
|
|
38482
|
-
|
|
38483
|
-
var pikadayExports = /*@__PURE__*/ requirePikaday();
|
|
38484
|
-
var Pikaday = /*@__PURE__*/getDefaultExportFromCjs(pikadayExports);
|
|
38485
|
-
|
|
38486
|
-
// Ensure moment is available globally for Pikaday
|
|
38487
|
-
if (typeof window !== 'undefined') {
|
|
38488
|
-
window.moment = moment;
|
|
38489
|
-
}
|
|
38490
|
-
// Export a function to safely create Pikaday instances
|
|
38491
|
-
function createPikaday(options) {
|
|
38492
|
-
if (typeof window === 'undefined') {
|
|
38493
|
-
console.warn('Pikaday requires a browser environment');
|
|
38494
|
-
return null;
|
|
38495
|
-
}
|
|
38496
|
-
const Pikaday = window.Pikaday;
|
|
38497
|
-
if (!Pikaday) {
|
|
38498
|
-
console.error('Pikaday not available. Make sure pikaday.js is loaded.');
|
|
38499
|
-
return null;
|
|
38500
|
-
}
|
|
38501
|
-
if (!moment || !window.moment) {
|
|
38502
|
-
console.error('Moment.js not available. Pikaday requires moment.js.');
|
|
38503
|
-
return null;
|
|
38504
|
-
}
|
|
38505
|
-
return new Pikaday(options);
|
|
38506
|
-
}
|
|
37336
|
+
// Ensure moment is available globally for Pikaday
|
|
37337
|
+
if (typeof window !== 'undefined') {
|
|
37338
|
+
window.moment = moment;
|
|
37339
|
+
}
|
|
37340
|
+
// Export a function to safely create Pikaday instances
|
|
37341
|
+
function createPikaday(options) {
|
|
37342
|
+
if (typeof window === 'undefined') {
|
|
37343
|
+
console.warn('Pikaday requires a browser environment');
|
|
37344
|
+
return null;
|
|
37345
|
+
}
|
|
37346
|
+
const Pikaday = window.Pikaday;
|
|
37347
|
+
if (!Pikaday) {
|
|
37348
|
+
console.error('Pikaday not available. Make sure pikaday.js is loaded.');
|
|
37349
|
+
return null;
|
|
37350
|
+
}
|
|
37351
|
+
if (!moment || !window.moment) {
|
|
37352
|
+
console.error('Moment.js not available. Pikaday requires moment.js.');
|
|
37353
|
+
return null;
|
|
37354
|
+
}
|
|
37355
|
+
return new Pikaday(options);
|
|
37356
|
+
}
|
|
38507
37357
|
|
|
38508
37358
|
class DateTimePicker extends ChartComponent {
|
|
38509
37359
|
constructor(renderTarget) {
|
|
@@ -39077,7 +37927,30 @@
|
|
|
39077
37927
|
this.pickerIsVisible = false;
|
|
39078
37928
|
}
|
|
39079
37929
|
buttonDateTimeFormat(millis) {
|
|
39080
|
-
|
|
37930
|
+
const date = new Date(millis);
|
|
37931
|
+
const locale = this.chartOptions.dateLocale || 'en-US';
|
|
37932
|
+
const is24Hour = this.chartOptions.is24HourTime !== false;
|
|
37933
|
+
const formatOptions = {
|
|
37934
|
+
year: 'numeric',
|
|
37935
|
+
month: '2-digit',
|
|
37936
|
+
day: '2-digit',
|
|
37937
|
+
hour: '2-digit',
|
|
37938
|
+
minute: '2-digit',
|
|
37939
|
+
second: '2-digit',
|
|
37940
|
+
hour12: !is24Hour
|
|
37941
|
+
};
|
|
37942
|
+
try {
|
|
37943
|
+
if (this.chartOptions.offset && this.chartOptions.offset !== 'Local') {
|
|
37944
|
+
formatOptions.timeZone = this.getTimezoneFromOffset(this.chartOptions.offset);
|
|
37945
|
+
}
|
|
37946
|
+
const baseFormat = date.toLocaleString(locale, formatOptions);
|
|
37947
|
+
const milliseconds = date.getMilliseconds().toString().padStart(3, '0');
|
|
37948
|
+
return `${baseFormat}.${milliseconds}`;
|
|
37949
|
+
}
|
|
37950
|
+
catch (error) {
|
|
37951
|
+
console.warn(`Failed to format date for locale ${locale}:`, error);
|
|
37952
|
+
return Utils.timeFormat(!this.chartOptions.minutesForTimeLabels, !this.chartOptions.minutesForTimeLabels, this.chartOptions.offset, this.chartOptions.is24HourTime, 0, null, this.chartOptions.dateLocale)(millis);
|
|
37953
|
+
}
|
|
39081
37954
|
}
|
|
39082
37955
|
render(chartOptions, minMillis, maxMillis, onSet = null) {
|
|
39083
37956
|
this.chartOptions.setOptions(chartOptions);
|
|
@@ -39097,11 +37970,22 @@
|
|
|
39097
37970
|
}
|
|
39098
37971
|
super.themify(select(this.renderTarget), this.chartOptions.theme);
|
|
39099
37972
|
}
|
|
37973
|
+
getTimezoneFromOffset(offset) {
|
|
37974
|
+
const timezoneMap = {
|
|
37975
|
+
'UTC': 'UTC',
|
|
37976
|
+
'EST': 'America/New_York',
|
|
37977
|
+
'PST': 'America/Los_Angeles',
|
|
37978
|
+
'CST': 'America/Chicago',
|
|
37979
|
+
'MST': 'America/Denver'
|
|
37980
|
+
};
|
|
37981
|
+
return timezoneMap[offset] || 'UTC';
|
|
37982
|
+
}
|
|
39100
37983
|
}
|
|
39101
37984
|
|
|
39102
37985
|
class DateTimeButtonRange extends DateTimeButton {
|
|
39103
37986
|
constructor(renderTarget) {
|
|
39104
37987
|
super(renderTarget);
|
|
37988
|
+
this.clickOutsideHandler = null;
|
|
39105
37989
|
}
|
|
39106
37990
|
setButtonText(fromMillis, toMillis, isRelative, quickTime) {
|
|
39107
37991
|
let fromString = this.buttonDateTimeFormat(fromMillis);
|
|
@@ -39121,10 +38005,38 @@
|
|
|
39121
38005
|
onClose() {
|
|
39122
38006
|
this.dateTimePickerContainer.style("display", "none");
|
|
39123
38007
|
this.dateTimeButton.node().focus();
|
|
38008
|
+
this.removeClickOutsideHandler();
|
|
38009
|
+
}
|
|
38010
|
+
removeClickOutsideHandler() {
|
|
38011
|
+
if (this.clickOutsideHandler) {
|
|
38012
|
+
document.removeEventListener('click', this.clickOutsideHandler);
|
|
38013
|
+
this.clickOutsideHandler = null;
|
|
38014
|
+
}
|
|
38015
|
+
}
|
|
38016
|
+
setupClickOutsideHandler() {
|
|
38017
|
+
// Remove any existing handler first
|
|
38018
|
+
this.removeClickOutsideHandler();
|
|
38019
|
+
// Add handler after a small delay to prevent the opening click from immediately closing the picker
|
|
38020
|
+
setTimeout(() => {
|
|
38021
|
+
this.clickOutsideHandler = (event) => {
|
|
38022
|
+
const pickerElement = this.dateTimePickerContainer.node();
|
|
38023
|
+
const buttonElement = this.dateTimeButton.node();
|
|
38024
|
+
const target = event.target;
|
|
38025
|
+
// Check if click is outside both the picker and the button
|
|
38026
|
+
if (pickerElement && buttonElement &&
|
|
38027
|
+
!pickerElement.contains(target) &&
|
|
38028
|
+
!buttonElement.contains(target)) {
|
|
38029
|
+
this.onClose();
|
|
38030
|
+
}
|
|
38031
|
+
};
|
|
38032
|
+
document.addEventListener('click', this.clickOutsideHandler);
|
|
38033
|
+
}, 0);
|
|
39124
38034
|
}
|
|
39125
38035
|
render(chartOptions = {}, minMillis, maxMillis, fromMillis = null, toMillis = null, onSet = null, onCancel = null) {
|
|
39126
38036
|
super.render(chartOptions, minMillis, maxMillis, onSet);
|
|
39127
|
-
select(this.renderTarget)
|
|
38037
|
+
let container = select(this.renderTarget);
|
|
38038
|
+
container.classed('tsi-dateTimeContainerRange', true);
|
|
38039
|
+
container.style('position', 'relative');
|
|
39128
38040
|
this.fromMillis = fromMillis;
|
|
39129
38041
|
this.toMillis = toMillis;
|
|
39130
38042
|
this.onCancel = onCancel ? onCancel : () => { };
|
|
@@ -39150,6 +38062,7 @@
|
|
|
39150
38062
|
this.onClose();
|
|
39151
38063
|
this.onCancel();
|
|
39152
38064
|
});
|
|
38065
|
+
this.setupClickOutsideHandler();
|
|
39153
38066
|
}
|
|
39154
38067
|
});
|
|
39155
38068
|
}
|
|
@@ -43455,7 +42368,7 @@
|
|
|
43455
42368
|
super(renderTarget);
|
|
43456
42369
|
this.chartOptions = new ChartOptions(); // TODO handle onkeyup and oninput in chart options
|
|
43457
42370
|
}
|
|
43458
|
-
render(
|
|
42371
|
+
render(chartOptions) {
|
|
43459
42372
|
this.chartOptions.setOptions(chartOptions);
|
|
43460
42373
|
let targetElement = select(this.renderTarget);
|
|
43461
42374
|
targetElement.html("");
|
|
@@ -43809,20 +42722,102 @@
|
|
|
43809
42722
|
}
|
|
43810
42723
|
}
|
|
43811
42724
|
|
|
42725
|
+
// Centralized renderer for the hierarchy tree. Keeps a stable D3 data-join and
|
|
42726
|
+
// updates existing DOM nodes instead of fully recreating them on each render.
|
|
42727
|
+
class TreeRenderer {
|
|
42728
|
+
static render(owner, data, target) {
|
|
42729
|
+
// Ensure an <ul> exists for this target (one list per level)
|
|
42730
|
+
let list = target.select('ul');
|
|
42731
|
+
if (list.empty()) {
|
|
42732
|
+
list = target.append('ul').attr('role', target === owner.hierarchyElem ? 'tree' : 'group');
|
|
42733
|
+
}
|
|
42734
|
+
const entries = Object.keys(data).map(k => ({ key: k, item: data[k] }));
|
|
42735
|
+
const liSelection = list.selectAll('li').data(entries, (d) => d && d.key);
|
|
42736
|
+
liSelection.exit().remove();
|
|
42737
|
+
const liEnter = liSelection.enter().append('li')
|
|
42738
|
+
.attr('role', 'none')
|
|
42739
|
+
.classed('tsi-leaf', (d) => !!d.item.isLeaf);
|
|
42740
|
+
const liMerged = liEnter.merge(liSelection);
|
|
42741
|
+
const setSize = entries.length;
|
|
42742
|
+
liMerged.each((d, i, nodes) => {
|
|
42743
|
+
const entry = d;
|
|
42744
|
+
const li = select(nodes[i]);
|
|
42745
|
+
if (owner.selectedIds && owner.selectedIds.includes(entry.item.id)) {
|
|
42746
|
+
li.classed('tsi-selected', true);
|
|
42747
|
+
}
|
|
42748
|
+
else {
|
|
42749
|
+
li.classed('tsi-selected', false);
|
|
42750
|
+
}
|
|
42751
|
+
// determine instance vs hierarchy node by presence of isLeaf flag
|
|
42752
|
+
const isInstance = !!entry.item.isLeaf;
|
|
42753
|
+
const nodeNameToCheckIfExists = isInstance ? owner.instanceNodeString(entry.item) : entry.key;
|
|
42754
|
+
const displayName = (entry.item && (entry.item.displayName || nodeNameToCheckIfExists)) || nodeNameToCheckIfExists;
|
|
42755
|
+
li.attr('data-display-name', displayName);
|
|
42756
|
+
let itemElem = li.select('.tsi-hierarchyItem');
|
|
42757
|
+
if (itemElem.empty()) {
|
|
42758
|
+
const newListElem = owner.createHierarchyItemElem(entry.item, entry.key);
|
|
42759
|
+
li.node().appendChild(newListElem.node());
|
|
42760
|
+
itemElem = li.select('.tsi-hierarchyItem');
|
|
42761
|
+
}
|
|
42762
|
+
itemElem.attr('aria-label', isInstance ? owner.getAriaLabel(entry.item) : entry.key);
|
|
42763
|
+
itemElem.attr('title', isInstance ? owner.getAriaLabel(entry.item) : entry.key);
|
|
42764
|
+
itemElem.attr('aria-expanded', String(entry.item.isExpanded));
|
|
42765
|
+
// accessibility: set treeitem level and position in set
|
|
42766
|
+
const ariaLevel = String(((entry.item && typeof entry.item.level === 'number') ? entry.item.level : 0) + 1);
|
|
42767
|
+
itemElem.attr('aria-level', ariaLevel);
|
|
42768
|
+
itemElem.attr('aria-posinset', String(i + 1));
|
|
42769
|
+
itemElem.attr('aria-setsize', String(setSize));
|
|
42770
|
+
if (!isInstance) {
|
|
42771
|
+
itemElem.select('.tsi-caret-icon').attr('style', `left: ${(entry.item.level) * 18 + 20}px`);
|
|
42772
|
+
itemElem.select('.tsi-name').text(entry.key);
|
|
42773
|
+
itemElem.select('.tsi-instanceCount').text(entry.item.cumulativeInstanceCount);
|
|
42774
|
+
}
|
|
42775
|
+
else {
|
|
42776
|
+
const nameSpan = itemElem.select('.tsi-name');
|
|
42777
|
+
nameSpan.html('');
|
|
42778
|
+
Utils.appendFormattedElementsFromString(nameSpan, owner.instanceNodeStringToDisplay(entry.item));
|
|
42779
|
+
}
|
|
42780
|
+
entry.item.node = li;
|
|
42781
|
+
if (entry.item.children) {
|
|
42782
|
+
entry.item.isExpanded = true;
|
|
42783
|
+
li.classed('tsi-expanded', true);
|
|
42784
|
+
// recurse using TreeRenderer to keep rendering logic centralized
|
|
42785
|
+
TreeRenderer.render(owner, entry.item.children, li);
|
|
42786
|
+
}
|
|
42787
|
+
else {
|
|
42788
|
+
li.classed('tsi-expanded', false);
|
|
42789
|
+
li.selectAll('ul').remove();
|
|
42790
|
+
}
|
|
42791
|
+
});
|
|
42792
|
+
}
|
|
42793
|
+
}
|
|
42794
|
+
|
|
43812
42795
|
class HierarchyNavigation extends Component {
|
|
43813
42796
|
constructor(renderTarget) {
|
|
43814
42797
|
super(renderTarget);
|
|
43815
42798
|
this.path = [];
|
|
42799
|
+
// debounce + request cancellation fields
|
|
42800
|
+
this.debounceTimer = null;
|
|
42801
|
+
this.debounceDelay = 250; // ms
|
|
42802
|
+
this.requestCounter = 0; // increments for each outgoing request
|
|
42803
|
+
this.latestRequestId = 0; // id of the most recent request
|
|
43816
42804
|
//selectedIds
|
|
43817
42805
|
this.selectedIds = [];
|
|
43818
42806
|
this.searchEnabled = true;
|
|
43819
|
-
this.
|
|
42807
|
+
this.autocompleteEnabled = true; // Enable/disable autocomplete suggestions
|
|
42808
|
+
// Search mode state
|
|
42809
|
+
this.isSearchMode = false;
|
|
42810
|
+
// Paths that should be auto-expanded (Set of path strings like "Factory North/Building A")
|
|
42811
|
+
this.pathsToAutoExpand = new Set();
|
|
42812
|
+
this.renderSearchResult = async (r, payload, target) => {
|
|
43820
42813
|
const hierarchyData = r.hierarchyNodes?.hits?.length
|
|
43821
42814
|
? this.fillDataRecursively(r.hierarchyNodes, payload, payload)
|
|
43822
42815
|
: {};
|
|
43823
42816
|
const instancesData = r.instances?.hits?.length
|
|
43824
42817
|
? r.instances.hits.reduce((acc, i) => {
|
|
43825
|
-
|
|
42818
|
+
const inst = new InstanceNode(i.timeSeriesId, i.name, payload.path.length - this.path.length, i.id, i.description);
|
|
42819
|
+
inst.displayName = this.instanceNodeStringToDisplay(i) || '';
|
|
42820
|
+
acc[this.instanceNodeIdentifier(i)] = inst;
|
|
43826
42821
|
return acc;
|
|
43827
42822
|
}, {})
|
|
43828
42823
|
: {};
|
|
@@ -43833,7 +42828,17 @@
|
|
|
43833
42828
|
}
|
|
43834
42829
|
hitCountElem.text(r.hierarchyNodes.hitCount);
|
|
43835
42830
|
}
|
|
43836
|
-
|
|
42831
|
+
const merged = { ...hierarchyData, ...instancesData };
|
|
42832
|
+
this.renderTree(merged, target);
|
|
42833
|
+
// Auto-expand nodes that should be expanded and load their children
|
|
42834
|
+
for (const key in hierarchyData) {
|
|
42835
|
+
const node = hierarchyData[key];
|
|
42836
|
+
if (node.isExpanded && !node.children) {
|
|
42837
|
+
// This node should be expanded but doesn't have children loaded yet
|
|
42838
|
+
// We need to trigger expansion after the node is rendered
|
|
42839
|
+
await this.autoExpandNode(node);
|
|
42840
|
+
}
|
|
42841
|
+
}
|
|
43837
42842
|
};
|
|
43838
42843
|
this.hierarchyNodeIdentifier = (hName) => {
|
|
43839
42844
|
return hName ? hName : '(' + this.getString("Empty") + ')';
|
|
@@ -43855,12 +42860,20 @@
|
|
|
43855
42860
|
const targetElement = select(this.renderTarget).text('');
|
|
43856
42861
|
this.hierarchyNavWrapper = this.createHierarchyNavWrapper(targetElement);
|
|
43857
42862
|
this.selectedIds = preselectedIds;
|
|
42863
|
+
// Allow disabling autocomplete via options
|
|
42864
|
+
if (hierarchyNavOptions.autocompleteEnabled !== undefined) {
|
|
42865
|
+
this.autocompleteEnabled = hierarchyNavOptions.autocompleteEnabled;
|
|
42866
|
+
}
|
|
42867
|
+
// Pre-compute paths that need to be auto-expanded for preselected instances
|
|
42868
|
+
if (preselectedIds && preselectedIds.length > 0) {
|
|
42869
|
+
await this.computePathsToAutoExpand(preselectedIds);
|
|
42870
|
+
}
|
|
43858
42871
|
//render search wrapper
|
|
43859
|
-
|
|
42872
|
+
this.renderSearchBox();
|
|
43860
42873
|
super.themify(this.hierarchyNavWrapper, this.chartOptions.theme);
|
|
43861
42874
|
const results = this.createResultsWrapper(this.hierarchyNavWrapper);
|
|
43862
42875
|
this.hierarchyElem = this.createHierarchyElem(results);
|
|
43863
|
-
this.pathSearchAndRenderResult({ search: { payload: this.requestPayload() }, render: { target: this.hierarchyElem } });
|
|
42876
|
+
await this.pathSearchAndRenderResult({ search: { payload: this.requestPayload() }, render: { target: this.hierarchyElem } });
|
|
43864
42877
|
}
|
|
43865
42878
|
createHierarchyNavWrapper(targetElement) {
|
|
43866
42879
|
return targetElement.append('div').attr('class', 'tsi-hierarchy-nav-wrapper');
|
|
@@ -43868,8 +42881,129 @@
|
|
|
43868
42881
|
createResultsWrapper(hierarchyNavWrapper) {
|
|
43869
42882
|
return hierarchyNavWrapper.append('div').classed('tsi-hierarchy-or-list-wrapper', true);
|
|
43870
42883
|
}
|
|
42884
|
+
// create hierarchy container and attach keyboard handler
|
|
43871
42885
|
createHierarchyElem(results) {
|
|
43872
|
-
|
|
42886
|
+
const sel = results.append('div').classed('tsi-hierarchy', true).attr("role", "navigation").on('scroll', () => { });
|
|
42887
|
+
// attach keydown listener for keyboard navigation (delegated)
|
|
42888
|
+
// use native event to preserve focus handling
|
|
42889
|
+
const node = sel.node();
|
|
42890
|
+
if (node) {
|
|
42891
|
+
node.addEventListener('keydown', (ev) => this.onKeyDown(ev));
|
|
42892
|
+
}
|
|
42893
|
+
return sel;
|
|
42894
|
+
}
|
|
42895
|
+
// Keyboard navigation handlers and helpers
|
|
42896
|
+
onKeyDown(ev) {
|
|
42897
|
+
const key = ev.key;
|
|
42898
|
+
const active = document.activeElement;
|
|
42899
|
+
const container = this.hierarchyElem?.node();
|
|
42900
|
+
if (!container)
|
|
42901
|
+
return;
|
|
42902
|
+
const isInside = active && container.contains(active);
|
|
42903
|
+
if (!isInside && (key === 'ArrowDown' || key === 'ArrowUp')) {
|
|
42904
|
+
// focus first visible item on navigation keys
|
|
42905
|
+
const visible = this.getVisibleItemElems();
|
|
42906
|
+
if (visible.length) {
|
|
42907
|
+
this.focusItem(visible[0]);
|
|
42908
|
+
ev.preventDefault();
|
|
42909
|
+
}
|
|
42910
|
+
return;
|
|
42911
|
+
}
|
|
42912
|
+
if (!active)
|
|
42913
|
+
return;
|
|
42914
|
+
const current = active.classList && active.classList.contains('tsi-hierarchyItem') ? active : active.closest('.tsi-hierarchyItem');
|
|
42915
|
+
if (!current)
|
|
42916
|
+
return;
|
|
42917
|
+
switch (key) {
|
|
42918
|
+
case 'ArrowDown':
|
|
42919
|
+
this.focusNext(current);
|
|
42920
|
+
ev.preventDefault();
|
|
42921
|
+
break;
|
|
42922
|
+
case 'ArrowUp':
|
|
42923
|
+
this.focusPrev(current);
|
|
42924
|
+
ev.preventDefault();
|
|
42925
|
+
break;
|
|
42926
|
+
case 'ArrowRight':
|
|
42927
|
+
this.handleArrowRight(current);
|
|
42928
|
+
ev.preventDefault();
|
|
42929
|
+
break;
|
|
42930
|
+
case 'ArrowLeft':
|
|
42931
|
+
this.handleArrowLeft(current);
|
|
42932
|
+
ev.preventDefault();
|
|
42933
|
+
break;
|
|
42934
|
+
case 'Enter':
|
|
42935
|
+
case ' ':
|
|
42936
|
+
// activate (toggle expand or select)
|
|
42937
|
+
current.click();
|
|
42938
|
+
ev.preventDefault();
|
|
42939
|
+
break;
|
|
42940
|
+
}
|
|
42941
|
+
}
|
|
42942
|
+
getVisibleItemElems() {
|
|
42943
|
+
if (!this.hierarchyElem)
|
|
42944
|
+
return [];
|
|
42945
|
+
const root = this.hierarchyElem.node();
|
|
42946
|
+
if (!root)
|
|
42947
|
+
return [];
|
|
42948
|
+
const items = Array.from(root.querySelectorAll('.tsi-hierarchyItem'));
|
|
42949
|
+
return items.filter(i => i.offsetParent !== null && getComputedStyle(i).display !== 'none');
|
|
42950
|
+
}
|
|
42951
|
+
focusItem(elem) {
|
|
42952
|
+
if (!this.hierarchyElem)
|
|
42953
|
+
return;
|
|
42954
|
+
const root = this.hierarchyElem.node();
|
|
42955
|
+
if (!root)
|
|
42956
|
+
return;
|
|
42957
|
+
const items = Array.from(root.querySelectorAll('.tsi-hierarchyItem'));
|
|
42958
|
+
items.forEach(i => i.setAttribute('tabindex', '-1'));
|
|
42959
|
+
elem.setAttribute('tabindex', '0');
|
|
42960
|
+
elem.focus();
|
|
42961
|
+
}
|
|
42962
|
+
focusNext(current) {
|
|
42963
|
+
const visible = this.getVisibleItemElems();
|
|
42964
|
+
const idx = visible.indexOf(current);
|
|
42965
|
+
if (idx >= 0 && idx < visible.length - 1) {
|
|
42966
|
+
this.focusItem(visible[idx + 1]);
|
|
42967
|
+
}
|
|
42968
|
+
}
|
|
42969
|
+
focusPrev(current) {
|
|
42970
|
+
const visible = this.getVisibleItemElems();
|
|
42971
|
+
const idx = visible.indexOf(current);
|
|
42972
|
+
if (idx > 0) {
|
|
42973
|
+
this.focusItem(visible[idx - 1]);
|
|
42974
|
+
}
|
|
42975
|
+
}
|
|
42976
|
+
handleArrowRight(current) {
|
|
42977
|
+
const caret = current.querySelector('.tsi-caret-icon');
|
|
42978
|
+
const expanded = current.getAttribute('aria-expanded') === 'true';
|
|
42979
|
+
if (caret && !expanded) {
|
|
42980
|
+
// expand
|
|
42981
|
+
current.click();
|
|
42982
|
+
return;
|
|
42983
|
+
}
|
|
42984
|
+
// if already expanded, move to first child
|
|
42985
|
+
if (caret && expanded) {
|
|
42986
|
+
const li = current.closest('li');
|
|
42987
|
+
const childLi = li?.querySelector('ul > li');
|
|
42988
|
+
const childItem = childLi?.querySelector('.tsi-hierarchyItem');
|
|
42989
|
+
if (childItem)
|
|
42990
|
+
this.focusItem(childItem);
|
|
42991
|
+
}
|
|
42992
|
+
}
|
|
42993
|
+
handleArrowLeft(current) {
|
|
42994
|
+
const caret = current.querySelector('.tsi-caret-icon');
|
|
42995
|
+
const expanded = current.getAttribute('aria-expanded') === 'true';
|
|
42996
|
+
if (caret && expanded) {
|
|
42997
|
+
// collapse
|
|
42998
|
+
current.click();
|
|
42999
|
+
return;
|
|
43000
|
+
}
|
|
43001
|
+
// move focus to parent
|
|
43002
|
+
const li = current.closest('li');
|
|
43003
|
+
const parentLi = li?.parentElement?.closest('li');
|
|
43004
|
+
const parentItem = parentLi?.querySelector('.tsi-hierarchyItem');
|
|
43005
|
+
if (parentItem)
|
|
43006
|
+
this.focusItem(parentItem);
|
|
43873
43007
|
}
|
|
43874
43008
|
// prepares the parameters for search request
|
|
43875
43009
|
requestPayload(hierarchy = null) {
|
|
@@ -43878,32 +43012,7 @@
|
|
|
43878
43012
|
}
|
|
43879
43013
|
// renders tree for both 'Navigate' and 'Filter' mode (with Hierarchy View option selected), locInTarget refers to the 'show more' element -either hierarchy or instance- within the target
|
|
43880
43014
|
renderTree(data, target) {
|
|
43881
|
-
|
|
43882
|
-
Object.keys(data).forEach(el => {
|
|
43883
|
-
let nodeNameToCheckIfExists = data[el] instanceof InstanceNode ? this.instanceNodeString(data[el]) : el;
|
|
43884
|
-
let li;
|
|
43885
|
-
if (list.selectAll(".tsi-name").nodes().find(e => e.innerText === nodeNameToCheckIfExists)) {
|
|
43886
|
-
li = null;
|
|
43887
|
-
}
|
|
43888
|
-
else {
|
|
43889
|
-
li = list.append('li').classed('tsi-leaf', data[el].isLeaf);
|
|
43890
|
-
//if the node is already selected, we want to highlight it
|
|
43891
|
-
if (this.selectedIds && this.selectedIds.includes(data[el].id)) {
|
|
43892
|
-
li.classed('tsi-selected', true);
|
|
43893
|
-
}
|
|
43894
|
-
}
|
|
43895
|
-
if (!li)
|
|
43896
|
-
return;
|
|
43897
|
-
li.attr("role", "none");
|
|
43898
|
-
let newListElem = this.createHierarchyItemElem(data[el], el);
|
|
43899
|
-
li.node().appendChild(newListElem.node());
|
|
43900
|
-
data[el].node = li;
|
|
43901
|
-
if (data[el].children) {
|
|
43902
|
-
data[el].isExpanded = true;
|
|
43903
|
-
data[el].node.classed('tsi-expanded', true);
|
|
43904
|
-
this.renderTree(data[el].children, data[el].node);
|
|
43905
|
-
}
|
|
43906
|
-
});
|
|
43015
|
+
TreeRenderer.render(this, data, target);
|
|
43907
43016
|
}
|
|
43908
43017
|
renderSearchBox() {
|
|
43909
43018
|
this.searchWrapperElem = this.hierarchyNavWrapper.append('div').classed('tsi-hierarchy-search', true);
|
|
@@ -43912,40 +43021,140 @@
|
|
|
43912
43021
|
let input = inputWrapper
|
|
43913
43022
|
.append("input")
|
|
43914
43023
|
.attr("class", "tsi-searchInput")
|
|
43915
|
-
.attr("aria-label", this.getString("Search
|
|
43916
|
-
.attr("aria-describedby", "tsi-search-desc")
|
|
43024
|
+
.attr("aria-label", this.getString("Search"))
|
|
43025
|
+
.attr("aria-describedby", "tsi-hierarchy-search-desc")
|
|
43917
43026
|
.attr("role", "combobox")
|
|
43918
43027
|
.attr("aria-owns", "tsi-search-results")
|
|
43919
43028
|
.attr("aria-expanded", "false")
|
|
43920
43029
|
.attr("aria-haspopup", "listbox")
|
|
43921
|
-
.attr("placeholder", this.getString("Search
|
|
43030
|
+
.attr("placeholder", this.getString("Search") + "...");
|
|
43031
|
+
// Add ARIA description for screen readers
|
|
43032
|
+
inputWrapper
|
|
43033
|
+
.append("span")
|
|
43034
|
+
.attr("id", "tsi-hierarchy-search-desc")
|
|
43035
|
+
.style("display", "none")
|
|
43036
|
+
.text(this.getString("Search suggestion instruction") || "Use arrow keys to navigate suggestions");
|
|
43037
|
+
// Add live region for search results info
|
|
43038
|
+
inputWrapper
|
|
43039
|
+
.append("div")
|
|
43040
|
+
.attr("class", "tsi-search-results-info")
|
|
43041
|
+
.attr("aria-live", "assertive");
|
|
43042
|
+
// Add clear button
|
|
43043
|
+
let clear = inputWrapper
|
|
43044
|
+
.append("div")
|
|
43045
|
+
.attr("class", "tsi-clear")
|
|
43046
|
+
.attr("tabindex", "0")
|
|
43047
|
+
.attr("role", "button")
|
|
43048
|
+
.attr("aria-label", "Clear Search")
|
|
43049
|
+
.on("click keydown", function (event) {
|
|
43050
|
+
if (Utils.isKeyDownAndNotEnter(event)) {
|
|
43051
|
+
return;
|
|
43052
|
+
}
|
|
43053
|
+
input.node().value = "";
|
|
43054
|
+
self.exitSearchMode();
|
|
43055
|
+
self.ap.close();
|
|
43056
|
+
select(this).classed("tsi-shown", false);
|
|
43057
|
+
});
|
|
43058
|
+
// Initialize Awesomplete for autocomplete (only if enabled)
|
|
43059
|
+
let Awesomplete = window.Awesomplete;
|
|
43060
|
+
if (this.autocompleteEnabled && Awesomplete) {
|
|
43061
|
+
this.ap = new Awesomplete(input.node(), {
|
|
43062
|
+
minChars: 1,
|
|
43063
|
+
maxItems: 10,
|
|
43064
|
+
autoFirst: true
|
|
43065
|
+
});
|
|
43066
|
+
}
|
|
43067
|
+
else {
|
|
43068
|
+
// Create a dummy object if autocomplete is disabled
|
|
43069
|
+
this.ap = {
|
|
43070
|
+
list: [],
|
|
43071
|
+
close: () => { },
|
|
43072
|
+
evaluate: () => { }
|
|
43073
|
+
};
|
|
43074
|
+
}
|
|
43922
43075
|
let self = this;
|
|
43076
|
+
let noSuggest = false;
|
|
43077
|
+
let justAwesompleted = false;
|
|
43078
|
+
// Handle autocomplete selection (only if enabled)
|
|
43079
|
+
if (this.autocompleteEnabled) {
|
|
43080
|
+
input.node().addEventListener("awesomplete-selectcomplete", (event) => {
|
|
43081
|
+
noSuggest = true;
|
|
43082
|
+
const selectedValue = event.text.value;
|
|
43083
|
+
// Trigger search with selected value
|
|
43084
|
+
self.performDeepSearch(selectedValue);
|
|
43085
|
+
justAwesompleted = true;
|
|
43086
|
+
});
|
|
43087
|
+
}
|
|
43923
43088
|
input.on("keydown", (event) => {
|
|
43089
|
+
// Handle ESC key to clear the search box
|
|
43090
|
+
if (event.key === 'Escape') {
|
|
43091
|
+
const inputElement = event.target;
|
|
43092
|
+
inputElement.value = '';
|
|
43093
|
+
// Trigger input event to clear search results
|
|
43094
|
+
self.exitSearchMode();
|
|
43095
|
+
self.ap.close();
|
|
43096
|
+
clear.classed("tsi-shown", false);
|
|
43097
|
+
return;
|
|
43098
|
+
}
|
|
43924
43099
|
this.chartOptions.onKeydown(event, this.ap);
|
|
43925
43100
|
});
|
|
43926
|
-
|
|
43101
|
+
input.node().addEventListener("keyup", function (event) {
|
|
43102
|
+
if (justAwesompleted) {
|
|
43103
|
+
justAwesompleted = false;
|
|
43104
|
+
return;
|
|
43105
|
+
}
|
|
43106
|
+
let key = event.which || event.keyCode;
|
|
43107
|
+
if (key === 13) {
|
|
43108
|
+
noSuggest = true;
|
|
43109
|
+
}
|
|
43110
|
+
});
|
|
43111
|
+
// Debounced input handler to reduce work while typing
|
|
43927
43112
|
input.on("input", function (event) {
|
|
43928
|
-
|
|
43929
|
-
|
|
43930
|
-
|
|
43931
|
-
self.
|
|
43932
|
-
self.
|
|
43113
|
+
const val = event.target.value;
|
|
43114
|
+
// always clear existing timer
|
|
43115
|
+
if (self.debounceTimer) {
|
|
43116
|
+
clearTimeout(self.debounceTimer);
|
|
43117
|
+
self.debounceTimer = null;
|
|
43118
|
+
}
|
|
43119
|
+
// Show/hide clear button
|
|
43120
|
+
clear.classed("tsi-shown", val.length > 0);
|
|
43121
|
+
if (!val || val.length === 0) {
|
|
43122
|
+
// Exit search mode and restore navigation view
|
|
43123
|
+
self.exitSearchMode();
|
|
43124
|
+
self.ap.close();
|
|
43125
|
+
return;
|
|
43126
|
+
}
|
|
43127
|
+
// Populate autocomplete suggestions with instance leaves (only if enabled)
|
|
43128
|
+
if (self.autocompleteEnabled && !noSuggest && val.length >= 1) {
|
|
43129
|
+
self.fetchAutocompleteSuggestions(val);
|
|
43933
43130
|
}
|
|
43934
43131
|
else {
|
|
43935
|
-
|
|
43936
|
-
self.filterTree(searchText);
|
|
43132
|
+
self.ap.close();
|
|
43937
43133
|
}
|
|
43134
|
+
// Use deep search for comprehensive results
|
|
43135
|
+
self.debounceTimer = setTimeout(() => {
|
|
43136
|
+
self.performDeepSearch(val);
|
|
43137
|
+
}, self.debounceDelay);
|
|
43138
|
+
noSuggest = false;
|
|
43938
43139
|
});
|
|
43939
43140
|
}
|
|
43940
43141
|
async pathSearchAndRenderResult({ search: { payload, bubbleUpReject = false }, render: { target, locInTarget = null } }) {
|
|
43142
|
+
const requestId = ++this.requestCounter;
|
|
43143
|
+
this.latestRequestId = requestId;
|
|
43941
43144
|
try {
|
|
43942
43145
|
const result = await this.searchFunction(payload);
|
|
43146
|
+
if (requestId !== this.latestRequestId) {
|
|
43147
|
+
return;
|
|
43148
|
+
}
|
|
43943
43149
|
if (result.error) {
|
|
43944
43150
|
throw result.error;
|
|
43945
43151
|
}
|
|
43946
|
-
this.renderSearchResult(result, payload, target);
|
|
43152
|
+
await this.renderSearchResult(result, payload, target);
|
|
43947
43153
|
}
|
|
43948
43154
|
catch (err) {
|
|
43155
|
+
if (requestId !== this.latestRequestId) {
|
|
43156
|
+
return;
|
|
43157
|
+
}
|
|
43949
43158
|
this.chartOptions.onError("Error in hierarchy navigation", "Failed to complete search", err instanceof XMLHttpRequest ? err : null);
|
|
43950
43159
|
if (bubbleUpReject) {
|
|
43951
43160
|
throw err;
|
|
@@ -43953,11 +43162,18 @@
|
|
|
43953
43162
|
}
|
|
43954
43163
|
}
|
|
43955
43164
|
filterTree(searchText) {
|
|
43956
|
-
|
|
43957
|
-
|
|
43165
|
+
const nodes = this.hierarchyElem.selectAll('ul').nodes();
|
|
43166
|
+
if (!nodes || !nodes.length)
|
|
43167
|
+
return;
|
|
43168
|
+
const tree = nodes[0];
|
|
43169
|
+
if (!tree)
|
|
43170
|
+
return;
|
|
43171
|
+
const list = tree.querySelectorAll('li');
|
|
43172
|
+
const needle = searchText.toLowerCase();
|
|
43958
43173
|
list.forEach((li) => {
|
|
43959
|
-
|
|
43960
|
-
|
|
43174
|
+
const attrName = li.getAttribute('data-display-name');
|
|
43175
|
+
let name = attrName && attrName.length ? attrName : (li.querySelector('.tsi-name')?.textContent || '');
|
|
43176
|
+
if (name.toLowerCase().includes(needle)) {
|
|
43961
43177
|
li.style.display = 'block';
|
|
43962
43178
|
}
|
|
43963
43179
|
else {
|
|
@@ -43965,11 +43181,300 @@
|
|
|
43965
43181
|
}
|
|
43966
43182
|
});
|
|
43967
43183
|
}
|
|
43184
|
+
// Fetch autocomplete suggestions for instances (leaves)
|
|
43185
|
+
async fetchAutocompleteSuggestions(searchText) {
|
|
43186
|
+
if (!searchText || searchText.length < 1) {
|
|
43187
|
+
this.ap.list = [];
|
|
43188
|
+
return;
|
|
43189
|
+
}
|
|
43190
|
+
try {
|
|
43191
|
+
// Call server search to get instance suggestions
|
|
43192
|
+
const payload = {
|
|
43193
|
+
path: this.path,
|
|
43194
|
+
searchTerm: searchText,
|
|
43195
|
+
recursive: true,
|
|
43196
|
+
includeInstances: true,
|
|
43197
|
+
// Limit results for autocomplete
|
|
43198
|
+
maxResults: 10
|
|
43199
|
+
};
|
|
43200
|
+
const results = await this.searchFunction(payload);
|
|
43201
|
+
if (results.error) {
|
|
43202
|
+
this.ap.list = [];
|
|
43203
|
+
return;
|
|
43204
|
+
}
|
|
43205
|
+
// Extract instance names for autocomplete suggestions
|
|
43206
|
+
const suggestions = [];
|
|
43207
|
+
if (results.instances?.hits) {
|
|
43208
|
+
results.instances.hits.forEach((i) => {
|
|
43209
|
+
const displayName = this.instanceNodeStringToDisplay(i);
|
|
43210
|
+
const pathStr = i.hierarchyPath && i.hierarchyPath.length > 0
|
|
43211
|
+
? i.hierarchyPath.join(' > ') + ' > '
|
|
43212
|
+
: '';
|
|
43213
|
+
suggestions.push({
|
|
43214
|
+
label: pathStr + displayName,
|
|
43215
|
+
value: displayName
|
|
43216
|
+
});
|
|
43217
|
+
});
|
|
43218
|
+
}
|
|
43219
|
+
// Update Awesomplete list
|
|
43220
|
+
this.ap.list = suggestions;
|
|
43221
|
+
}
|
|
43222
|
+
catch (err) {
|
|
43223
|
+
// Silently fail for autocomplete - don't interrupt user experience
|
|
43224
|
+
this.ap.list = [];
|
|
43225
|
+
}
|
|
43226
|
+
}
|
|
43227
|
+
// Perform deep search across entire hierarchy using server-side search
|
|
43228
|
+
async performDeepSearch(searchText) {
|
|
43229
|
+
if (!searchText || searchText.length < 2) {
|
|
43230
|
+
this.exitSearchMode();
|
|
43231
|
+
return;
|
|
43232
|
+
}
|
|
43233
|
+
this.isSearchMode = true;
|
|
43234
|
+
const requestId = ++this.requestCounter;
|
|
43235
|
+
this.latestRequestId = requestId;
|
|
43236
|
+
try {
|
|
43237
|
+
// Call server search with recursive flag
|
|
43238
|
+
const payload = {
|
|
43239
|
+
path: this.path,
|
|
43240
|
+
searchTerm: searchText,
|
|
43241
|
+
recursive: true, // Search entire subtree
|
|
43242
|
+
includeInstances: true
|
|
43243
|
+
};
|
|
43244
|
+
const results = await this.searchFunction(payload);
|
|
43245
|
+
if (requestId !== this.latestRequestId)
|
|
43246
|
+
return; // Stale request
|
|
43247
|
+
if (results.error) {
|
|
43248
|
+
throw results.error;
|
|
43249
|
+
}
|
|
43250
|
+
// Render search results in flat list view
|
|
43251
|
+
this.renderSearchResults(results, searchText);
|
|
43252
|
+
}
|
|
43253
|
+
catch (err) {
|
|
43254
|
+
if (requestId !== this.latestRequestId)
|
|
43255
|
+
return;
|
|
43256
|
+
this.chartOptions.onError("Search failed", "Unable to search hierarchy", err instanceof XMLHttpRequest ? err : null);
|
|
43257
|
+
}
|
|
43258
|
+
}
|
|
43259
|
+
// Render search results with breadcrumb paths
|
|
43260
|
+
renderSearchResults(results, searchText) {
|
|
43261
|
+
this.hierarchyElem.selectAll('*').remove();
|
|
43262
|
+
const flatResults = [];
|
|
43263
|
+
// Flatten hierarchy results with full paths
|
|
43264
|
+
if (results.hierarchyNodes?.hits) {
|
|
43265
|
+
results.hierarchyNodes.hits.forEach((h) => {
|
|
43266
|
+
flatResults.push({
|
|
43267
|
+
type: 'hierarchy',
|
|
43268
|
+
name: h.name,
|
|
43269
|
+
path: h.path || [],
|
|
43270
|
+
id: h.id,
|
|
43271
|
+
cumulativeInstanceCount: h.cumulativeInstanceCount,
|
|
43272
|
+
highlightedName: this.highlightMatch(h.name, searchText),
|
|
43273
|
+
node: h
|
|
43274
|
+
});
|
|
43275
|
+
});
|
|
43276
|
+
}
|
|
43277
|
+
// Flatten instance results with full paths
|
|
43278
|
+
if (results.instances?.hits) {
|
|
43279
|
+
results.instances.hits.forEach((i) => {
|
|
43280
|
+
const displayName = this.instanceNodeStringToDisplay(i);
|
|
43281
|
+
flatResults.push({
|
|
43282
|
+
type: 'instance',
|
|
43283
|
+
name: i.name,
|
|
43284
|
+
path: i.hierarchyPath || [],
|
|
43285
|
+
id: i.id,
|
|
43286
|
+
timeSeriesId: i.timeSeriesId,
|
|
43287
|
+
description: i.description,
|
|
43288
|
+
highlightedName: this.highlightMatch(displayName, searchText),
|
|
43289
|
+
node: i
|
|
43290
|
+
});
|
|
43291
|
+
});
|
|
43292
|
+
}
|
|
43293
|
+
// Render flat list with breadcrumbs
|
|
43294
|
+
const searchList = this.hierarchyElem
|
|
43295
|
+
.append('div')
|
|
43296
|
+
.classed('tsi-search-results', true);
|
|
43297
|
+
if (flatResults.length === 0) {
|
|
43298
|
+
searchList.append('div')
|
|
43299
|
+
.classed('tsi-noResults', true)
|
|
43300
|
+
.text(this.getString('No results'));
|
|
43301
|
+
return;
|
|
43302
|
+
}
|
|
43303
|
+
searchList.append('div')
|
|
43304
|
+
.classed('tsi-search-results-header', true)
|
|
43305
|
+
.html(`<strong>${flatResults.length}</strong> ${this.getString('results found') || 'results found'}`);
|
|
43306
|
+
const resultItems = searchList.selectAll('.tsi-search-result-item')
|
|
43307
|
+
.data(flatResults)
|
|
43308
|
+
.enter()
|
|
43309
|
+
.append('div')
|
|
43310
|
+
.classed('tsi-search-result-item', true)
|
|
43311
|
+
.attr('tabindex', '0')
|
|
43312
|
+
.attr('role', 'option')
|
|
43313
|
+
.attr('aria-label', (d) => {
|
|
43314
|
+
const pathStr = d.path.length > 0 ? d.path.join(' > ') + ' > ' : '';
|
|
43315
|
+
return pathStr + d.name;
|
|
43316
|
+
});
|
|
43317
|
+
const self = this;
|
|
43318
|
+
resultItems.each(function (d) {
|
|
43319
|
+
const item = select(this);
|
|
43320
|
+
// Breadcrumb path
|
|
43321
|
+
if (d.path.length > 0) {
|
|
43322
|
+
item.append('div')
|
|
43323
|
+
.classed('tsi-search-breadcrumb', true)
|
|
43324
|
+
.text(d.path.join(' > '));
|
|
43325
|
+
}
|
|
43326
|
+
// Highlighted name
|
|
43327
|
+
item.append('div')
|
|
43328
|
+
.classed('tsi-search-result-name', true)
|
|
43329
|
+
.html(d.highlightedName);
|
|
43330
|
+
// Instance description or count
|
|
43331
|
+
if (d.type === 'instance' && d.description) {
|
|
43332
|
+
item.append('div')
|
|
43333
|
+
.classed('tsi-search-result-description', true)
|
|
43334
|
+
.text(d.description);
|
|
43335
|
+
}
|
|
43336
|
+
else if (d.type === 'hierarchy') {
|
|
43337
|
+
item.append('div')
|
|
43338
|
+
.classed('tsi-search-result-count', true)
|
|
43339
|
+
.text(`${d.cumulativeInstanceCount || 0} instances`);
|
|
43340
|
+
}
|
|
43341
|
+
});
|
|
43342
|
+
// Click handlers
|
|
43343
|
+
resultItems.on('click keydown', function (event, d) {
|
|
43344
|
+
if (Utils.isKeyDownAndNotEnter(event))
|
|
43345
|
+
return;
|
|
43346
|
+
if (d.type === 'instance') {
|
|
43347
|
+
// Handle instance selection
|
|
43348
|
+
if (self.chartOptions.onInstanceClick) {
|
|
43349
|
+
const inst = new InstanceNode(d.timeSeriesId, d.name, d.path.length, d.id, d.description);
|
|
43350
|
+
// Update selection state
|
|
43351
|
+
if (self.selectedIds && self.selectedIds.includes(d.id)) {
|
|
43352
|
+
self.selectedIds = self.selectedIds.filter(id => id !== d.id);
|
|
43353
|
+
select(this).classed('tsi-selected', false);
|
|
43354
|
+
}
|
|
43355
|
+
else {
|
|
43356
|
+
self.selectedIds.push(d.id);
|
|
43357
|
+
select(this).classed('tsi-selected', true);
|
|
43358
|
+
}
|
|
43359
|
+
self.chartOptions.onInstanceClick(inst);
|
|
43360
|
+
}
|
|
43361
|
+
}
|
|
43362
|
+
else {
|
|
43363
|
+
// Navigate to hierarchy node - exit search and expand to that path
|
|
43364
|
+
self.navigateToPath(d.path);
|
|
43365
|
+
}
|
|
43366
|
+
});
|
|
43367
|
+
// Apply selection state to already-selected instances
|
|
43368
|
+
resultItems.each(function (d) {
|
|
43369
|
+
if (d.type === 'instance' && self.selectedIds && self.selectedIds.includes(d.id)) {
|
|
43370
|
+
select(this).classed('tsi-selected', true);
|
|
43371
|
+
}
|
|
43372
|
+
});
|
|
43373
|
+
}
|
|
43374
|
+
// Exit search mode and restore tree
|
|
43375
|
+
exitSearchMode() {
|
|
43376
|
+
this.isSearchMode = false;
|
|
43377
|
+
this.hierarchyElem.selectAll('*').remove();
|
|
43378
|
+
this.pathSearchAndRenderResult({
|
|
43379
|
+
search: { payload: this.requestPayload() },
|
|
43380
|
+
render: { target: this.hierarchyElem }
|
|
43381
|
+
});
|
|
43382
|
+
}
|
|
43383
|
+
// Navigate to a specific path in the hierarchy
|
|
43384
|
+
async navigateToPath(targetPath) {
|
|
43385
|
+
this.exitSearchMode();
|
|
43386
|
+
// For now, just exit search mode and return to root
|
|
43387
|
+
// In a more advanced implementation, this would progressively
|
|
43388
|
+
// expand nodes along the path to reveal the target
|
|
43389
|
+
// This would require waiting for each level to load before expanding the next
|
|
43390
|
+
}
|
|
43391
|
+
// Pre-compute which paths need to be auto-expanded for preselected instances
|
|
43392
|
+
async computePathsToAutoExpand(instanceIds) {
|
|
43393
|
+
if (!instanceIds || instanceIds.length === 0) {
|
|
43394
|
+
return;
|
|
43395
|
+
}
|
|
43396
|
+
// console.log('[HierarchyNavigation] Computing paths to auto-expand for:', instanceIds);
|
|
43397
|
+
try {
|
|
43398
|
+
this.pathsToAutoExpand.clear();
|
|
43399
|
+
for (const instanceId of instanceIds) {
|
|
43400
|
+
// Search for this specific instance
|
|
43401
|
+
const result = await this.searchFunction({
|
|
43402
|
+
path: this.path,
|
|
43403
|
+
searchTerm: instanceId,
|
|
43404
|
+
recursive: true,
|
|
43405
|
+
includeInstances: true
|
|
43406
|
+
});
|
|
43407
|
+
if (result?.instances?.hits) {
|
|
43408
|
+
for (const instance of result.instances.hits) {
|
|
43409
|
+
// Match by ID
|
|
43410
|
+
if (instance.id === instanceId ||
|
|
43411
|
+
(instance.id && instance.id.includes(instanceId))) {
|
|
43412
|
+
if (instance.hierarchyPath && instance.hierarchyPath.length > 0) {
|
|
43413
|
+
// Add all parent paths that need to be expanded
|
|
43414
|
+
const hierarchyPath = instance.hierarchyPath;
|
|
43415
|
+
for (let i = 1; i <= hierarchyPath.length; i++) {
|
|
43416
|
+
const pathArray = hierarchyPath.slice(0, i);
|
|
43417
|
+
const pathKey = pathArray.join('/');
|
|
43418
|
+
this.pathsToAutoExpand.add(pathKey);
|
|
43419
|
+
}
|
|
43420
|
+
}
|
|
43421
|
+
}
|
|
43422
|
+
}
|
|
43423
|
+
}
|
|
43424
|
+
}
|
|
43425
|
+
// console.log('[HierarchyNavigation] Paths to auto-expand:', Array.from(this.pathsToAutoExpand));
|
|
43426
|
+
}
|
|
43427
|
+
catch (err) {
|
|
43428
|
+
console.warn('Failed to compute paths to auto-expand:', err);
|
|
43429
|
+
}
|
|
43430
|
+
}
|
|
43431
|
+
// Check if a path should be auto-expanded
|
|
43432
|
+
shouldAutoExpand(pathArray) {
|
|
43433
|
+
if (this.pathsToAutoExpand.size === 0) {
|
|
43434
|
+
return false;
|
|
43435
|
+
}
|
|
43436
|
+
const pathKey = pathArray.join('/');
|
|
43437
|
+
return this.pathsToAutoExpand.has(pathKey);
|
|
43438
|
+
}
|
|
43439
|
+
// Auto-expand a node by triggering its expand function
|
|
43440
|
+
async autoExpandNode(node) {
|
|
43441
|
+
if (!node || !node.expand || !node.node) {
|
|
43442
|
+
return;
|
|
43443
|
+
}
|
|
43444
|
+
try {
|
|
43445
|
+
// Wait for the DOM node to be available
|
|
43446
|
+
await new Promise(resolve => setTimeout(resolve, 10));
|
|
43447
|
+
// Mark as expanded visually
|
|
43448
|
+
node.node.classed('tsi-expanded', true);
|
|
43449
|
+
// Call the expand function to load children
|
|
43450
|
+
await node.expand();
|
|
43451
|
+
// console.log(`[HierarchyNavigation] Auto-expanded node: ${node.path.join('/')}`);
|
|
43452
|
+
}
|
|
43453
|
+
catch (err) {
|
|
43454
|
+
console.warn(`Failed to auto-expand node ${node.path.join('/')}:`, err);
|
|
43455
|
+
}
|
|
43456
|
+
}
|
|
43457
|
+
// Highlight search term in text
|
|
43458
|
+
highlightMatch(text, searchTerm) {
|
|
43459
|
+
if (!text)
|
|
43460
|
+
return '';
|
|
43461
|
+
const escapedTerm = searchTerm.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
43462
|
+
const regex = new RegExp(`(${escapedTerm})`, 'gi');
|
|
43463
|
+
return text.replace(regex, '<mark>$1</mark>');
|
|
43464
|
+
}
|
|
43968
43465
|
// creates in-depth data object using the server response for hierarchyNodes to show in the tree all expanded, considering UntilChildren
|
|
43969
43466
|
fillDataRecursively(hierarchyNodes, payload, payloadForContinuation = null) {
|
|
43970
43467
|
let data = {};
|
|
43971
43468
|
hierarchyNodes.hits.forEach((h) => {
|
|
43972
43469
|
let hierarchy = new HierarchyNode(h.name, payload.path, payload.path.length - this.path.length, h.cumulativeInstanceCount, h.id);
|
|
43470
|
+
// cache display name on node for client-side filtering
|
|
43471
|
+
hierarchy.displayName = h.name || '';
|
|
43472
|
+
// Check if this path should be auto-expanded
|
|
43473
|
+
const shouldExpand = this.shouldAutoExpand(hierarchy.path);
|
|
43474
|
+
if (shouldExpand) {
|
|
43475
|
+
hierarchy.isExpanded = true;
|
|
43476
|
+
//console.log(`[HierarchyNavigation] Auto-expanding node: ${hierarchy.path.join('/')}`);
|
|
43477
|
+
}
|
|
43973
43478
|
hierarchy.expand = () => {
|
|
43974
43479
|
hierarchy.isExpanded = true;
|
|
43975
43480
|
hierarchy.node.classed('tsi-expanded', true);
|
|
@@ -43993,7 +43498,7 @@
|
|
|
43993
43498
|
.attr('style', `padding-left: ${hORi.isLeaf ? hORi.level * 18 + 20 : (hORi.level + 1) * 18 + 20}px`)
|
|
43994
43499
|
.attr('tabindex', 0)
|
|
43995
43500
|
//.attr('arialabel', isHierarchyNode ? key : Utils.getTimeSeriesIdString(hORi))
|
|
43996
|
-
.attr('
|
|
43501
|
+
.attr('aria-label', isHierarchyNode ? key : self.getAriaLabel(hORi))
|
|
43997
43502
|
.attr('title', isHierarchyNode ? key : self.getAriaLabel(hORi))
|
|
43998
43503
|
.attr("role", "treeitem").attr('aria-expanded', hORi.isExpanded)
|
|
43999
43504
|
.on('click keydown', async function (event) {
|
|
@@ -44045,6 +43550,8 @@
|
|
|
44045
43550
|
return hORi.description || hORi.name || hORi.id || Utils.getTimeSeriesIdString(hORi);
|
|
44046
43551
|
}
|
|
44047
43552
|
}
|
|
43553
|
+
// TreeRenderer has been moved to its own module: ./TreeRenderer
|
|
43554
|
+
// The rendering logic was extracted to reduce file size and improve testability.
|
|
44048
43555
|
class HierarchyNode {
|
|
44049
43556
|
constructor(name, parentPath, level, cumulativeInstanceCount = null, id = null) {
|
|
44050
43557
|
this.name = name;
|
|
@@ -44289,6 +43796,7 @@
|
|
|
44289
43796
|
class DateTimeButtonSingle extends DateTimeButton {
|
|
44290
43797
|
constructor(renderTarget) {
|
|
44291
43798
|
super(renderTarget);
|
|
43799
|
+
this.clickOutsideHandler = null;
|
|
44292
43800
|
this.sDTPOnSet = (millis = null) => {
|
|
44293
43801
|
if (millis !== null) {
|
|
44294
43802
|
this.dateTimeButton.text(this.buttonDateTimeFormat(millis));
|
|
@@ -44301,6 +43809,32 @@
|
|
|
44301
43809
|
closeSDTP() {
|
|
44302
43810
|
this.dateTimePickerContainer.style("display", "none");
|
|
44303
43811
|
this.dateTimeButton.node().focus();
|
|
43812
|
+
this.removeClickOutsideHandler();
|
|
43813
|
+
}
|
|
43814
|
+
removeClickOutsideHandler() {
|
|
43815
|
+
if (this.clickOutsideHandler) {
|
|
43816
|
+
document.removeEventListener('click', this.clickOutsideHandler);
|
|
43817
|
+
this.clickOutsideHandler = null;
|
|
43818
|
+
}
|
|
43819
|
+
}
|
|
43820
|
+
setupClickOutsideHandler() {
|
|
43821
|
+
// Remove any existing handler first
|
|
43822
|
+
this.removeClickOutsideHandler();
|
|
43823
|
+
// Add handler after a small delay to prevent the opening click from immediately closing the picker
|
|
43824
|
+
setTimeout(() => {
|
|
43825
|
+
this.clickOutsideHandler = (event) => {
|
|
43826
|
+
const pickerElement = this.dateTimePickerContainer.node();
|
|
43827
|
+
const buttonElement = this.dateTimeButton.node();
|
|
43828
|
+
const target = event.target;
|
|
43829
|
+
// Check if click is outside both the picker and the button
|
|
43830
|
+
if (pickerElement && buttonElement &&
|
|
43831
|
+
!pickerElement.contains(target) &&
|
|
43832
|
+
!buttonElement.contains(target)) {
|
|
43833
|
+
this.closeSDTP();
|
|
43834
|
+
}
|
|
43835
|
+
};
|
|
43836
|
+
document.addEventListener('click', this.clickOutsideHandler);
|
|
43837
|
+
}, 0);
|
|
44304
43838
|
}
|
|
44305
43839
|
render(chartOptions = {}, minMillis, maxMillis, selectedMillis = null, onSet = null) {
|
|
44306
43840
|
super.render(chartOptions, minMillis, maxMillis, onSet);
|
|
@@ -44310,12 +43844,11 @@
|
|
|
44310
43844
|
if (!this.dateTimePicker) {
|
|
44311
43845
|
this.dateTimePicker = new SingleDateTimePicker(this.dateTimePickerContainer.node());
|
|
44312
43846
|
}
|
|
44313
|
-
let targetElement = select(this.renderTarget);
|
|
44314
|
-
(targetElement.select(".tsi-dateTimePickerContainer")).selectAll("*");
|
|
44315
43847
|
this.dateTimeButton.on("click", () => {
|
|
44316
43848
|
this.chartOptions.dTPIsModal = true;
|
|
44317
43849
|
this.dateTimePickerContainer.style("display", "block");
|
|
44318
43850
|
this.dateTimePicker.render(this.chartOptions, this.minMillis, this.maxMillis, this.selectedMillis, this.sDTPOnSet);
|
|
43851
|
+
this.setupClickOutsideHandler();
|
|
44319
43852
|
});
|
|
44320
43853
|
}
|
|
44321
43854
|
}
|
|
@@ -44613,8 +44146,16 @@
|
|
|
44613
44146
|
class PlaybackControls extends Component {
|
|
44614
44147
|
constructor(renderTarget, initialTimeStamp = null) {
|
|
44615
44148
|
super(renderTarget);
|
|
44616
|
-
this.
|
|
44617
|
-
this.
|
|
44149
|
+
this.playbackInterval = null;
|
|
44150
|
+
this.playButton = null;
|
|
44151
|
+
this.handleElement = null;
|
|
44152
|
+
this.controlsContainer = null;
|
|
44153
|
+
this.track = null;
|
|
44154
|
+
this.selectTimeStampCallback = null;
|
|
44155
|
+
this.wasPlayingWhenDragStarted = false;
|
|
44156
|
+
this.rafId = null;
|
|
44157
|
+
this.handleRadius = PlaybackControls.CONSTANTS.HANDLE_RADIUS;
|
|
44158
|
+
this.minimumPlaybackInterval = PlaybackControls.CONSTANTS.MINIMUM_PLAYBACK_INTERVAL_MS;
|
|
44618
44159
|
this.playbackInterval = null;
|
|
44619
44160
|
this.selectedTimeStamp = initialTimeStamp;
|
|
44620
44161
|
}
|
|
@@ -44622,6 +44163,21 @@
|
|
|
44622
44163
|
return this.selectedTimeStamp;
|
|
44623
44164
|
}
|
|
44624
44165
|
render(start, end, onSelectTimeStamp, options, playbackSettings) {
|
|
44166
|
+
// Validate inputs
|
|
44167
|
+
if (!(start instanceof Date) || !(end instanceof Date)) {
|
|
44168
|
+
throw new TypeError('start and end must be Date objects');
|
|
44169
|
+
}
|
|
44170
|
+
if (start >= end) {
|
|
44171
|
+
throw new RangeError('start must be before end');
|
|
44172
|
+
}
|
|
44173
|
+
if (!onSelectTimeStamp || typeof onSelectTimeStamp !== 'function') {
|
|
44174
|
+
throw new TypeError('onSelectTimeStamp must be a function');
|
|
44175
|
+
}
|
|
44176
|
+
// Clean up any pending animation frames before re-rendering
|
|
44177
|
+
if (this.rafId !== null) {
|
|
44178
|
+
cancelAnimationFrame(this.rafId);
|
|
44179
|
+
this.rafId = null;
|
|
44180
|
+
}
|
|
44625
44181
|
this.end = end;
|
|
44626
44182
|
this.selectTimeStampCallback = onSelectTimeStamp;
|
|
44627
44183
|
this.chartOptions.setOptions(options);
|
|
@@ -44683,6 +44239,9 @@
|
|
|
44683
44239
|
this.playButton = this.controlsContainer.append('button')
|
|
44684
44240
|
.classed('tsi-play-button', this.playbackInterval === null)
|
|
44685
44241
|
.classed('tsi-pause-button', this.playbackInterval !== null)
|
|
44242
|
+
// Accessibility attributes
|
|
44243
|
+
.attr('aria-label', 'Play/Pause playback')
|
|
44244
|
+
.attr('title', 'Play/Pause playback')
|
|
44686
44245
|
.on('click', () => {
|
|
44687
44246
|
if (this.playbackInterval === null) {
|
|
44688
44247
|
this.play();
|
|
@@ -44734,6 +44293,27 @@
|
|
|
44734
44293
|
this.updateSelection(handlePosition, this.selectedTimeStamp);
|
|
44735
44294
|
this.selectTimeStampCallback(this.selectedTimeStamp);
|
|
44736
44295
|
}
|
|
44296
|
+
/**
|
|
44297
|
+
* Cleanup resources to prevent memory leaks
|
|
44298
|
+
*/
|
|
44299
|
+
destroy() {
|
|
44300
|
+
this.pause();
|
|
44301
|
+
// Cancel any pending animation frames
|
|
44302
|
+
if (this.rafId !== null) {
|
|
44303
|
+
cancelAnimationFrame(this.rafId);
|
|
44304
|
+
this.rafId = null;
|
|
44305
|
+
}
|
|
44306
|
+
// Remove event listeners
|
|
44307
|
+
if (this.controlsContainer) {
|
|
44308
|
+
this.controlsContainer.selectAll('*').on('.', null);
|
|
44309
|
+
}
|
|
44310
|
+
// Clear DOM references
|
|
44311
|
+
this.playButton = null;
|
|
44312
|
+
this.handleElement = null;
|
|
44313
|
+
this.controlsContainer = null;
|
|
44314
|
+
this.track = null;
|
|
44315
|
+
this.selectTimeStampCallback = null;
|
|
44316
|
+
}
|
|
44737
44317
|
clamp(number, min, max) {
|
|
44738
44318
|
let clamped = Math.max(number, min);
|
|
44739
44319
|
return Math.min(clamped, max);
|
|
@@ -44742,9 +44322,17 @@
|
|
|
44742
44322
|
this.wasPlayingWhenDragStarted = this.wasPlayingWhenDragStarted ||
|
|
44743
44323
|
(this.playbackInterval !== null);
|
|
44744
44324
|
this.pause();
|
|
44745
|
-
|
|
44746
|
-
|
|
44747
|
-
this.
|
|
44325
|
+
// Use requestAnimationFrame to batch DOM updates for better performance
|
|
44326
|
+
// Cancel any pending animation frame to prevent stacking updates
|
|
44327
|
+
if (this.rafId !== null) {
|
|
44328
|
+
cancelAnimationFrame(this.rafId);
|
|
44329
|
+
}
|
|
44330
|
+
this.rafId = requestAnimationFrame(() => {
|
|
44331
|
+
const handlePosition = this.clamp(positionX, 0, this.trackWidth);
|
|
44332
|
+
this.selectedTimeStamp = this.timeStampToPosition.invert(handlePosition);
|
|
44333
|
+
this.updateSelection(handlePosition, this.selectedTimeStamp);
|
|
44334
|
+
this.rafId = null;
|
|
44335
|
+
});
|
|
44748
44336
|
}
|
|
44749
44337
|
onDragEnd() {
|
|
44750
44338
|
this.selectTimeStampCallback(this.selectedTimeStamp);
|
|
@@ -44767,6 +44355,12 @@
|
|
|
44767
44355
|
.text(this.timeFormatter(timeStamp));
|
|
44768
44356
|
}
|
|
44769
44357
|
}
|
|
44358
|
+
PlaybackControls.CONSTANTS = {
|
|
44359
|
+
HANDLE_RADIUS: 7,
|
|
44360
|
+
MINIMUM_PLAYBACK_INTERVAL_MS: 1000,
|
|
44361
|
+
HANDLE_PADDING: 8,
|
|
44362
|
+
AXIS_OFFSET: 6,
|
|
44363
|
+
};
|
|
44770
44364
|
class TimeAxis extends TemporalXAxisComponent {
|
|
44771
44365
|
constructor(renderTarget) {
|
|
44772
44366
|
super(renderTarget);
|