SwiftUI - Changing struct data set in ForEach? - struct

I'm new to programming and SwiftUI and I am making this app where users can select these buttons labeled A-D. They may choose more than 1, and I am hoping that when they click on the button, the background colour will change from grey to green. However, if I replace "// Here" in the code at the bottom with
Data.Selected = true
Data.Colour = .green
I get an error saying "Cannot assign to property: 'Data' is a 'let' constant". I understand what that means, but I don't know how to change Data to var. I tried typing var in front of "Data in" but I got this error instead "Consecutive statements on a line must be separated by ';'". Is there anyway I can directly modify Data/ButtonsData? Or is there a workaround?
struct Buttons: Hashable {
var Crit: String
var Selected: Bool
var Colour: Color
}
var ButtonsData = [
Buttons(Crit: "A", Selected: false, Colour: Color(.systemGray4)),
Buttons(Crit: "B", Selected: false, Colour: Color(.systemGray4)),
Buttons(Crit: "C", Selected: false, Colour: Color(.systemGray4)),
Buttons(Crit: "D", Selected: false, Colour: Color(.systemGray4))
]
struct CritView: View {
#Binding var CritBoard: Bool
#Binding var BackgroundColor: Color
var body: some View {
ZStack(alignment: .topLeading) {
ScrollView(.vertical, showsIndicators: false) {
HStack(spacing: 15) {
ForEach(ButtonsData, id: \.self) { Data in
Button(action: {
// HERE
}) {
Text(Data.Crit)
.font(.system(size: 30))
}
.frame(width: 65, height: 55)
.background(Data.Colour)
.cornerRadius(10)
}
}
.padding(.top, 50)
}
.frame(width: UIScreen.main.bounds.width, height: UIScreen.main.bounds.height/8)
.padding(.bottom, UIApplication.shared.windows.first?.safeAreaInsets.bottom)
.background(Color(.white))
.cornerRadius(25)
Button(action: {
self.CritBoard.toggle()
self.BackgroundColor = .white
}) {
Image(systemName: "xmark").foregroundColor(.black)
}.padding(25)
}
}
}

Here is possible solution - create array of index/value tuples and modify your data in original container by index:
ForEach(Array(ButtonsData.enumerated()), id: \.element) { i, Data in
Button(action: {
ButtonsData[i].Selected = true
ButtonsData[i].Colour = .green
}) {
Text(Data.Crit)
.font(.system(size: 30))
}
.frame(width: 65, height: 55)
.background(Data.Colour)
.cornerRadius(10)
}

Well, I don't think a lot of people would actually have the same/similar problem but this is my working code. The code uses both #Aspersi's answer as well as the code in a Hacking with Swift article. (it might not be the most simplified code, but it works right now at least)
ForEach(Array(ButtonsData.enumerated()), id: \.element) { i, Data in
Button(action: {
self.AllData[i].Selected.toggle()
if self.AllData[i].Selected == true {
self.AllData[i].Colour = .green
} else {
self.AllData[i].Colour = Color(.systemGray4)
}
}) {
Text(Data.Crit)
.font(.system(size: 30))
}
.frame(width: 65, height: 55)
.background(self.AllData[i].Colour)
.cornerRadius(10)
}
Full code below
struct Buttons: Hashable {
var Crit: String
var Selected: Bool = false
var Colour: Color
}
var ButtonsData = [
Buttons(Crit: "A", Selected: false, Colour: Color(.systemGray4)),
Buttons(Crit: "B", Selected: false, Colour: Color(.systemGray4)),
Buttons(Crit: "C", Selected: false, Colour: Color(.systemGray4)),
Buttons(Crit: "D", Selected: false, Colour: Color(.systemGray4))
]
struct CritView: View {
#Binding var CritBoard: Bool
#Binding var BackgroundColor: Color
#State private var AllData = ButtonsData
var body: some View {
ZStack(alignment: .topLeading) {
ScrollView(.vertical, showsIndicators: false) {
HStack(spacing: 15) {
ForEach(Array(ButtonsData.enumerated()), id: \.element) { I, Data in
Button(action: {
self.AllData[i].Selected.toggle()
if self.AllData[i].Selected == true {
self.AllData[i].Colour = .green
} else {
self.AllData[i].Colour = Color(.systemGray4)
}
}) {
Text(Data.Crit)
.font(.system(size: 30))
}
.frame(width: 65, height: 55)
.background(self.AllData[i].Colour)
.cornerRadius(10)
}
}
.padding(.top, 50)
}
.frame(width: UIScreen.main.bounds.width, height: UIScreen.main.bounds.height/8)
.padding(.bottom, UIApplication.shared.windows.first?.safeAreaInsets.bottom)
.background(Color(.white))
.cornerRadius(25)
Button(action: {
self.CritBoard.toggle()
self.BackgroundColor = .white
}) {
Image(systemName: "xmark").foregroundColor(.black)
}.padding(25)
}
}
}

