Set alternative colors when using the propertyFields on series.columns.template - amcharts

Im having a problem to set an alternative color for a label on a series column template using the propertyField.
For example. I use this:
let columnTemplate = series.columns.template;
columnTemplate.propertyFields.fill = 'color';
Where 'color' is comming from DataItem, as a data from backend system.
And later on I want to add a label on the column. And I want a contrast so I want to use the alternative color: https://www.amcharts.com/docs/v4/concepts/colors/#Getting_contrasting_color
I make up my label as:
let label = columnTemplate.createChild(am4core.Label);
Then I will set the alternative color from the series.columns.template.
But I dont get it work. I tried for example:
label.fill = series.columns.template.fill.alternative;
Dosent work.
This:
label.fill = am4core.color(series.columns.template.propertyFields.fill).alternative;
Dont work.
This:
label.adapter.add('fill', (value, target, key) => {
const dataContext: { [key: string]: string } | undefined = target.dataItem?.dataContext as {
[key: string]: string;
};
return am4core.color(dataContext?.color).alternative;
});
Throws an error...
Is there anyone who know a solution for this? Please help.

I,m not sure this is a good solution but it works:
export function setContrastColor(value: any, target: any) {
const dataContext = (target.dataItem?.dataContext ?? {}) as Record<string, string | undefined>;
if (dataContext.color) {
return am4core.color(`${dataContext?.color}`).alternative;
} else {
return am4core.color('#FFF');
}
}
And calling it:
label.adapter.add('fill', setContrastColor);

Related

Customizing colors for PrimeReact datatable

