Add css class to noUiSlider handle on event - javascript

By default on the start event noUiSlider adds an active class to the active handle but removes it when the event has ended. I want a way to show the user that the handle has been dragged already so changing the colour via a css class would be ideal.
I cannot tell which of the handles has been dragged from the data it provides.
Here is my function which initiates noUISlider
setRangeSlider(e) {
const min = 0;
const max = 1000000;
noUiSlider.cssClasses.target += ' c-range-slider';
const options = {
start: [min || 0, max || this.maxAvailablePrice.id],
step: 50000,
tooltips: true,
connect: true,
range: {
min,
max,
},
format: {
to(value) {
return formatCurrency([value]);
},
from(value) {
return stripFormatting(value);
},
},
};
this.slider = noUiSlider.create(e, options);
this.slider.on('start', (values) => {
console.log('SearchFiltersPriceComponent -> setRangeSlider -> this.slider', this.slider);
});
}
When I console out this.slider from the start event it prints out all sorts of useful information but I cannot find which handle has just been dragged and how to target that handle to add a class to it.

this.slider.target will return the slider element and handle parameter in event callback function will return the index of the handle that has been dragged. so these two can be used together to locate a particular handle. see the code for example
setRangeSlider(e) {
const min = 0;
const max = 1000000;
noUiSlider.cssClasses.target += ' c-range-slider';
const options = {
start: [min || 0, max || this.maxAvailablePrice.id],
step: 50000,
tooltips: true,
connect: true,
range: {
min,
max,
},
format: {
to(value) {
return formatCurrency([value]);
},
from(value) {
return stripFormatting(value);
},
},
};
this.slider = noUiSlider.create(e, options);
this.slider.on('start', (values, handle, unencoded, tap, positions, noUiSlider) => {
let sliderElem = this.slider.target;
let handleElem = document.querySelectorAll("div.noUi-handle[data-handle='" + handle + "']")[0];
console.log('SearchFiltersPriceComponent -> setRangeSlider -> this.slider', handleElem);
handleElem.classList.add("mystyle");
});
this.slider.on('end', (values, handle, unencoded, tap, positions, noUiSlider) => {
let sliderElem = this.slider.target;
let handleElem = document.querySelectorAll("div.noUi-handle[data-handle='"+handle+"']")[0];
handleElem.classList.remove("mystyle");
});
}

Related

Apply class to specific element based on button hover (with matching pairs)

As the title suggest I am trying to apply a class/styling to a specific element (in this case an SVG path) based on its corresponding button.
I have a map of the UK that is split up into regions and corresponding buttons to those regions, and I am trying to style a region based on hovering over a button.
So far I have a list of buttons and regions:
// England Buttons
const grBtn = document.querySelector(".eng-gr");
const seBtn = document.querySelector(".eng-se");
const eeBtn = document.querySelector(".eng-ee");
const emBtn = document.querySelector(".eng-em");
const yhBtn = document.querySelector(".eng-yh");
const neBtn = document.querySelector(".eng-ne");
const nwBtn = document.querySelector(".eng-nw");
const wmBtn = document.querySelector(".eng-wm");
const swBtn = document.querySelector(".eng-sw");
// England Region
const grRgn = document.querySelector(".greater-london");
const seRgn = document.querySelector(".south-east-england");
const eeRgn = document.querySelector(".east-england");
const emRgn = document.querySelector(".east-midlands");
const yhRgn = document.querySelector(".yorkshire-humber");
const neRgn = document.querySelector(".north-east-england");
const nwRgn = document.querySelector(".north-west-england");
const wmRgn = document.querySelector(".west-midlands");
const swRgn = document.querySelector(".south-west-england");
I know I can manually use a function for each button/region like so:
grBtn.addEventListener("mouseover", function () {
grRgn.classList.add("rgn-hover");
});
grBtn.addEventListener("mouseout", function () {
grRgn.classList.remove("rgn-hover");
});
But I am trying to figure out how I can do it with one (or a few) functions instead of each button/region (i will eventually be adding the rest of the UK).
Codepen of Project: https://codepen.io/MartynMc/pen/OJZWWer
Just an idea but if you can edit the HTML part and trasform this:
Greater London
Into this
Greater London
So for each button you specify the SVG class to highlight, you can just use event delegation on the buttons container like this:
let container = document.querySelector(".buttons");
container.addEventListener("mouseover", function (e) {
let ref = e.target.dataset.highlight;
if (ref) {
document.querySelector("." + ref).classList.add("rgn-hover");
}
});
container.addEventListener("mouseout", function (e) {
let ref = e.target.dataset.highlight;
if (ref) {
document.querySelector("." + ref).classList.remove("rgn-hover");
}
});
And that's all the JS code you need.
The parent receives the event from its children and if the child contains a data-highlight attribute, it finds a node with that class name and adds/removes the css class.
try this:
let arr = [
{ btn: grBtn, rgn: grRgn },
{ btn: seBtn, rgn: seRgn },
{ btn: eeBtn, rgn: eeRgn },
{ btn: emBtn, rgn: emRgn },
{ btn: yhBtn, rgn: yhRgn },
{ btn: neBtn, rgn: neRgn },
{ btn: nwBtn, rgn: nwRgn },
{ btn: wmBtn, rgn: wmRgn },
{ btn: swBtn, rgn: swRgn },
];
arr.forEach((item) => {
item.btn.addEventListener("mouseover", function () {
item.rgn.classList.add("rgn-hover");
});
item.btn.addEventListener("mouseout", function () {
item.rgn.classList.remove("rgn-hover");
});
});

