How to render ChartJs with Kotlin React Frontend - reactjs

I am experimenting with a simple project based with Kotlin and React and I have some data I would like to visualise with ChartJs.
But I am not sure how exactly to 'connect the dots'.
I am randomly generating some data in background about some dummy 'customers'. It is a continuous stream of data (technically it goes on forever if I don't stop it) and it looks like this:
data:{"name":"John","age": 22,"money":1088.434131568658585820230655372142791748046875}
data:{"name":"Patric","age": 32,"money":9803.739582599226196180097758769989013671875}
data:{"name":"Sven","age": 13,"money":6654.2184028105320976465009152889251708984375}
data:{"name":"Trevor","age": 29,"money":1818.314185601747112741577439010143280029296875}
data:{"name":"John","age": 30,"money":427.240483111973617269541136920452117919921875}
data:{"name":"Mark","age": 15,"money":3065.9830877835693172528408467769622802734375}
data:{"name":"Daniel","age": 18,"money":5860.1371074951812261133454740047454833984375}
data:{"name":"Chris","age": 28,"money":7547.2329861790212817140854895114898681640625}
Here is my Model:
data class CustomerData(var name: String, var age: Int, var money: Int)
and my Chart Component:
interface CustomerChartProps : RProps {
var customerDataList: List<CustomerData>
}
interface CustomerChartState : RState {
var customerDataList: List<CustomerData>
}
class CustomerChart : RComponent<CustomerChartProps, CustomerChartState>() {
override fun CustomerChartState.init(props: CustomerChartProps) {
customerDataList = props.customerDataList
}
override fun RBuilder.render() {
canvas() {
attrs.id = "myChart"
attrs.height = "600"
attrs.width = "600"
key = "chart"
div {
props.customerDataList
}
}
var chart = document.getElementById("myChart")
}
}
fun dummyChartData(data: List<CustomerData>) =
listOf(CustomerData("John", 30, 9999))
fun RBuilder.customerChart(customerDataList: List<CustomerData>) = child(CustomerChart::class) {
attrs.customerDataList = customerDataList
}
What is not clear is how do I tell the RState that my data is constantly changing. I want to be able to update the chart dynamically, while I get new data from my dummy backend. I am not persisting the data in anyway.
What are your suggestions or recommendations?
I cannot seem to find a suitable example/tutorial/documentation either.
I know my question is pretty vague, but I hope at least I am the right direction, or that someone can point out how to get there.

Related

Visual Relations using Winium automating a WPF app

Using other automation tools for Windows Apps, such as LeanFT, there is a way to specify a visual relation. For example, if I identified a label, I could then say look for the text box to the right of it.
Is there a way to do this in Winium?
Here is a sample of what the code looks like in LeanFT
var projectButton = baseOR.PlatformWindow.ProjectList.BrowseProjects.Describe<IButton>(new ButtonDescription
{
FullType = "button",
Text = node,
Index = 0
});
return baseOR.PlatformWindow.ProjectList.BrowseProjects.Describe<IButton>(new ButtonDescription
{
FullType = "button",
Vri =
{
new VisualRelation
{
TestObject = projectButton,
HorizontalRelation = HorizontalVisualRelation.LeftAndInline,
}
}
});
As you can see, we are essentially identifying one element, and then using that as the TestObject in the VisualRelation to identify something else.

Implement custom editor for Quill blot

I'm trying to customize the Quill editor for my needs. I managed to implement and insert custom blots, as described in https://quilljs.com/guides/cloning-medium-with-parchment/ But I need to edit data, which is attached to my blots, like the URL of a link for example. The default implementation of Quill displays a small "inline" edit box for links. I want to implement something like that myself, but just don't get it. I did not find any hints in the docs and guides. Reading the source code of Quill, I was not able to figure out where the editing dialog for links is implemented. Any starting point would be very appreciated.
I've tried something similar. Proper way of doing it should be creating a module. Unfortunately as you already know it is not as easy as it seems.
Let me point you to some useful resources that helped me a lot with understanding how to create extensions for quill.
Quills maintainer is curating Awesome quill list.
I recommend looking especially into
quill-emoji it contains code to display tooltip emoji while writing
quill-form maybe some form extension has some code that will point you in the right direction
Here is my try on to it using custom quill module.
const InlineBlot = Quill.import('blots/inline');
class NamedLinkBlot extends InlineBlot {
static create(value) {
const node = super.create(value);
node.setAttribute('href', value);
node.setAttribute('target', '_blank');
return node;
}
}
NamedLinkBlot.blotName = 'namedlink';
NamedLinkBlot.tagName = 'A';
Quill.register('formats/namedlink', NamedLinkBlot);
const Tooltip = Quill.import('ui/tooltip');
class NamedLinkTooltip extends Tooltip {
show() {
super.show();
this.root.classList.add('ql-editing');
}
}
NamedLinkTooltip.TEMPLATE = [
'<a class="ql-preview" target="_blank" href="about:blank"></a>',
'<input type="text" data-link="https://quilljs.com">',
'Url displayed',
'<input type="text" data-name="Link name">',
'<a class="ql-action"></a>',
'<a class="ql-remove"></a>',
].join('');
const QuillModule = Quill.import('core/module');
class NamedLinkModule extends QuillModule {
constructor(quill, options) {
super(quill, options);
this.tooltip = new NamedLinkTooltip(this.quill, options.bounds);
this.quill.getModule('toolbar').addHandler('namedlink', this.namedLinkHandler.bind(this));
}
namedLinkHandler(value) {
if (value) {
var range = this.quill.getSelection();
if (range == null || range.length === 0) return;
var preview = this.quill.getText(range);
this.tooltip.show();
}
}
}
Quill.register('modules/namedlink', NamedLinkModule);
const quill = new Quill('#editor', {
theme: 'snow',
modules: {
namedlink: {},
toolbar: {
container: [
'bold',
'link',
'namedlink'
]
}
}
});
CodePen Demo
To see the tooltip:
Select any word
Click invisible button on the right of link button, your cursor will turn to hand.
Main issues that need to be addressed:
in order to customize the tooltip you will need to copy majority of the code from SnowTooltip Main pain point is that you cannot easily extend That tooltip.
you need to also adapt the code of event listeners but it should be straightforward
Here's a partial answer. Please see lines 41-64 in file "https://github.com/quilljs/quill/blob/08107187eb039eababf925c8216ee2b7d5166d41/themes/snow.js" (Please note, that the authors have since moved to TypeScript, lines 45-70?, and possibly made other changes.)
I haven't tried implementing something similar but it looks like Quill is watching a "selection-change" event and checks if the selection is on a LinkBlot with a defined link.
The SnowTooltip class includes references to the selectors, 'a.ql-preview', 'ql-editing', 'a.ql-action', and 'a.ql-remove', which we find in the link-editing tooltip.
this.quill.on(
Emitter.events.SELECTION_CHANGE,
(range, oldRange, source) => {
if (range == null) return;
if (range.length === 0 && source === Emitter.sources.USER) {
const [link, offset] = this.quill.scroll.descendant(
LinkBlot,
range.index,
);
if (link != null) {
this.linkRange = new Range(range.index - offset, link.length());
const preview = LinkBlot.formats(link.domNode);
this.preview.textContent = preview;
this.preview.setAttribute('href', preview);
this.show();
this.position(this.quill.getBounds(this.linkRange));
return;
}
} else {
delete this.linkRange;
}
this.hide();
},
);

Displaying data from Firebase in React without arrays

I am new to both React and Firebase. I struggled a bit to get data from the database, even though the instructions on the Firebase website were pretty straightforward.
I managed to print data in the view by using this code:
Get data from DB and save it in state:
INSTRUMENTS_DB.once('value').then(function(snapshot) {
this.state.instruments.push(snapshot.val());
this.setState({
instruments: this.state.instruments
});
From Firebase, I receive and Object containing several objects, which correspond to the differen instruments, like shown in the following snippet:
Object {
Object {
name: "Electric guitar",
image: "img/guitar.svg"
}
Object {
name: "Bass guitar",
image: "img/bass.svg"
}
// and so on..
}
Currently, I print data by populating an array like this:
var rows = [];
for (var obj in this.state.instruments[0]) {
rows.push(<Instrument name={this.state.instruments[0][obj].name}
image={this.state.instruments[0][obj].image}/>);
}
I feel like there's a better way to do it, can somedody give a hint? Thanks
I user firebase a lot and mu solution is little ES6 helper function
const toArray = function (firebaseObj) {
return Object.keys(firebaseObj).map((key)=> {
return Object.assign(firebaseObj[key], {key});
})
};
I also assign the firebase key to object key property, so later I can work with the keys.
The native map function only works for arrays, so using directly it on this object won't work.
What you can do instead is:
Call the map function on the keys of your object using Object.keys():
getInstrumentRows() {
const instruments = this.state.instruments;
Object.keys(instruments).map((key, index) => {
let instrument = instruments[key];
// You can now use instrument.name and instrument.image
return <Instrument name={instrument.name} image={instrument.image}/>
});
}
Alternatively, you can also import the lodash library and use its map method which would allow you to refactor the above code into:
getInstrumentRowsUsingLodash() {
const instruments = this.state.instruments;
_.map(instruments, (key, index) => {
let instrument = instruments[key];
// You can now use instrument.name and instrument.image
return <Instrument name={instrument.name} image={instrument.image}/>
});
}
Side note:
When you retrieve you data from Firebase you attempt to update the state directly with a call on this.state.instruments. The state in React should be treated as Immutable and should not be mutated with direct calls to it like push.
I would use map function:
_getInstrumentRows() {
const instruments = this.state.instruments[0];
if (instruments) {
return instruments.map((instrument) =>
<Instrument name={instrument.name}
image={instrument.image}/>);
}
}
In your render() method you just use {_getInstrumentRows()} wherever you need it.

Capturing click events on clusters with markercluster and angular-leaflet-directive

I'm playing with the angular-leaflet-directive, and getting the marker names from a mouse click is straight forward. I just listen for the leafletDirectiveMarker.click event and then access args.markerName.
angular-leaflet-directive also works with markercluster, so I can cluster markers that have the same coordinates or ones that are close by. However, I would like to do the following, but it is not clear from the documentation on how to do it:
Make user double-click on cluster to zoom in. Currently doing a single click on a cluster will zoom in on the markers. see example.
How to listen for click event on cluster and get all marker names in the cluster.
The documentation for clustermarker has a cluster event:
markers.on('clusterclick', function (a) {
console.log('cluster ' + a.layer.getAllChildMarkers().length);
});
But I'm not sure what event I should be listening to using angular-leaflet-directive.
As far as your first question goes, you'll have to hook the doubleclick and pass it the fire('click') command after overriding the usual click event. Probably more trouble than its really worth, especially on mobile - and not something I can easily solve.
Regarding your second question, I have just solved it.
$scope.openMarker is a reference to an ng-click event in my jade template that is attached to an ng-repeat which pulls images and their id's from the database.
$scope.openMarker = function(id) {
var _this = [];
_this.id = id;
leafletData.getMarkers()
.then(function(markers) {
$scope.london = {
lat: $scope.markers[_this.id].lat,
lng: $scope.markers[_this.id].lng,
zoom: 19
};
var _markers = [];
_markers.currentMarker = markers[_this.id];
_markers.currentParent = _markers.currentMarker.__parent._group;
_markers.visibleParent = _markers.currentParent.getVisibleParent(markers[id]);
_markers.markers = markers;
return _markers;
}).then(function(_markers){
if (_markers.visibleParent !== null) {
_markers.visibleParent.fire('clusterclick');
} else {
_markers.currentMarker.fire('click');
}
return _markers;
}).then(function(_markers){
_markers.currentParent.zoomToShowLayer(_markers.markers[ _this.id ], function() {
$scope.hamburg = {
lat: $scope.markers[_this.id].lat,
lng: $scope.markers[_this.id].lng,
zoom: 19
};
if (_markers.currentMarker !== null) {
_markers.currentMarker.fire('click');
} else {
_markers.visibleParent.fire('clusterclick');
_markers.currentMarker.fire('click');
}
});
});
};
You can read more about how I came to this solution here at github.
Much like many people, I too had a long search with no results. While experimenting with another method, I came across this:
$timeout(function(){
leafletData.getLayers().then(function(layers) {
$scope.markerClusterGrp = layers.overlays.locations;
var clusters = $scope.markerClusterGrp.getLayers();
$scope.markerClusterGrp.on('clustermouseover', function (a) {
var clusterObjects = a.layer.getAllChildMarkers();
console.log(clusterObjects);
});
$scope.markerClusterGrp.on('clusterclick', function (a) {
var clusterObjects = a.layer.getAllChildMarkers();
console.log(clusterObjects);
});
});
},1000);
It works the same, the difference is that it requires a timeout in order to wait for the layer to render with all markers (my understanding, correct me if wrong :-) ).
I hope this helps anyone searching for an angular solution. Just remember to include $timeout in your controller dependencies.

Rally Ext JS change functionalty of existing component

I am trying to either override or extend an existing component - the Rally.ui.PercentDone component. I want to provide it with a portfolio item with all the data necessary to render it using the same color coding Rally native apps use (the Rally Health Color Calculator).
I really just need the function to pass the correct record. Here is my idea so far:
Ext.define('Custom.PercentDone', {
requires: ['Rally.ui.renderer.template.progressbar.PortfolioItemPercentDoneTemplate', 'Rally.util.HealthColorCalculator'],
extend : 'Rally.ui.PercentDone',
alias : 'widget.cpercentdone',
config: {
record: null
},
constructor: function(config) {
config = this.config;
this.renderTpl = Ext.create('Custom.renderer.template.progressbar.PercentDoneTemplate', {
calculateColorFn: Ext.bind(function(recordData) {
console.log('called my custom coloring fn');
var colorObject = Rally.util.HealthColorCalculator.calculateHealthColorForPortfolioItemData(config.record, config.percentDoneName);
return colorObject.hex;
}, this)
});
this.renderData = config;
this.mergeConfig(config);
this.callParent([this.config]);
}
});
var custom = Ext.create('Custom.PercentDone', {
record: item,
percentDoneName: 'PercentDoneByStoryPlanEstimate',
});
but my calculate color function is not being called instead of the default.
I got it to work by overriding:
var percentDoneByStoryCountEl = Ext.create("Rally.ui.PercentDone", {
record: item,
percentDoneName: "PercentDoneByStoryCount",
percentDone: item.PercentDoneByStoryCount
});
tpl = percentDoneByStoryCountEl.renderTpl;
tpl.calculateColorFn = function (recordData) {
var colorObject = Rally.util.HealthColorCalculator.calculateHealthColorForPortfolioItemData(item, "PercentDoneByStoryCount");
return colorObject.hex;
};
Ext.override(percentDoneByStoryCountEl, {
renderTpl: tpl
});
but I would like to figure out how to do it via extending the component instead.
Thanks for your help.

Resources