Rating alert/feature using Ionic alert - ionic-framework

I wanted to create a rating alert feature using Ionic alert. This is how it looks.
Go through the answer for detailed codes.
This works properly in all browsers and platforms(as much as I have tested on).

The JS/TS part to create alert -
showRatingAlert() {
const alert = this.alertCtrl.create({
title: 'Rate this app',
cssClass: 'rating_alert',
buttons: [
{ text: '1',
handler: data => { return false; }, // prevents from closing the alert
cssClass: 'rate-icon-button'
},
{ text: '2',
handler: data => { return false; }, // prevents from closing the alert
cssClass: 'rate-icon-button'
},
{ text: '3',
handler: data => { return false; }, // prevents from closing the alert
cssClass: 'rate-icon-button'
},
{ text: '4',
handler: data => { return false; }, // prevents from closing the alert
cssClass: 'rate-icon-button'
},
{ text: '5',
handler: data => { return false; }, // prevents from closing the alert
cssClass: 'rate-icon-button'
},
{ text: 'Later', handler: data => { }, cssClass: 'rate-action-button rate-later' },
{ text: 'Submit', handler: data => { }, cssClass: 'rate-action-button rate-now' },
],
});
// add event listener for icon buttons in ask rating alert popup
setTimeout(()=>{
const buttonElms: NodeList = document.querySelectorAll('.rating_alert .alert-button-group .rate-icon-button');
for(let index = 0; index < buttonElms.length; index++) {
buttonElms[index].addEventListener('click', this.selectedRatingHandler);
}
}, 500);
}
selectedRatingHandler = (event: MouseEvent)=>{
// handler for clicked rating icon button
let target: any = event.target; // target element
let siblings: HTMLCollection = target.parentElement.children; // list of all siblings
for(let index = 0; index < siblings.length; index++){
siblings[index].classList.remove('selected-rating'); // remove selected class from all siblings
}
target.classList.add('selected-rating'); // add selected class to currently selected item
};
The CSS/SCSS part to style elements/buttons -
.rating_alert{
border-radius: 8px;
.alert-wrapper{ border-radius: 8px; }
.alert-button-group{
padding:0;
margin-top: 10px;
flex-direction: row;
justify-content: space-around;
// flex-direction: row; justify-content: space-around;
display: flex;
flex-wrap: wrap;
}
button{
flex: 1 1 20%;
&.rate-icon-button{
width: 40px;
max-width: 20%;
min-width: auto;
height: 40px;
margin: 0 !important;
background-image:url('../assets/img/emotions.png');
background-repeat: no-repeat;
background-position: center;
border: none;
.button-inner{
visibility: hidden;
}
&:nth-child(1){ background-position-y: 3px; }
&:nth-child(2){ background-position-y: -36px; }
&:nth-child(3){ background-position-y: -76px; }
&:nth-child(4){ background-position-y: -114px; }
&:nth-child(5){ background-position-y: -153px; }
&.selected-rating{
position: relative;
overflow: visible;
&:after{
content: '';
position: absolute;
bottom: -4px;
height: 4px;
background-color: #2E6DFF;
width: 80%;
left: 10%;
}
}
}
&.rate-action-button{
text-align: center;
margin-top: 20px;
margin-right: 0;
border-top: 1px solid #c3c3c3;
.button-inner{
justify-content: center;
}
&.rate-later{
border-right: 1px solid #c3c3c3;
}
&.rate-now{
border-left: 1px solid #c3c3c3;
}
}
}
}
The image file used in this example.

Related

How to make the hAxis scrollable in Google Chart API