Highchart Legend with Checkbox, events, hover style, symbol order change

I currently have Highcharts implemented in a Chart component in my application, but I need to make some changes to the Legend, went through most of the documentation, created some functions with Highcharts.wrap().
First, the Legend was simple, each legend item being
[Symbol] [Label] .
But now I need to change it into:
[Checkbox] [Label] [Symbol]
Here is what I got so far:
[Checkbox] [Symbol] [Label]
And with the click on the checkbox replicating the click on the Legend (symbol, label), which shows/hide the series line.
how? with this: (showing only the important parts)
const defaultOptions: Highcharts.Options = {
...,
legend: {
borderColor: "transparent",
verticalAlign: "top",
align: "left",
x: 14,
itemCheckboxStyle: {
cursor: "pointer",
border: "1px solid #62737a",
},
},
...,
plotOptions: {
series: {
...,
showCheckbox: true,
selected: true,
events: {
checkboxClick: function () {
this.setVisible(!this.visible);
},
},
},
...,
},
...,
}
If we only use showCheckbox: true, the checkbox will be far on the right side of each label, not ideal. So this is needed: (If possible I also would like tips on how to avoid the any error on TS in this case, without the comments).
Highcharts.wrap(Highcharts.Legend.prototype, "positionCheckboxes", legendCheckboxPosition);
function legendCheckboxPosition(
// eslint-disable-next-line #typescript-eslint/no-explicit-any
this: any,
// eslint-disable-next-line #typescript-eslint/no-explicit-any
p: any,
scrollOffset: number
) {
const alignAttr = this.group.alignAttr;
const clipHeight = this.clipHeight || this.legendHeight;
let translateY: number;
if (alignAttr) {
translateY = alignAttr.translateY;
Highcharts.each(
this.allItems,
function (item: {
// eslint-disable-next-line #typescript-eslint/no-explicit-any
checkbox: any;
// eslint-disable-next-line #typescript-eslint/no-explicit-any
legendItem: { getBBox: (arg0: boolean) => any };
// eslint-disable-next-line #typescript-eslint/no-explicit-any
checkboxOffset: any;
}) {
const checkbox = item.checkbox;
const bBox = item.legendItem.getBBox(true);
let top;
if (checkbox) {
top = translateY + checkbox.y + (scrollOffset || 0) + 2;
Highcharts.css(checkbox, {
left:
alignAttr.translateX +
item.checkboxOffset +
checkbox.x -
100 -
bBox.width +
17 +
"px",
top: top + "px",
display: top > translateY - 6 && top < translateY + clipHeight - 6 ? "" : "none",
});
}
}
);
}
}
But with this done, I still need to make some changes, which are:
Change the order of Symbol and Label
There is supposed to be a rtl property inside the legends options, which is supposed to change the order of Symbol and Label , but if I do that, it reverses, but it also reverse the order of the legends somehow, I'll show:
-> Without rtl:
-> With rtl: true inside the legends options:
The checkbox distance I understand, because it will need to change my legendCheckboxPosition function, my real problem here is the order of the legends being changed, like if I used legend.reversed: true.. I found out that I can use the reversed property to fix this, but I was wondering if this was a bug with something else..because in the documentation the rtl property only changes the order of Symbol and Label, not the legends order.
This is what I need:
I need to put a style in the :hover of the checkbox, I tried using the legend.itemCheckboxStyle but that doesn't allow me to add hover effects... (I need to place a box-shadow when hovering the checkbox)
ONE ISSUE SOLVED: Another issue is when clicking the legend item (which is separated of the checkbox)
When clicking the legend item, it shows/hide the series, but it doesn't change the checkbox selection.
I know that the checkbox selection is determined by the series.selected property, and that I have the legendItemClick event inside the plotOptions.series.events, but inside that I don't have a this.setSelected function, only this.setVisible function. I tried using that, but it seems to freeze the chart, not doing anything.
How to change the checkbox selection when clicking only in the legend item?
Edit: Managed to solve this by adding this event to options.plotOptions.series.events :
legendItemClick: function () {
const seriesIndex = this.index;
this.chart.series[seriesIndex].select();
},
Well.. that is my problem, with the hope that you guys can help me solve it.
A possible way to arranging the elements of legend would be to edit the legend in legend.labelFormatter and add a Unicode line character. To have the checkbox on the right also you can style it in legend.itemCheckboxStyle.
legend: {
symbolWidth: 0,
useHTML: true,
labelFormatter: function() {
return `<div>${this.name}<span style="color: green;">━</span></div>`;
},
itemDistance: 50,
itemCheckboxStyle: {
"width": "13px",
"height": "13px",
"margin-left": "-130px",
"position": "absolute"
}
},
API References:
https://api.highcharts.com/highcharts/legend.itemCheckboxStyle
Demo: https://jsfiddle.net/BlackLabel/sbcL7rp9/3/
After a lot of research and experimenting, I managed to have it all working.
Options (default options and events, also toggling visibility on load)
const series = GetSeries(opts, null);
...
// Check if Series is supposed to be hidden from load (from the checkbox selected prop), if it is, hide it
series.forEach((serie) => {
if (serie.selected !== undefined && serie.selected === false) serie.visible = false;
});
const defaultOptions: Highcharts.Options = {
...,
legend: {
borderColor: "transparent",
verticalAlign: "top",
align: "left",
x: 14,
},
...,
plotOptions: {
series: {
...
showCheckbox: true,
selected: true,
events: {
checkboxClick: function () {
this.setVisible(!this.visible);
},
legendItemClick: function () {
const seriesIndex = this.index;
this.chart.series[seriesIndex].select();
},
},
},
...
},
...
};
return mergeObjects(defaultOptions, opts);
}
And a lot of resolved extending Highcharts:
// Adjust position of the Highchart Legend Checkbox, and switch label and symbol in the legend
function legendPositionAdjustments(
// eslint-disable-next-line #typescript-eslint/no-explicit-any
this: any,
// eslint-disable-next-line #typescript-eslint/no-explicit-any
p: any,
scrollOffset: number
) {
const pixelsToREM = (value: number) => {
return value / 16;
};
const alignAttr = this.group.alignAttr;
const clipHeight = this.clipHeight || this.legendHeight;
let translateY: number;
const legendMainElement = this.box.parentGroup.element;
// Adjust the main legend element to be closer to the checkbox
if (legendMainElement) {
Highcharts.attr(legendMainElement, "transform", "translate(19, 10)");
}
if (alignAttr) {
translateY = alignAttr.translateY;
this.allItems.forEach(function (item: {
// eslint-disable-next-line #typescript-eslint/no-explicit-any
checkbox: any;
// eslint-disable-next-line #typescript-eslint/no-explicit-any
legendItem: { getBBox: (arg0: boolean) => any };
// eslint-disable-next-line #typescript-eslint/no-explicit-any
checkboxOffset: any;
// eslint-disable-next-line #typescript-eslint/no-explicit-any
legendGroup: any;
}) {
const bBox = item.legendItem.getBBox(true);
// Change position of Label and Symbol in the Highcharts Legend
const legendItemElement = item.legendGroup.element;
const legendItemPath = legendItemElement.querySelector("path.highcharts-graph");
const legendItemPoint = legendItemElement.querySelector("path.highcharts-point");
const legendItemText = legendItemElement.querySelector("text");
if (legendItemPath) {
Highcharts.attr(legendItemPath, "transform", `translate(${bBox.width + 3}, 0)`);
}
if (legendItemPoint) {
Highcharts.attr(legendItemPoint, "transform", `translate(${bBox.width + 3}, 0)`);
}
if (legendItemText) {
Highcharts.attr(legendItemText, "x", 0);
}
// Adjust the position of the checkbox to the left side of the Highcharts Legend
const checkbox = item.checkbox;
let top;
let left;
if (checkbox) {
top = translateY + checkbox.y + (scrollOffset || 0) + 4;
left = alignAttr.translateX + item.checkboxOffset + checkbox.x - 100 - bBox.width + 17;
Highcharts.css(checkbox, {
left: pixelsToREM(left) + "rem",
top: pixelsToREM(top) + "rem",
display: top > translateY - 6 && top < translateY + clipHeight - 6 ? "" : "none",
});
}
});
}
}
// This function is called when triggering show/hide of series, always calling with visibility = true
// eslint-disable-next-line #typescript-eslint/no-explicit-any, #typescript-eslint/no-unused-vars
function colorizeLegendRegardlessOfVisibility(this: any, proceed: any, item: any, visible: any) {
proceed.call(this, item, true);
}
This will:
Avoid changing the color of the legend and symbol when series is hidden or legend is out of focus
Correct position the checkbox to left side of the legend
Switch positions of the symbol and label, which are in the SVG
Hope to help someone who encounters similar problems in the future.