Related

how to set background color in excel js, depending on the value?

How can I get the values of a column and paint the background color depending on the value.
I have the column: "Stock" and I need to know when the value is 0 to make the background red.
in this way I paint the first row of the header. Is it possible to implement the logic to obtain the mentioned values?
ws.eachRow((row, rowNumber) => {
row.eachCell((cell) => {
console.log(rowNumber);
if (rowNumber == 1) {
cell.fill = {
type: "pattern",
pattern: "solid",
fgColor: { argb: "2563EB" },
};
}
cell.font = {
color: { argb: "FFFFFF" },
bold: true,
};
cell.border = {
top: { style: "thin" },
left: { style: "thin" },
bottom: { style: "thin" },
right: { style: "thin" },
};
});
row.commit();
});
ws.addRows(arrProducts);
.......

Link:dbclick event with vertex Adding set to true [Jointjs] [Rappid]

I am unable to get Link:dbclick event on double click of link with vertexAdding: set to true.
with Vertex Adding set to false the dbclick event works fine.
i am using link:pointerup to add Tool view to link like this::
this.paper.on('link:pointerup', (linkView) => {
var tools;
var ns = joint.linkTools;
var toolsView = new joint.dia.ToolsView({
name: 'link:pointerup',
tools: [
new ns.Vertices(),
new ns.Vertices({ vertexAdding: true}),
new ns.Remove({ focusOpacity: 0.5,
distance: 5,
offset: 10}),
new ns.TargetArrowhead(),
new ns.Segments(),
new ns.Boundary({ padding: 10 }),
]
});
joint.ui.Halo.clear(this.paper);
joint.ui.FreeTransform.clear(this.paper);
this.paper.removeTools();
linkView.addTools(toolsView);
});
For Link Creation:::
export class Link2 extends joint.shapes.standard.Link {
defaults() {
return joint.util.defaultsDeep({
router: {
name: 'normal',
},
connector: {
name: 'normal',
},
labels: [],
attrs: {
line: {
stroke: '#284f96',
strokeDasharray: '0',
strokeWidth: 1,
fill: 'none',
sourceMarker: {
d: 'M 0 0 0 0',
fill: {
type: 'color-palette',
group: 'marker-source',
when: { ne: { 'attrs/line/sourceMarker/d': 'M 0 0 0 0' } },
index: 2
},
},
targetMarker: {
d: ' M 0 -3 -6 0 0 3 z',
},
}
}
}, joint.shapes.standard.Link.prototype.defaults);
}
defaultLabel = {
attrs: {
rect: {
fill: '#ffffff',
stroke: '#8f8f8f',
strokeWidth: 2,
refWidth: 10,
refHeight: 10,
refX: 0,
refY: 0
}
}
};
getMarkerWidth(type: any) {
const d = (type === 'source') ? this.attr('line/sourceMarker/d') : this.attr('line/targetMarker/d');
console.log("marker width", this.getDataWidth(d));
return this.getDataWidth(d);
}
getDataWidth = memoize(function (d: any) {
return (new joint.g.Rect(d)).bbox().width;
});
static connectionPoint(line: any, view: any, magnet: any, opt: any, type: any, linkView: any) {
// const markerWidth = linkView.model.getMarkerWidth(type);
opt = { offset: 5, stroke: true };
// connection point for UML shapes lies on the root group containg all the shapes components
const modelType = view.model.get('type');
if (modelType.indexOf('uml') === 0) opt.selector = 'root';
// taking the border stroke-width into account
if (modelType === 'standard.InscribedImage') opt.selector = 'border';
return joint.connectionPoints.boundary.call(this, line, view, magnet, opt, type, linkView);
}
}
and i am using following code to get double click event on Link:::
this.rappid.paper.on(
"link:pointerdblclick", (linkView) => {
// linkView.model.attr("priority",0);
console.log("linkview", linkView);
// this.currentLink = linkView
this.setState({ modalView: "lableModal", visible: true, currentLink: linkView });
});

Adding joint.dia.ToolsView to link, Unable to get double click event. [JointJS] [RappidJS]