I would like to make my hAxis scrollable and show all my hAxis label values. On the hAxis, the IDs of certain stores are displayed. I have written the following code for the graph:
let array = [[
'Store', 'Amount',
]];
for (let item of response){
array.push([item.store_id.toString(), item.count]);
}
console.log(array);
let data = google.visualization.arrayToDataTable(array);
let options = {
chartArea: {
top: 20,
height: '65%',
width: '75%'
},
hAxis: {
title: 'Store ID',
slantedText: true,
slantedTextAngle: 90,
},
bar: {
groupWidth: 1
},
legend: {
position: 'none'
}
};
let chart = new google.visualization.ColumnChart(document.getElementById('chart-canvas-3'));
chart.draw(data, options);
And the .scss code:
.card-image {
display: block;
box-sizing: border-box;
position: relative;
overflow-x: scroll;
margin-bottom: sz(10pt);
padding-left: 10px;
padding-top: 10px;
padding-right: 10px;
.card-image-inner {
width: 100%;
}
}
So my question is, how can I display all 53 store ID labels on the hAxis (with scroll functionality)?
Thanks!
Current graph

How to resize chart.js graph dynamically for responsive sites?

Hej,
I know this has been answered in one way or the other many times, but I really can't get it work in my context with flex-boxes.
I have fixed elements, such as headers and footers on my page, and a chart that shall take up all the remaining space in between. While it appears to be working on first glance, you can see that the text and numbers are blurred and not properly scaled when the window is resized.
How can I fix this, or have chart.js redraw the canvas upon resize?
Check out my fiddle
https://jsfiddle.net/fg9pd1b3/
<body>
<div class="container">
<div class="fixed">
header
</div>
<div id="chartcontainer">
<canvas id="chartJSContainer"></canvas>
</div>
<div class="fixed">
footer
</div>
</div>
</body>
CSS:
html,
body {
height: 100vh;
min-height: 100vh;
max-height: 100vh;
margin: 0;
padding: 0;
}
.container {
width: 80%;
height: 100%;
margin: 0 auto;
display: flex;
flex-direction: column;
flex-basis: 100%;
flex-grow: 1;
background: #CCC;
}
.p {
text-align: center;
font-size: 14px;
padding-top: 140px;
}
.fixed {
background: black;
height: 100px;
min-height: 100px;
color: white;
text-align: center;
}
#chartcontainer {
position: relative;
min-height: 200px;
display: flex;
flex-grow: 1;
}
canvas {
width: 100% !important;
height: 100% !important;
}
JS:
Chart.defaults.global.responsive = true;
var options = {
type: 'line',
data: {
labels: ["Red", "Blue", "Yellow", "Green", "Purple", "Orange"],
datasets: [{
label: '# of Votes',
data: [12, 19, 3, 5, 2, 3],
borderWidth: 1
},
{
label: '# of Points',
data: [7, 11, 5, 8, 3, 7],
borderWidth: 1
}
]
},
options: {
scales: {
yAxes: [{
ticks: {
reverse: false
}
}]
}
}
}
var ctx = document.getElementById('chartJSContainer').getContext('2d');
new Chart(ctx, options);
You have given fixed min-height to the canvas div which restricts the chart to further change it's dimensions. So, the following code has auto height and width automates to 100% which gives flexible chart.
#chartcontainer {
position:relative;
min-height:auto;
width:100%;
display: flex;
flex-grow:1;
}

Mapbox: Visibility none for all layers when one is visible