Go JS Tree view equivalent in React

I need to achieve the tree view (Go JS Tree View). The respective tree view sample source code without React JS is at (Tree View Source Code). I'm trying to do the same using React JS and have the following code written. But somehow I'm missing something and the diagram/tree view is not rendering. Can you please help me to figure out the issue?
import React from 'react';
import * as go from 'gojs';
import { ReactDiagram } from 'gojs-react';
import '../../../App.css';
go.Shape.defineFigureGenerator("ExpandedLine", function(shape, w, h) {
return new go.Geometry()
.add(new go.PathFigure(0, 0.25*h, false)
.add(new go.PathSegment(go.PathSegment.Line, .5 * w, 0.75*h))
.add(new go.PathSegment(go.PathSegment.Line, w, 0.25*h)));
});
// use a sideways V figure instead of PlusLine in the TreeExpanderButton
go.Shape.defineFigureGenerator("CollapsedLine", function(shape, w, h) {
return new go.Geometry()
.add(new go.PathFigure(0.25*w, 0, false)
.add(new go.PathSegment(go.PathSegment.Line, 0.75*w, .5 * h))
.add(new go.PathSegment(go.PathSegment.Line, 0.25*w, h)));
});
let nodeDataArray = [{ key: 0 }];
const initDiagram = () => {
let $ = go.GraphObject.make;
const diagram =
$(go.Diagram, "myDiagramDiv",
{
allowMove: false,
allowCopy: false,
allowDelete: false,
allowHorizontalScroll: false,
layout:
$(go.TreeLayout,
{
alignment: go.TreeLayout.AlignmentStart,
angle: 0,
compaction: go.TreeLayout.CompactionNone,
layerSpacing: 16,
layerSpacingParentOverlap: 1,
nodeIndentPastParent: 1.0,
nodeSpacing: 0,
setsPortSpot: false,
setsChildPortSpot: false
})
});
diagram.nodeTemplate =
$(go.Node,
{ // no Adornment: instead change panel background color by binding to Node.isSelected
selectionAdorned: false,
// a custom function to allow expanding/collapsing on double-click
// this uses similar logic to a TreeExpanderButton
doubleClick: function(e, node) {
let cmd = diagram.commandHandler;
if (node.isTreeExpanded) {
if (!cmd.canCollapseTree(node)) return;
} else {
if (!cmd.canExpandTree(node)) return;
}
e.handled = true;
if (node.isTreeExpanded) {
cmd.collapseTree(node);
} else {
cmd.expandTree(node);
}
}
},
$("TreeExpanderButton",
{ // customize the button's appearance
"_treeExpandedFigure": "ExpandedLine",
"_treeCollapsedFigure": "CollapsedLine",
"ButtonBorder.fill": "whitesmoke",
"ButtonBorder.stroke": null,
"_buttonFillOver": "rgba(0,128,255,0.25)",
"_buttonStrokeOver": null
}),
$(go.Panel, "Horizontal",
{ position: new go.Point(18, 0) },
new go.Binding("background", "isSelected",
s => (s ? 'lightblue' : 'white')).ofObject(),
$(go.Picture,
{
width: 18, height: 18,
margin: new go.Margin(0, 4, 0, 0),
imageStretch: go.GraphObject.Uniform
},
// bind the picture source on two properties of the Node
// to display open folder, closed folder, or document
new go.Binding("source", "isTreeExpanded", imageConverter).ofObject(),
new go.Binding("source", "isTreeLeaf", imageConverter).ofObject()),
$(go.TextBlock,
{ font: '9pt Verdana, sans-serif' },
new go.Binding("text", "key", function(s) { return "item " + s; }))
) // end Horizontal Panel
); // end Node
diagram.linkTemplate = $(go.Link);
let max = 499;
let count = 0;
while (count < max) {
count = makeTree(3, count, max, nodeDataArray, nodeDataArray[0]);
}
diagram.model = new go.TreeModel(nodeDataArray);
return diagram;
}
function makeTree(level, count, max, nodeDataArray, parentData) {
let numChildren = Math.floor(Math.random() * 10);
for (let i = 0; i < numChildren; i++) {
if (count >= max) return count;
count++;
let childData = { key: count, parent: parentData.key };
nodeDataArray.push(childData);
if (level > 0 && Math.random() > 0.5) {
count = makeTree(level - 1, count, max, nodeDataArray, childData);
}
}
return count;
}
function imageConverter(prop, picture) {
let node = picture.part;
if (node.isTreeLeaf) {
return "images/document.svg";
} else {
if (node.isTreeExpanded) {
return "images/openFolder.svg";
} else {
return "images/closedFolder.svg";
}
}
}
window.addEventListener('DOMContentLoaded', initDiagram);
const TreeView = () => {
return (
<>
GO JS
<div id="myDiagramDiv">
<ReactDiagram
initDiagram={initDiagram}
divClassName='diagram-component'
nodeDataArray={nodeDataArray}
skipsDiagramUpdate={false}
/>
</div>
</>
);
}
export default TreeView;
When React start executing, the DOMContentLoaded event have already been fired. Instead try to call initDiagram in a useEffect hook
const TreeView = () => {
useEffect(initDiagram);
return (
<>
GO JS
<div id="myDiagramDiv">
<ReactDiagram
initDiagram={initDiagram}
divClassName='diagram-component'
nodeDataArray={nodeDataArray}
skipsDiagramUpdate={false}
/>
</div>
</>
);
}

