| 
 | 1 | +# Magic quadrant  | 
 | 2 | + | 
 | 3 | +```js chart-editor  | 
 | 4 | +// <block:setup:6>  | 
 | 5 | +const META = [[  | 
 | 6 | +  {color: 'rgb(255, 174, 201)', backgroundColor: 'rgba(255, 174, 201, 0.5)', label: 'Niche players'},  | 
 | 7 | +  {color: 'rgb(159, 168, 218)', backgroundColor: 'rgba(159, 168, 218, 0.5)', label: 'Visionaries'},  | 
 | 8 | +], [  | 
 | 9 | +  {color: 'rgb(255, 245, 157)', backgroundColor: 'rgba(255, 245, 157, 0.5)', label: 'Challengers'},  | 
 | 10 | +  {color: 'rgb(165, 214, 167)', backgroundColor: 'rgba(165, 214, 167, 0.5)', label: 'Leaders'},  | 
 | 11 | +]];  | 
 | 12 | +const DATA_COUNT = 12;  | 
 | 13 | +const MIN = 0.5;  | 
 | 14 | +const MAX = 9.5;  | 
 | 15 | + | 
 | 16 | +const data = {  | 
 | 17 | +  datasets: [{  | 
 | 18 | +    data: randomize(),  | 
 | 19 | +    pointRadius: 8  | 
 | 20 | +  }]  | 
 | 21 | +};  | 
 | 22 | +// </block:setup>  | 
 | 23 | + | 
 | 24 | +// <block:annotation1:1>  | 
 | 25 | +const annotation1 = {  | 
 | 26 | +  type: 'box',  | 
 | 27 | +  backgroundColor: (ctx) => gradient(ctx, META[1][0].backgroundColor),  | 
 | 28 | +  xMax: 5,  | 
 | 29 | +  yMin: 5,  | 
 | 30 | +  label: {  | 
 | 31 | +    content: META[1][0].label,  | 
 | 32 | +    position: {  | 
 | 33 | +      x: 'start',  | 
 | 34 | +      y: 'start'  | 
 | 35 | +    }  | 
 | 36 | +  }  | 
 | 37 | +};  | 
 | 38 | +// </block:annotation1>  | 
 | 39 | + | 
 | 40 | +// <block:annotation2:2>  | 
 | 41 | +const annotation2 = {  | 
 | 42 | +  type: 'box',  | 
 | 43 | +  backgroundColor: (ctx) => gradient(ctx, META[1][1].backgroundColor, true),  | 
 | 44 | +  xMin: 5,  | 
 | 45 | +  yMin: 5,  | 
 | 46 | +  label: {  | 
 | 47 | +    content: META[1][1].label,  | 
 | 48 | +    position: {  | 
 | 49 | +      x: 'end',  | 
 | 50 | +      y: 'start'  | 
 | 51 | +    }  | 
 | 52 | +  }  | 
 | 53 | +};  | 
 | 54 | +// </block:annotation2>  | 
 | 55 | + | 
 | 56 | +// <block:annotation3:3>  | 
 | 57 | +const annotation3 = {  | 
 | 58 | +  type: 'box',  | 
 | 59 | +  backgroundColor: (ctx) => gradient(ctx, META[0][0].backgroundColor),  | 
 | 60 | +  xMax: 5,  | 
 | 61 | +  yMax: 5,  | 
 | 62 | +  label: {  | 
 | 63 | +    content: META[0][0].label,  | 
 | 64 | +    position: {  | 
 | 65 | +      x: 'start',  | 
 | 66 | +      y: 'end'  | 
 | 67 | +    }  | 
 | 68 | +  }  | 
 | 69 | +};  | 
 | 70 | +// </block:annotation3>  | 
 | 71 | + | 
 | 72 | +// <block:annotation4:4>  | 
 | 73 | +const annotation4 = {  | 
 | 74 | +  type: 'box',  | 
 | 75 | +  backgroundColor: (ctx) => gradient(ctx, META[0][1].backgroundColor, true),  | 
 | 76 | +  xMin: 5,  | 
 | 77 | +  yMax: 5,  | 
 | 78 | +  label: {  | 
 | 79 | +    content: META[0][1].label,  | 
 | 80 | +    position: {  | 
 | 81 | +      x: 'end',  | 
 | 82 | +      y: 'end'  | 
 | 83 | +    }  | 
 | 84 | +  }  | 
 | 85 | +};  | 
 | 86 | +// </block:annotation4>  | 
 | 87 | + | 
 | 88 | +// <block:utils:5>  | 
 | 89 | +function randomize() {  | 
 | 90 | +  const xValues = Utils.numbers({count: DATA_COUNT, min: MIN, max: MAX});  | 
 | 91 | +  const yValues = Utils.numbers({count: DATA_COUNT, min: MIN, max: MAX});  | 
 | 92 | +  const values = [];  | 
 | 93 | +  for (let i = 0; i < DATA_COUNT; i++) {  | 
 | 94 | +    values.push({x: xValues[i], y: yValues[i], co: 'Company ' + (i + 1)});  | 
 | 95 | +  }  | 
 | 96 | +  return values;  | 
 | 97 | +}  | 
 | 98 | + | 
 | 99 | +function gradient({chart: {ctx}, element}, color, rtl = false) {  | 
 | 100 | +  const g = ctx.createLinearGradient(element.x, element.y, element.x2, element.y);  | 
 | 101 | +  g.addColorStop(rtl ? 1 : 0, color);  | 
 | 102 | +  g.addColorStop(rtl ? 0 : 1, 'transparent');  | 
 | 103 | +  return g;  | 
 | 104 | +}  | 
 | 105 | + | 
 | 106 | +function gridColor(context) {  | 
 | 107 | +  if (context.tick.value === 5) {  | 
 | 108 | +    return 'lightGray';  | 
 | 109 | +  } else if (context.tick.value === 0 || context.tick.value === 10) {  | 
 | 110 | +    return 'lightGray';  | 
 | 111 | +  }  | 
 | 112 | +  return 'transparent';  | 
 | 113 | +}  | 
 | 114 | +// </block:utils>  | 
 | 115 | + | 
 | 116 | +/* <block:config:0> */  | 
 | 117 | +const config = {  | 
 | 118 | +  type: 'scatter',  | 
 | 119 | +  data,  | 
 | 120 | +  options: {  | 
 | 121 | +    layout: {  | 
 | 122 | +      padding: {  | 
 | 123 | +        top: 12  | 
 | 124 | +      }  | 
 | 125 | +    },  | 
 | 126 | +    elements: {  | 
 | 127 | +      boxAnnotation: {  | 
 | 128 | +        borderWidth: 0,  | 
 | 129 | +        label: {  | 
 | 130 | +          drawTime: 'beforeDatasetsDraw',  | 
 | 131 | +          display: true,  | 
 | 132 | +          font: {  | 
 | 133 | +            size: 20  | 
 | 134 | +          }  | 
 | 135 | +        }  | 
 | 136 | +      }  | 
 | 137 | +    },  | 
 | 138 | +    scales: {  | 
 | 139 | +      x: {  | 
 | 140 | +        beginAtZero: true,  | 
 | 141 | +        max: 10,  | 
 | 142 | +        min: 0,  | 
 | 143 | +        grid: {  | 
 | 144 | +          drawTicks: false,  | 
 | 145 | +          color: gridColor  | 
 | 146 | +        },  | 
 | 147 | +        ticks: {  | 
 | 148 | +          display: false  | 
 | 149 | +        },  | 
 | 150 | +        title: {  | 
 | 151 | +          display: true,  | 
 | 152 | +          text: 'Completeness of vision',  | 
 | 153 | +          padding: 10,  | 
 | 154 | +          font: {  | 
 | 155 | +            size: 20,  | 
 | 156 | +            weight: 'bold'  | 
 | 157 | +          }  | 
 | 158 | +        }  | 
 | 159 | +      },  | 
 | 160 | +      y: {  | 
 | 161 | +        display: true,  | 
 | 162 | +        beginAtZero: true,  | 
 | 163 | +        max: 10,  | 
 | 164 | +        min: 0,  | 
 | 165 | +        grid: {  | 
 | 166 | +          drawTicks: false,  | 
 | 167 | +          color: gridColor  | 
 | 168 | +        },  | 
 | 169 | +        ticks: {  | 
 | 170 | +          display: false  | 
 | 171 | +        },  | 
 | 172 | +        title: {  | 
 | 173 | +          display: true,  | 
 | 174 | +          text: 'Ability to execute',  | 
 | 175 | +          padding: 10,  | 
 | 176 | +          font: {  | 
 | 177 | +            size: 20,  | 
 | 178 | +            weight: 'bold'  | 
 | 179 | +          }  | 
 | 180 | +        }  | 
 | 181 | +      }  | 
 | 182 | +    },  | 
 | 183 | +    plugins: {  | 
 | 184 | +      annotation: {  | 
 | 185 | +        common: {  | 
 | 186 | +          drawTime: 'beforeDraw'  | 
 | 187 | +        },  | 
 | 188 | +        annotations: {  | 
 | 189 | +          annotation1,  | 
 | 190 | +          annotation2,  | 
 | 191 | +          annotation3,  | 
 | 192 | +          annotation4  | 
 | 193 | +        }  | 
 | 194 | +      },  | 
 | 195 | +      tooltip: {  | 
 | 196 | +        mode: 'nearest',  | 
 | 197 | +        intersect: true,  | 
 | 198 | +        usePointStyle: true,  | 
 | 199 | +        footerAlign: 'right',  | 
 | 200 | +        footerColor: 'lightGray',  | 
 | 201 | +        footerMarginTop: 10,  | 
 | 202 | +        callbacks: {  | 
 | 203 | +          title: (items) => items[0].raw.co,  | 
 | 204 | +          labelColor({raw}) {  | 
 | 205 | +            const x = raw.x > 5 ? 1 : 0;  | 
 | 206 | +            const y = raw.y > 5 ? 1 : 0;  | 
 | 207 | +            return {  | 
 | 208 | +              borderColor: META[y][x].color,  | 
 | 209 | +              backgroundColor: META[y][x].backgroundColor,  | 
 | 210 | +              borderWidth: 3  | 
 | 211 | +            };  | 
 | 212 | +          },  | 
 | 213 | +          label({raw}) {  | 
 | 214 | +            const x = raw.x > 5 ? 1 : 0;  | 
 | 215 | +            const y = raw.y > 5 ? 1 : 0;  | 
 | 216 | +            return META[y][x].label;  | 
 | 217 | +          },  | 
 | 218 | +          footer(items) {  | 
 | 219 | +            const {raw} = items[0];  | 
 | 220 | +            return ['Completeness of vision: ' + raw.x.toFixed(2), 'Ability to execute: ' + raw.y.toFixed(2)];  | 
 | 221 | +          }  | 
 | 222 | +        }  | 
 | 223 | +      }  | 
 | 224 | +    }  | 
 | 225 | +  }  | 
 | 226 | +};  | 
 | 227 | +/* </block:config> */  | 
 | 228 | + | 
 | 229 | +const actions = [  | 
 | 230 | +  {  | 
 | 231 | +    name: 'Randomize',  | 
 | 232 | +    handler: function(chart) {  | 
 | 233 | +      chart.data.datasets.forEach(function(dataset, i) {  | 
 | 234 | +        const values = randomize();  | 
 | 235 | +        dataset.data.forEach((p, idx) => {  | 
 | 236 | +          const v = values[idx];  | 
 | 237 | +          p.x = v.x;  | 
 | 238 | +          p.y = v.y;  | 
 | 239 | +          p.co = v.co;  | 
 | 240 | +        });  | 
 | 241 | +      });  | 
 | 242 | +      chart.update();  | 
 | 243 | +    }  | 
 | 244 | +  },  | 
 | 245 | +];  | 
 | 246 | + | 
 | 247 | +module.exports = {  | 
 | 248 | +  actions: actions,  | 
 | 249 | +  config: config,  | 
 | 250 | +};  | 
 | 251 | +```  | 
0 commit comments