Following this Mapbox example (Show and hide layers: https://docs.mapbox.com/mapbox-gl-js/example/toggle-layers/), I have been able to toggle between several layers on a map. But what I would like to achieve is when Layer 1 is visible, the visibility of Layer 2 and Layer 3 is none, when Layer 2 is visible, the visibility of Layer 1 and Layer 3 is none, etc.
Here's a fiddle example with the code I've written so far:
https://jsfiddle.net/reL53uo1/
Here's the logic for the toggle part:
// enumerate ids of the layers
var toggleableLayerIds = ['contours', 'museums', 'contours2'];
// set up the corresponding toggle button for each layer
for (var i = 0; i < toggleableLayerIds.length; i++) {
var id = toggleableLayerIds[i];
var link = document.createElement('a');
link.href = '#';
link.className = 'active';
link.textContent = id;
link.onclick = function(e) {
var clickedLayer = this.textContent;
e.preventDefault();
e.stopPropagation();
var visibility = map.getLayoutProperty(clickedLayer, 'visibility');
// toggle layer visibility by changing the layout object's visibility property
if (visibility === 'visible') {
map.setLayoutProperty(clickedLayer, 'visibility', 'none');
this.className = '';
} else {
this.className = 'active';
map.setLayoutProperty(clickedLayer, 'visibility', 'visible');
}
};
var layers = document.getElementById('menu');
layers.appendChild(link);
What would it be the most efficient approach to achieve this? Thanks.
You could change your onClick function so that instead of toggling individual layers off-and-on it activates the one you want and hides the others. You could do this with a for loop:
link.onclick = function(e) {
var clickedLayer = this.textContent;
e.preventDefault();
e.stopPropagation();
for (var j = 0; j < toggleableLayerIds.length; j++) {
if (clickedLayer === toggleableLayerIds[j]) {
layers.children[j].className = 'active';
map.setLayoutProperty(toggleableLayerIds[j], 'visibility', 'visible');
}
else {
layers.children[j].className = '';
map.setLayoutProperty(toggleableLayerIds[j], 'visibility', 'none');
}
}
};
Additionally, you should set the 'visibility' property of each layer to 'none' in your addLayer() function so that they are hidden until selected.
'layout': {
// make layer invisible by default
'visibility': 'none',
'line-join': 'round',
'line-cap': 'round'
},
I made these changes to your JSFiddle code and pasted below:
mapboxgl.accessToken = 'pk.eyJ1Ijoid2FzaGluZ3RvbnBvc3QiLCJhIjoibWJkTGx1SSJ9.6cMdwgs-AYrRtQsEkXlHqg';
var map = new mapboxgl.Map({
container: 'map',
style: 'mapbox://styles/mapbox/streets-v11',
zoom: 15,
center: [-71.97722138410576, -13.517379300798098]
});
map.on('load', function() {
// add source and layer for museums
map.addSource('museums', {
type: 'vector',
url: 'mapbox://mapbox.2opop9hr'
});
map.addLayer({
'id': 'Layer 1',
'type': 'circle',
'source': 'museums',
'layout': {
// make layer invisible by default
'visibility': 'none'
},
'paint': {
'circle-radius': 8,
'circle-color': 'rgba(55,148,179,1)'
},
'source-layer': 'museum-cusco'
});
// add source and layer for contours
map.addSource('contours', {
type: 'vector',
url: 'mapbox://mapbox.mapbox-terrain-v2'
});
map.addLayer({
'id': 'Layer 2',
'type': 'line',
'source': 'contours',
'source-layer': 'contour',
'layout': {
// make layer invisible by default
'visibility': 'none',
'line-join': 'round',
'line-cap': 'round'
},
'paint': {
'line-color': '#877b59',
'line-width': 5
}
});
map.addLayer({
'id': 'Layer 3',
'type': 'line',
'source': 'contours',
'source-layer': 'contour',
'layout': {
// make layer invisible by default
'visibility': 'none',
'line-join': 'round',
'line-cap': 'round'
},
'paint': {
'line-color': 'yellow',
'line-width': 2
}
});
});
// enumerate ids of the layers
var toggleableLayerIds = ['Layer 1', 'Layer 2', 'Layer 3'];
// set up the corresponding toggle button for each layer
for (var i = 0; i < toggleableLayerIds.length; i++) {
var id = toggleableLayerIds[i];
var link = document.createElement('a');
link.href = '#';
link.className = '';
link.textContent = id;
link.onclick = function(e) {
var clickedLayer = this.textContent;
e.preventDefault();
e.stopPropagation();
for (var j = 0; j < toggleableLayerIds.length; j++) {
if (clickedLayer === toggleableLayerIds[j]) {
layers.children[j].className = 'active';
map.setLayoutProperty(toggleableLayerIds[j], 'visibility', 'visible');
}
else {
layers.children[j].className = '';
map.setLayoutProperty(toggleableLayerIds[j], 'visibility', 'none');
}
}
};
var layers = document.getElementById('menu');
layers.appendChild(link);
}
body { margin: 0; padding: 0; }
#map { position: absolute; top: 0; bottom: 0; width: 100%; }
#menu {
background: #fff;
position: absolute;
z-index: 1;
top: 10px;
right: 10px;
border-radius: 3px;
width: 120px;
border: 1px solid rgba(0, 0, 0, 0.4);
font-family: 'Open Sans', sans-serif;
}
#menu a {
font-size: 13px;
color: #404040;
display: block;
margin: 0;
padding: 0;
padding: 10px;
text-decoration: none;
border-bottom: 1px solid rgba(0, 0, 0, 0.25);
text-align: center;
}
#menu a:last-child {
border: none;
}
#menu a:hover {
background-color: #f8f8f8;
color: #404040;
}
#menu a.active {
background-color: #3887be;
color: #ffffff;
}
#menu a.active:hover {
background: #3074a4;
}
<link href="https://api.mapbox.com/mapbox-gl-js/v1.10.0/mapbox-gl.css" rel="stylesheet"/>
<script src="https://api.mapbox.com/mapbox-gl-js/v1.10.0/mapbox-gl.js"></script>
<nav id="menu"></nav>
<div id="map"></div>