Infinitegrid duplicates API calls

I use vue-infinitegrid and I have realized in a browser that a backend API is called three times. Some code first (git):
<GridLayout
ref="ig"
:options="options"
:layoutOptions="layoutOptions"
#append="onAppend"
#layout-complete="onLayoutComplete"
#image-error="onImageError"
>
<div slot="loading">Loading...</div>
<div class="item" v-for="(item) in list" :key="item.key">
<ViewItem :item="item"/>
</div>
</GridLayout>
data() {
return {
start: 0,
loading: false,
list: [],
isEnded: false,
options: {
isOverflowScroll: false,
useFit: true,
useRecycle: true,
horizontal: false,
align: 'center',
transitionDuration: 0.2,
},
layoutOptions: {
margin: 15,
align: 'center',
},
pageSize: 3,
};
},
methods: {
async onAppend({ groupKey, startLoading }) {
this.$log.debug(`onAppend group key = ${groupKey}`);
const { list } = this;
if (this.isEnded) return;
const items = await this.loadItems();
startLoading();
this.list = list.concat(items);
},
async loadItems() {
const start = this.start || 0, size = parseFloat(this.pageSize), { tag } = this;
this.$log.debug(`loadItems start = ${start}, size = ${size}`);
let res = await this.$store.dispatch('GET_ITEM_STREAM', { start, size, tag });
if (res.length === 0) { // todo or smaller than requested
this.$log.debug('loadItems no data');
this.isEnded = true;
this.$refs.ig.endLoading();
return res;
}
if (this.exceptItem) {
res = res.filter(item => item._id !== this.exceptItem._id);
}
this.start = start + res.length;
this.$log.debug('loadItems finished');
return res;
},
onLayoutComplete({ isLayout, endLoading }) {
this.$log.debug(`onLayoutComplete isLayout = ${isLayout}`);
if (!isLayout) {
endLoading();
}
},
And some logs:
onAppend group key =
ItemList.vue:71 loadItems start = 0, size = 3
items.js:132 GET_ITEM_STREAM {"start":0,"size":3}
See more tips at https://vuejs.org/guide/deployment.html
ItemList.vue:83 loadItems finished
ItemList.vue:87 onLayoutComplete isLayout = false
ItemList.vue:62 onAppend group key =
ItemList.vue:71 loadItems start = 3, size = 3
items.js:132 GET_ITEM_STREAM {"start":3,"size":3}
ItemList.vue:62 onAppend group key =
ItemList.vue:71 loadItems start = 3, size = 3
items.js:132 GET_ITEM_STREAM {"start":3,"size":3}
2 ItemList.vue:83 loadItems finished
ItemList.vue:87 onLayoutComplete isLayout = false
ItemList.vue:62 onAppend group key =
ItemList.vue:71 loadItems start = 6, size = 3
items.js:132 GET_ITEM_STREAM {"start":6,"size":3}
ItemList.vue:62 onAppend group key =
ItemList.vue:71 loadItems start = 6, size = 3
items.js:132 GET_ITEM_STREAM {"start":6,"size":3}
2 ItemList.vue:83 loadItems finished
ItemList.vue:87 onLayoutComplete isLayout = false
I can see that start is incremented after onAppend is called. It looks like some concurrency issue, that the infinitegrid component does not wait until the REST call is finished and fires new event. Has anybody any experience with this component and knows how to handle this situation when I need to wait for a backend response?
Update
I have replaced async call with fixed data and it started to work correctly. So the trouble is with async.
// let items = await this.$store.dispatch('GET_ITEM_STREAM', { start, size, tag });
let items = [{ ...
Update:
Code sandbox with minimum reproducible scenerio: https://w56p2.csb.app/
The symptoms are different now, probably exhibiting the root cause - the event is emitted before the previous is processed.
https://github.com/naver/egjs-infinitegrid/issues/365
https://naver.github.io/egjs-infinitegrid/storybook/?path=/story/loading-bar-with-data-delay--grid-layout
In startLoading and endLoading, the loading bar appears and disappears, and some functions are temporarily disabled (moveTo, useFit).
The append and prepend work and must be prevented through the isProcessing method.
onAppend({ groupKey, startLoading, currentTarget }) {
if (currentTarget.isProcessing()) {
return;
}
}

React Data Grid using row drag and drop and Drag Columns to Reorder features togather

I am trying to use React Data Grid in my project and I want to use row drag and drop and Drag Columns to Reorder features together.
I tried to do this by passing draggable: true for ReactDataGrid column property and wrapping the ReactDataGrid and Draggable.Container with DraggableHeader.DraggableContainer.
It makes column header moveable but it does not trigger onHeaderDrop action in DraggableContainer and it gave a console error Uncaught TypeError: Cannot read property 'id' of undefined.
I change example23-row-reordering.js according to the description above.
const ReactDataGrid = require('react-data-grid');
const exampleWrapper = require('../components/exampleWrapper');
const React = require('react');
const {
Draggable: { Container, RowActionsCell, DropTargetRowContainer },
Data: { Selectors },
DraggableHeader: {DraggableContainer}
} = require('react-data-grid-addons');
import PropTypes from 'prop-types';
const RowRenderer = DropTargetRowContainer(ReactDataGrid.Row);
class Example extends React.Component {
static propTypes = {
rowKey: PropTypes.string.isRequired
};
static defaultProps = { rowKey: 'id' };
constructor(props, context) {
super(props, context);
this._columns = [
{
key: 'id',
name: 'ID',
draggable: true
},
{
key: 'task',
name: 'Title',
draggable: true
},
{
key: 'priority',
name: 'Priority',
draggable: true
},
{
key: 'issueType',
name: 'Issue Type',
draggable: true
}
];
this.state = { rows: this.createRows(1000), selectedIds: [1, 2] };
}
getRandomDate = (start, end) => {
return new Date(start.getTime() + Math.random() * (end.getTime() - start.getTime())).toLocaleDateString();
};
createRows = (numberOfRows) => {
let rows = [];
for (let i = 1; i < numberOfRows; i++) {
rows.push({
id: i,
task: 'Task ' + i,
complete: Math.min(100, Math.round(Math.random() * 110)),
priority: ['Critical', 'High', 'Medium', 'Low'][Math.floor((Math.random() * 3) + 1)],
issueType: ['Bug', 'Improvement', 'Epic', 'Story'][Math.floor((Math.random() * 3) + 1)],
startDate: this.getRandomDate(new Date(2015, 3, 1), new Date()),
completeDate: this.getRandomDate(new Date(), new Date(2016, 0, 1))
});
}
return rows;
};
rowGetter = (i) => {
return this.state.rows[i];
};
isDraggedRowSelected = (selectedRows, rowDragSource) => {
if (selectedRows && selectedRows.length > 0) {
let key = this.props.rowKey;
return selectedRows.filter(r => r[key] === rowDragSource.data[key]).length > 0;
}
return false;
};
reorderRows = (e) => {
let selectedRows = Selectors.getSelectedRowsByKey({rowKey: this.props.rowKey, selectedKeys: this.state.selectedIds, rows: this.state.rows});
let draggedRows = this.isDraggedRowSelected(selectedRows, e.rowSource) ? selectedRows : [e.rowSource.data];
let undraggedRows = this.state.rows.filter(function(r) {
return draggedRows.indexOf(r) === -1;
});
let args = [e.rowTarget.idx, 0].concat(draggedRows);
Array.prototype.splice.apply(undraggedRows, args);
this.setState({rows: undraggedRows});
};
onRowsSelected = (rows) => {
this.setState({selectedIds: this.state.selectedIds.concat(rows.map(r => r.row[this.props.rowKey]))});
};
onRowsDeselected = (rows) => {
let rowIds = rows.map(r => r.row[this.props.rowKey]);
this.setState({selectedIds: this.state.selectedIds.filter(i => rowIds.indexOf(i) === -1 )});
};
render() {
return (
<DraggableContainer
onHeaderDrop={()=>{console.log('column dropped');}}
>
<Container>
<ReactDataGrid
enableCellSelection={true}
rowActionsCell={RowActionsCell}
columns={this._columns}
rowGetter={this.rowGetter}
rowsCount={this.state.rows.length}
minHeight={500}
rowRenderer={<RowRenderer onRowDrop={this.reorderRows}/>}
rowSelection={{
showCheckbox: true,
enableShiftSelect: true,
onRowsSelected: this.onRowsSelected,
onRowsDeselected: this.onRowsDeselected,
selectBy: {
keys: {rowKey: this.props.rowKey, values: this.state.selectedIds}
}
}}/>
</Container>
</DraggableContainer>);
}
}
module.exports = exampleWrapper({
WrappedComponent: Example,
exampleName: 'Row Reordering',
exampleDescription: 'This examples demonstrates how single or multiple rows can be dragged to a different positions using components from Draggable React Addons',
examplePath: './scripts/example23-row-reordering.js'
});
I went through their documentation but could not find any place where they say these two features can't use together. But in their examples does not provide any example for this.Any example code for documentation describes how to use these two features together would be greatly appreciated.

Categories