After adding tool view to links on single click over link I am unable to get the link's double click event. I am using react to develop a interactive graph application using RappidJS library. Please help.
i am using link:pointerup to add Tool view to link like this::
this.paper.on('link:pointerup', (linkView) => {
var tools;
var ns = joint.linkTools;
var toolsView = new joint.dia.ToolsView({
name: 'link:pointerup',
tools: [
new ns.Vertices(),
new ns.Remove({ focusOpacity: 0.5,
distance: 5,
offset: 10}),
new ns.TargetArrowhead(),
new ns.Segments(),
new ns.Boundary({ padding: 10 }),
]
});
joint.ui.Halo.clear(this.paper);
joint.ui.FreeTransform.clear(this.paper);
this.paper.removeTools();
linkView.addTools(toolsView);
});
For Link Creation:::
export class Link2 extends joint.shapes.standard.Link {
defaults() {
return joint.util.defaultsDeep({
router: {
name: 'normal',
},
connector: {
name: 'normal',
},
labels: [],
attrs: {
line: {
stroke: '#284f96',
strokeDasharray: '0',
strokeWidth: 1,
fill: 'none',
sourceMarker: {
d: 'M 0 0 0 0',
fill: {
type: 'color-palette',
group: 'marker-source',
when: { ne: { 'attrs/line/sourceMarker/d': 'M 0 0 0 0' } },
index: 2
},
},
targetMarker: {
d: ' M 0 -3 -6 0 0 3 z',
},
}
}
}, joint.shapes.standard.Link.prototype.defaults);
}
defaultLabel = {
attrs: {
rect: {
fill: '#ffffff',
stroke: '#8f8f8f',
strokeWidth: 2,
refWidth: 10,
refHeight: 10,
refX: 0,
refY: 0
}
}
};
getMarkerWidth(type: any) {
const d = (type === 'source') ? this.attr('line/sourceMarker/d') : this.attr('line/targetMarker/d');
console.log("marker width", this.getDataWidth(d));
return this.getDataWidth(d);
}
getDataWidth = memoize(function (d: any) {
return (new joint.g.Rect(d)).bbox().width;
});
static connectionPoint(line: any, view: any, magnet: any, opt: any, type: any, linkView: any) {
// const markerWidth = linkView.model.getMarkerWidth(type);
opt = { offset: 5, stroke: true };
// connection point for UML shapes lies on the root group containg all the shapes components
const modelType = view.model.get('type');
if (modelType.indexOf('uml') === 0) opt.selector = 'root';
// taking the border stroke-width into account
if (modelType === 'standard.InscribedImage') opt.selector = 'border';
return joint.connectionPoints.boundary.call(this, line, view, magnet, opt, type, linkView);
}
}
and i am using following code to get double click event on Link:::
this.rappid.paper.on(
"link:pointerdblclick", (linkView) => {
// linkView.model.attr("priority",0);
console.log("linkview", linkView);
// this.currentLink = linkView
this.setState({ modalView: "lableModal", visible: true, currentLink: linkView });
});

Text Ellipsis in bubble chart