how to use multiple custom components in framework in ag-grid-angular?

I have created two custom components, i.e custom tooltip component and custom datepicker component. when im trying to declare in framework it is not working.
Its taking only datepicker custom component not tooltip component. If datepicker component is removed then its taking tooltip component Its considering only one custom component in frameworkComponent.
Please find the below code:
CustomTooltip :
import {Component, ViewEncapsulation} from '#angular/core';
import {ITooltipAngularComp} from "ag-grid-angular";
#Component({
selector: 'tooltip-component',
template: `
<div class="custom-tooltip" [style.background-color]="data.color">
<p>{{tooltipData}}</p>
</div>`,
styles: [
`
:host {
position: absolute;
width: 250px;
height: 60px;
border: 1px solid cornflowerblue;
overflow: hidden;
pointer-events: none;
transition: opacity 1s;
}
:host.ag-tooltip-hiding {
opacity: 0;
}
.custom-tooltip p {
margin: 5px;
white-space: nowrap;
}
`
],
styleUrls: ['./custom-tooltip.component.scss']
})
export class CustomTooltip implements ITooltipAngularComp {
params: any;
data: any;
tooltipData: any;
agInit(params): void {
console.log("params",params.value);
this.params = params;
this.tooltipData=params.value;
this.data = params.api.getRowNode(params.rowIndex).data;
this.data.color = this.params.color || 'white';
}
}
CustomDateComponent:
import {Component, ElementRef, ViewChild} from '#angular/core';
import flatpickr from 'flatpickr'
#Component({
selector: 'app-loading-overlay',
template: `
<div #flatpickrEl class="ag-input-text-wrapper custom-date-filter fa">
<input type='text' data-input />
<a class='input-button' title='clear' data-clear>
<i class='fa fa-times'></i>
</a>
<a class="input-button" title="toggle" data-toggle>
<i class="fa fa-calendar"></i>
</a>
</div>
`,
styles: [
`
.custom-date-filter a {
position: relative;
right: 34px;
color: rgba(0, 0, 0, 0.54);
cursor: pointer;
}
.custom-date-filter:after {
content: '\f073';
display: block;
font-weight: 400;
font-family: 'Font Awesome 5 Free';
position: relative;
right: 25px;
pointer-events: none;
color: rgba(0, 0, 0, 0.54);
}
`
]
})
export class CustomDateComponent {
#ViewChild("flatpickrEl", {read: ElementRef}) flatpickrEl: ElementRef;
private date: Date;
private params: any;
private picker: any;
agInit(params: any): void {
this.params = params;
}
ngAfterViewInit(): void {
// outputs `I am span`
this.picker = flatpickr(this.flatpickrEl.nativeElement, {
onChange: this.onDateChanged.bind(this),
wrap: true
});
this.picker.calendarContainer.classList.add('ag-custom-component-popup');
}
ngOnDestroy() {
console.log(`Destroying DateComponent`);
}
onDateChanged(selectedDates) {
this.date = selectedDates[0] || null;
this.params.onDateChanged();
}
getDate(): Date {
return this.date;
}
setDate(date: Date): void {
this.date = date || null;
this.picker.setDate(date);
}
}
Im trying to use both custom component in one grid i.e:
this.columnDefs = [
{
headerName: 'Request Number', field: 'request_no', sortable: true, filter: 'agNumberColumnFilter'
},
{
headerName: 'Request Date', field: 'created_at', sortable: true, width: 300,
filter: "agDateColumnFilter",
filterParams: {
comparator: function (filterLocalDateAtMidnight, cellValue) {
var dateAsString = cellValue;
var dateParts = dateAsString.split("/");
var cellDate = new Date(Number(dateParts[2]), Number(dateParts[1]) - 1, Number(dateParts[0]));
if (filterLocalDateAtMidnight.getTime() === cellDate.getTime()) {
return 0;
}
if (cellDate < filterLocalDateAtMidnight) {
return -1;
}
if (cellDate > filterLocalDateAtMidnight) {
return 1;
}
}
}
},
{ headerName: 'Requested By', field: 'user_name', sortable: true, filter: 'agTextColumnFilter', tooltipField: 'user_name' },
{
headerName: 'Client', field: 'borrower_name', sortable: true, filter: 'agTextColumnFilter',
tooltipField: 'borrower_name', tooltipComponentParams: { color: "#ececec" }, width: 200
},
{
headerName: 'Debtor', field: 'customer_name', sortable: true, filter: 'agTextColumnFilter',
cellStyle: { color: 'blue', cursor: 'pointer' }, tooltipField: 'customer_name', width: 200
},
{
headerName: 'Current Limit', field: 'current_limit', sortable: true, filter: 'agNumberColumnFilter',
cellStyle: { textAlign: 'right' },
cellRenderer: this.CurrencyCellRenderer
},
{
headerName: 'Requested Limit', field: 'requested_limit', sortable: true, filter: 'agNumberColumnFilter',
cellStyle: { textAlign: 'right' },
cellRenderer: this.CurrencyCellRenderer
},
{
headerName: 'Approved Limit', field: 'approved_limit', sortable: true, filter: 'agNumberColumnFilter',
cellStyle: { textAlign: 'right' },
cellRenderer: this.CurrencyCellRenderer
},
{ headerName: 'Status', field: 'status', sortable: true, filter: 'agTextColumnFilter', width: 120, },
{ headerName: 'Comment', field: 'comment', sortable: true, filter: 'agTextColumnFilter', tooltipField: 'comment', width: 200 },
{
headerName: "",
suppressMenu: true,
suppressSorting: false,
cellClass: 'action-class',
width: 120,
template:
`<i class="fa fa-pencil-square-o" aria-hidden="true" data-action-type="view" pTooltip="Edit Queue" tooltipPosition="top"></i>
<i class="fa fa-info-circle" aria-hidden="true" data-action-type="history" pTooltip="View Comment History" tooltipPosition="top"></i>`
}
];
this.defaultColDef = {
enableValue: true,
sortable: true,
tooltipComponent: "customTooltip",
resizable: true
};
this.frameworkComponents = {
customTooltip: CustomTooltip,
agDateInput: CustomDateComponent };
Seems it's a custom tooltip issue (somewhere inside ag-grid), will try to inform the team and describe the bug.
In your case, you can specify your component (CustomDateComponent) directly via cellEditorFramework rather than cellEditor, without internal declaration in frameworkComponents block.
{
headerName: "Date",
field: "date",
width: 190,
editable:true,
cellEditorFramework:CustomDateComponent
}
Just for info: your CustomDateComponent not yet ready to be used as cellEditor