There is a way to change a background or text color to a row in PrimeReact Datatable that is using rowClassName={rowClass} where rowClass is a function that allows returning a class configured in the CSS file.
but... what if I want to choose an arbitrary color? for example, one fetched from a database in #RRGGBB format?
Reading de documentation I can't see a way to call a function to return the style string. Another way could be, creating dynamically the class name, for example...
class RRGGBB for a selected color, define this class with background: #RRGGBB and let rowClassName={rowClass} call rowClass function returning this dynamically created class...
I have this approach, but don't work:
const mycolor = "#00ff00";
function createStyle() {
const style = document.createElement("style");
// add CSS styles
style.innerHTML = `
.lulu {
color: white;
background-color: ${mycolor};
}
`;
// append the style to the DOM in <head> section
document.head.appendChild(style);
}
createStyle();
const rowClass = (data) => {
return {
"lulu": data.category === "Accessories"
};
};
.....
<DataTable value={products} rowClassName={rowClass}>
this code is a modified version of the sample code by prime react, here, in sandbox:
https://codesandbox.io/s/o6k1n
thanks!
I have solved it...
What I did is to create a dynamic css, and then use it:
function createStyle(color) {
var style = document.getElementsByTagName("style");
var colortag = color.replace("#", "mycolor");
//Assuming there is a style section in the head
var pos = style[0].innerHTML.indexOf(colortag);
if(pos<0)
style[1].innerHTML += "."+colortag+`
{
color: ${color}!important;
}
`;
return colortag;
const rowClass = (data) => {
var colortag;
if (data.COLOR!=undefined)
colortag=createStyle(data.COLOR);
return { [colortag]: ata.COLOR!=undefined };
}
<DataTable ref={dt} value={Data} rowClassName={rowClass}>
<column>......</column>
</DataTable>
With this code, if in the data there is a field called COLOR:"#RRGGBB" then it will create a style with this color and use it as text color. The same can be applied to background or whatever

Cypress automation: multiple cy.get elements in one function

I have a function:
checkWebElemAndAssert(...elements) {
for (const element of elements) {
element.should('be.visible').click().should('be.checked');
}
}
and i use it within another function:
checkRegisterValues = () => {
let maleCheckBox = cy.get('input[value=Male]');
let femaleCheckBox = cy.get('input[value=FeMale]');
let cricketCheckBox = cy.get('#checkbox1');
let registerElemList = [maleCheckBox, femaleCheckBox, cricketCheckBox];
this.browserUtils.checkWebElemAndAssert(...registerElemList);
return this;
}
The problem is that when i use checkRegisterValues() it uses for each action the last element: cricketCheckBox. Any hints on what is wrong? i would expect that the action is made for each element and not the last one.
Have you tried passing in the array like this?
this.browserUtils.checkWebElemAndAssert(registerElemList);
You can also print out
checkWebElemAndAssert(...elements) {
console.log(elements);
for (const element of elements) {
element.should('be.visible').click().should('be.checked');
}
}
and see what you are passing in
ok so i read a bit more and made this:
checkWebElemAndAssert2(elements) {
cy.get(elements).each(($list) => {
cy.get($list).click({ multiple: true }).should('be.checked')
})
}
Basically in elements when i call checkWebElemAndAssert2 i will give the list identifier. Seems to work but not sure meets the standard.
checkRegisterValues = () => {
let myList = 'input[type=radio]';
this.browserUtils.checkWebElemAndAssert2(myList);
return this;
}

Plugin ReferenceError: Color is not defined in

First plugin with XD and i can't seem to create a instance of class Color.
Their documentation doesn't show any examples and any that i find in other examples just shows new Color() and that i don't have to perform any require.
https://adobexdplatform.com/plugin-docs/reference/Color.html
Plugin ReferenceError: Color is not defined
at exportToBmpFunction (C:\Users\<useR>\AppData\Local\Packages\Adobe.CC.XD_adky2gkssdxte\LocalState\develop\BMP_Export\main.js:21:21)
What am i doing wrong?
async function exportToBmpFunction(selection) {
if (selection.items.length == 0) {
return;
}
// Generate PNG rendition of the selected node
let application = require("application");
let scenegraph = require("scenegraph");
let fs = require("uxp").storage.localFileSystem;
let shape = selection.items[0];
let file = await fs.getFileForSaving('what.png', { types: ["png"] });
let renditions = [{
node: shape,
outputFile: file,
type: application.RenditionType.PNG,
scale: 1,
background: new Color('#FF00CD', 1)
}];
application.createRenditions(renditions).then(function(results) {
// ...do something with outputFiles on disk...
});
}
module.exports = {
commands: {
exportToBmp: exportToBmpFunction
}
};
Turns out after digging through other classes in the same 'namespace' that their documentation is just flat out wrong in some places. This is what it should be.
const Color = require("scenegraph").Color;
let color = new Color('#FF00CD');
This happens to be in direct contradiction to examples of use of Color in the docs.
Yay for freehanding code!

how to project data change into child component with angular2?

i'm trying to build angular2 component which draws chart (using jquery plot)
import {Component, ElementRef, Input, OnChanges} from 'angular2/core';
#Component({
selector: 'flot',
template: `<div>loading</div>`
})
export class FlotCmp implements OnChanges{
private width = '100%';
private height = 220;
static chosenInitialized = false;
#Input() private options: any;
#Input() private dataset:any;
#Input() private width:string;
#Input() private height:string;
constructor(public el: ElementRef) {}
ngOnChanges() {
if(!FlotCmp.chosenInitialized) {
let plotArea = $(this.el.nativeElement).find('div').empty();
plotArea.css({
width: this.width,
height: this.height
});
$.plot( plotArea, this.dataset, this.options);
FlotCmp.chosenInitialized = true;
}
}
}
Component getting chart "data" property as input parameter:
<flot [options]="splineOptions" [dataset]="dataset" height="250px" width="100%"></flot>
So far i managed to make it work as long as "dataset" is static variable.
this.dataset = [{label: "line1",color:"blue",data:[[1, 130], [3, 80], [4, 160], [5, 159], [12, 350]]}];
My problem is to make it work when data came as a promise:
export class App implements OnInit {
private dataset:any;
public entries;
getEntries() {
this._flotService.getFlotEntries().then(
entries => this.dataset[0].data = entries,
error => this.errorMessage = <any>error);
}
ngOnInit() {
this.getEntries()
}
constructor(private _flotService:FlotService) {
this.name = 'Angular2'
this.splineOptions = {
series: {
lines: { show: true },
points: {
radius: 3,
show: true
}
}
};
this.dataset = [{label: "line1",color:"blue",data:null]}];
}
}
For some reason data change cannot project to "flot" component
here is link to plunk
Please help
The problem is
entries => this.dataset[0].data = entries,
because only the inner state of the bound value is changed and Angular2 change detection doesn't observe the content only the value or reference itself.
A workaround would be to create a new array with the same content
this._flotService.getFlotEntries().then(
entries => {
this.dataset[0].data = entries;
this.dataset = this.dataset.slice();
},
In your case an additional event could work that notifies the child component that updates have happended.
Besides Günter's answer, another option is to implement your own change detection inside ngDoCheck() which will be called when your data comes back from the server:
ngDoCheck() {
if(this.dataset[0].data !== null && !this.dataPlotted) {
console.log('plotting data');
let plotArea = $(this.el.nativeElement).find('div').empty();
$.plot( plotArea, this.dataset, this.options);
this.dataPlotted = true;
}
}
I feel this is a cleaner approach, since we don't have to write code a specific way just to satisfy/trigger Angular change detection. But alas, it is less efficient. (I hate it when that happens!)
Also, the code you have in ngOnChanges() can be moved to ngOnInit().
Plunker
As Günter already mentioned, ngOnChanges() isn't called because the dataset array reference doesn't change when you fill in your data. So Angular doesn't think any input properties changed, so ngOnChanges() isn't called. ngDoCheck() is always called every change detection cycle, whether or not there are any input property changes.
Yet another option is to use #ViewChild(FlotCmp) in the parent component, which will get a reference to FlotCmp. The parent could then use that reference to call some method, say drawPlot(), on FlotCmp to draw/update the plot when the data arrives.
drawPlot(dataset) {
console.log('plotting data', dataset);
let plotArea = $(this.el.nativeElement).find('div').empty();
$.plot( plotArea, dataset, this.options);
this.dataset = dataset;
}
Plunker
This is more efficient than ngDoCheck(), and it doesn't have the issue I described above with the ngOnChanges() approach.
However, if I were to use this approach, I would rework the code somewhat, since I don't like how dataset is currently an input property, but then drawPlot() gets the data passed in via a function argument.

Trouble w/ Meteor Sorting

I'm trying to add a simple drop down control above a list such that I can sort it by "created" or "title".
The list template is called posts_list.html. In it's helper .js file I have:
posts: function () {
var sortCriteria = Session.get("sortCriteria") || {};
return Posts.find({},{sort: {sortCriteria: 1}});
}
Then, I have abstracted the list into another template. From here I have the following click event tracker in the helper.js
"click": function () {
// console.log(document.activeElement.id);
Session.set("sortCriteria", document.activeElement.id);
// Router.go('history');
Router.render('profile');
}
Here I can confirm that the right Sort criteria is written to the session. However, I can't make the page refresh. The collection on the visible page never re-sorts.
Frustrating. Any thoughts?
Thanks!
You can't use variables as keys in an object literal. Give this a try:
posts: function() {
var sortCriteria = Session.get('sortCriteria');
var options = {};
if (sortCriteria) {
options.sort = {};
options.sort[sortCriteria] = 1;
}
return Posts.find({}, options);
}
Also see the "Variables as keys" section of common mistakes.
thanks so much for that. Note I've left commented out code below to show what I pulled out. If I required a truly dynamic option, versus the simply binary below, I would have stuck w/ the "var options" approach. What I ended up going with was:
Template.postList.helpers({
posts: function () {
//var options = {};
if (Session.get("post-list-sort")) {
/*options.sort = {};
if (Session.get("post-list-sort") == "Asc") {
options.sort['created'] = 1;
} else {
options.sort['created'] = -1;
}*/
//return hunts.find({}, options);}
console.log(Session.get("hunt-list-sort"));
if (Session.get("hunt-list-sort") == "Asc") {
return Hunts.find({}, {sort: {title: 1}});
}
else {
return Hunts.find({}, {sort: {title: -1}});
};
}
}
});

Resources