i'm using bubble chart from Highcharts, the label text inside of the bubbles is dynamic and sometimes can be bigger than the bubble itself,
I wonder if there's a way to make the text ellipsis according to the size of the bubble that contain it?
containerOptions = {
chart: {
type: 'bubble',
renderTo: $(container)[0],
events: {
drilldown: function (e) {
if (!e.seriesOptions) {
var chart = this,
drilldowns = {
'Animals': {
name: 'Animals',
data: [
{name: 'Dogs', y:2, x:10, z: 7, drilldown: true},
{name: 'Cats', y:4, x:12, z: 7}
]
},
'Dogs': {
name:"Dogs",
data: [
{name: 'Pitbull', y:3.7, x:7.6, z: 5, drilldown: false},
{name: 'German shepherd', y:6.7, x:6.9, z: 5, drilldown: false}
]
}
},
series = drilldowns[e.point.name];
chart.showLoading('Loading..');
setTimeout(function () {
chart.hideLoading();
chart.addSeriesAsDrilldown(e.point, series);
}, 1000);
}
}
}
},
plotOptions: {
series: {
borderWidth: 0,
dataLabels: {
enabled: true,
style: { color: 'red' },
format: '{point.name}'
}
}
},
series: [{
name: 'Things',
colorByPoint: true,
data: [{
name: 'Animals',
y: 5,
x: 1,
z: 9,
drilldown: true
}, {
name: 'Fruits',
y: 2,
x: 9,
z: 9,
drilldown: false
}
]
}],
drilldown: {
series: [],
drillUpButton: {
relativeTo: 'spacingBox',
position: {
y: 0,
x: 0
}
}
}
}
}
You can loop through the data labels on load/redraw event and add/remove ellipsis according to the bubble's width and text's width.
function applyEllipsis() {
var series = this.series[0];
var options = series.options.dataLabels;
series.points.forEach(p => {
var r = p.marker.radius;
var label = p.dataLabel;
var text = label.text.textStr;
var bbox = label.getBBox(true);
while (bbox.width > 2 * r && text.length !== 1) {
text = text.slice(0, -1);
p.dataLabel.attr({
text: text + '\u2026'
});
bbox = label.getBBox(true);
}
p.dataLabel.align({
width: bbox.width,
height: bbox.height,
align: options.align,
verticalAlign: options.verticalAlign
}, null, p.dlBox);
});
}
Attach the function on load/redraw
Highcharts.chart('container', {
chart: {
type: 'bubble',
events: {
load: applyEllipsis,
redraw: applyEllipsis
}
},
example: http://jsfiddle.net/12d997o4/

Highchart y axis need 2 category each having 3 sub category

I am using high chart version v4.0.4, i have worked bar chart like mentioned below i need output with there bar for every year details given below
I am working on below high chart code
var data_first_vd = 0;
var data_second_vd = 0;
var glb_data_ary = [];
var dynamicval1 = 5.85572581;
var dynamicval2 = 0.16091656;
if((dynamicval1>1) || (dynamicval2>1)){
var data_tit = 'Value';
var data_val = '${value}';
var prdelvalaxis = 1000000;
}else{
prdelvalaxis = 1000;
data_tit = "Value";
data_val = "${value}";
}
data_first_vd=5.86;
data_second_vd =0.16;
var data_first_ud =1397.128;
var data_second_ud = 28.145;
data_first_ud_lbl = '1.40M';
data_second_ud_lbl = '28K';
data_first_vd_lbl = '5.86M';
data_second_vd_lbl = '161K';
data_first_vd_lbl_xaxis = '$5.86M';
data_second_vd_lbl_xaxis = '$161K';
var ud1 = 1397;
var ud2 = 28;
var vd1 = 6;
var vd2 = 0;
$('#id').highcharts({
credits: { enabled: false },
chart: {
type: 'bar',
height: 200,
marginLeft: 120,
marginBottom:50,
marginTop: 47,
marginRight:30,
plotBorderWidth: 0,
},
title: {
text: null
},
xAxis: {
drawHorizontalBorders: false,
labels: {
groupedOptions: [{
rotation: 270,
}],
rotation: 0,
align:'center',
x: -30,
y: 5,
formatter: function () {
if (curYear === this.value) {
return '<span style="color:#6C9CCC;">' + this.value + '</span>';
}
else if (prevYear === this.value) {
return '<span style="color: #ED7D31;">' + this.value + '</span>';
}
else if ('VALUE' === this.value) {
return '<span style="color:#942EE1;">' + this.value + '</span>';
}
else{
return '<span style="color: #E124D2;">' + this.value + '</span>';
}
},
useHTML:true
},
categories: [{
name: "UNITS",
categories: [2017, 2016]
},{
name: "VALUE",
categories: [2017, 2016]
}
],
},
yAxis: [{ // Primary yAxis
labels: {
format: data_val,
formatter: function () {
if(this.value!=0){
return '$'+valueConverter_crt(this.value*prdelvalaxis);
}else{
return this.value;
}
},
style: {
color: '#942EE1'
}
},
title: {
text: "<span style='font-size: 12px;'>"+data_tit+"</span>",
style: {
color: '#942EE1'
},
useHTML:true
}
}, { // Secondary yAxis
title: {
text: "<span style='font-size: 12px;'>Units</span>",
style: {
color: '#E124D2'
},
useHTML:true
},
labels: {
format: '{value}',
formatter: function () {
if(this.value!=0){
return cal_pro_del_xaxis(this.value);
}else{
return this.value;
}
},
style: {
color: '#E124D2'
}
},
opposite: true
}],
tooltip: { enabled: false },
exporting: { enabled: false },
legend: { enabled: false },
plotOptions: {
series: {
dataLabels: {
inside: true,
align: 'left',
enabled: true,
formatter: function () {
return this.point.name;
},
color: '#000000',
},
stacking: false,
pointWidth: 15,
groupPadding: 0.5
},
},
series: [{
yAxis: 1,
data: [{y:1397.128,name:data_first_ud_lbl,color:'#6C9CCC'},{y:28.145,name:data_second_ud_lbl,color:'#ED7D31'}],
}, {
data: [null,null,{y:5.86,name:data_first_vd_lbl_xaxis,color:'#6C9CCC'},{y:0.16,name:data_second_vd_lbl_xaxis,color:'#ED7D31'}],
}]
});
I need out put like below chart This chart i draw in paint.
Here 3 bar added in every year
Please help me to achieve this

Resources