Want to show the dataset labels

I am trying ChartJS line chart with a set of data.I have multiple lines in it. I want to show the tooltip for the data. but I am not able to.
My code is
new Chart(canvas).Line(data,{
responsive : true,
animation: true,
barValueSpacing : 5,
barDatasetSpacing : 1,
tooltipFillColor: "rgba(0,0,0,0.8)",
multiTooltipTemplate: "<%= datasetLabel %> - <%= value %>",
showTooltips:true
});
currently my view of line chart is
What I want is like enter image description here
You can use the custom tooltips option. The below code is adapted from the example at https://github.com/nnnick/Chart.js/blob/master/samples/line-customTooltips.html
Preview
CSS
#canvas-holder {
width: 500px;
height: 300px;
}
#chartjs-tooltip {
opacity: 0;
position: absolute;
background: rgba(0, 0, 0, .7);
font-size: 12px;
color: white;
padding: 5px;
border-radius: 3px;
-webkit-transition: all .1s ease;
transition: all .1s ease;
pointer-events: none;
}
.chartjs-tooltip-section{
padding: 1px;
}
HTML
<div id="canvas-holder">
<canvas id="chart" />
</div>
<div id="chartjs-tooltip"></div>
Script
function CustomLabel(short, long) {
this.short = short;
this.long = long;
}
CustomLabel.prototype.toString = function () {
return this.short;
}
var data = {
labels: [
new CustomLabel("January", "January 11"),
new CustomLabel("February", "February 12"),
new CustomLabel("March", "March 13"),
new CustomLabel("April", "April 14"),
new CustomLabel("May", "May 15"),
new CustomLabel("June", "June 16"),
new CustomLabel("July", "July 17")],
datasets: [
{
label: "My First dataset",
fillColor: "rgba(220,220,220,0.2)",
strokeColor: "rgba(220,220,220,1)",
pointColor: "rgba(220,220,220,1)",
pointStrokeColor: "#fff",
pointHighlightFill: "#fff",
pointHighlightStroke: "rgba(220,220,220,1)",
data: [65, 59, 80, 81, 56, 55, 40]
},
{
label: "My Second dataset",
fillColor: "rgba(151,187,205,0.2)",
strokeColor: "rgba(151,187,205,1)",
pointColor: "rgba(151,187,205,1)",
pointStrokeColor: "#fff",
pointHighlightFill: "#fff",
pointHighlightStroke: "rgba(151,187,205,1)",
data: [28, 48, 40, 19, 86, 27, 90]
}
]
};
var ctx = document.getElementById("chart").getContext("2d");
new Chart(ctx).Line(data, {
responsive: true,
customTooltips: function (tooltip) {
var tooltipEl = $('#chartjs-tooltip');
if (!tooltip) {
tooltipEl.css({
opacity: 0
});
return;
}
tooltipEl.removeClass('above below');
tooltipEl.addClass(tooltip.yAlign);
var innerHtml = ['<div class="chartjs-tooltip-section">',
' <span>' + tooltip.title.long + '</span>',
'</div>'
].join('');
for (var i = tooltip.labels.length - 1; i >= 0; i--) {
innerHtml += [
'<div class="chartjs-tooltip-section">',
' <span style="color:' + tooltip.legendColors[i].fill + '">' + data.datasets[i].label + ': ' + tooltip.labels[i] + '</span>',
'</div>'
].join('');
}
tooltipEl.html(innerHtml);
tooltipEl.css({
opacity: 1,
left: tooltip.chart.canvas.offsetLeft + tooltip.x + 'px',
top: tooltip.chart.canvas.offsetTop + tooltip.y + 'px',
fontFamily: tooltip.fontFamily,
fontStyle: tooltip.fontStyle,
});
}
});
Fiddle - http://jsfiddle.net/fLm8c5ee/