Do not input private or sensitive data. View Qlik Privacy & Cookie Policy.
Skip to main content

Announcements
Qlik Connect 2026! Turn data into bold moves, April 13 -15: Learn More!
cancel
Showing results for 
Search instead for 
Did you mean: 
johnnyjohn
Creator
Creator

Greying out dimensions in custom visualisation

Hi, I'm creating a custom visualisation using Plotly (a violin chart specifically). 

johnnyjohn_1-1746638347060.png

 

I've got it all working pretty well, there's one thing I'm stuck on though. I'd like for when I click on the charts to create the greying effect native to Qlik charts. 

I may suspect that could be a parameter that lives in there: const initialProperties. But I'm really not sure. Full code below. Thanks !

johnnyjohn_2-1746638382518.png

 

 
define([
  "qlik",
  "jquery",
  "text!./plotly-violin.css",
  "https://cdn.plot.ly/plotly-2.32.0.min.js"
], function (qlik, $, cssContent, Plotly) {
  // inject CSS
  $("<style>").html(cssContent).appendTo("head");

  // ─── Property panel definition ────────────────────────────────────────────
  const definition = {
    type: "items",
    component: "accordion",
    items: {
      dims:  { uses: "dimensions", min: 2, max: 2 },
      meas:  { uses: "measures",    min: 1, max: 1 },
      points:{
        type:  "items",
        label: "Points",
        items: {
          showSidePoints: {
            type: "boolean",
            ref:  "props.showSidePoints",
            label:"Show side-points",
            defaultValue: false
          }
        }
      },
      settings: { uses: "settings" }
    }
  };

  // ─── Initial properties (CONFIRM selection mode) ─────────────────────────
  const initialProperties = {
    selectionMode: "QUICK",
    qHyperCubeDef: {
      qDimensions: [],
      qMeasures: [],
      qInitialDataFetch: [{ qWidth: 3, qHeight: 1000 }]
    },
    props: {
      showSidePoints: false
    }
  };

  // ─── Paint/render function ─────────────────────────────────────────────────
  async function paint($element, layout) {
    // 1) ensure container
    const divId = "plotly_" + layout.qInfo.qId;
    if (!$element.find("#" + divId).length) {
      $element.html(
        `<div id="${divId}" class="plotly-violin-extension" style="width:100%;height:100%;"></div>`
      );
    }
    const plotDiv = document.getElementById(divId);

    // 2) load theme variables & styling
    const app = qlik.currApp(this);
    let vars = {}, axisCfg = {}, legendCfg = {}, palette = [];
    let tickFont = { family: "", size: 12, color: "#000" },
        lineColor = "#333", gridColor = "#444",
        legendTitleFont = { family: "", size: 12, color: "#000" },
        legendFont      = { family: "", size: 12, color: "#000" };

    try {
      const theme = await app.theme.getApplied();
      vars      = theme.properties._variables   || {};
      axisCfg   = theme.properties.object?.axis || {};
      legendCfg = theme.properties.object?.legend || {};

      const resolve = v =>
        (typeof v === "string" && v.startsWith("@")) ? (vars[v] || v) : v;

      // build palette @c1…@c12
      for (let i = 1; i <= 12; i++) {
        const c = vars[`@c${i}`];
        if (c) palette.push(c);
      }

      // tick font
      const lf = axisCfg.label?.name || {};
      tickFont = {
        family: resolve(lf.fontFamily) || "",
        size:   parseInt(resolve(lf.fontSize), 10) || 12,
        color:  resolve(lf.color)    || "#000"
      };

      // axis line/grid colors
      lineColor = resolve(axisCfg.line?.major?.color) || lineColor;
      gridColor = resolve(axisCfg.line?.minor?.color) || gridColor;

      // legend title font
      const lt = legendCfg.title || {};
      legendTitleFont = {
        family: resolve(lt.fontFamily) || "",
        size:   parseInt(resolve(lt.fontSize), 10) || tickFont.size,
        color:  resolve(lt.color)    || tickFont.color
      };

      // legend label font
      const ll = legendCfg.label || {};
      legendFont = {
        family: resolve(ll.fontFamily) || legendTitleFont.family,
        size:   parseInt(resolve(ll.fontSize), 10) || legendTitleFont.size,
        color:  resolve(ll.color)    || legendTitleFont.color
      };
    }
    catch (e) {
      console.warn("Theme load error:", e);
    }

    // 3) fetch all data pages
    const hc = this.backendApi;
    async function fetchAll(rowsPerPage = 1000) {
      let top = 0, all = [];
      while (true) {
        const pages = await hc.getData([{
          qTop: top, qLeft: 0,
          qWidth: 3, qHeight: rowsPerPage
        }]);
        const mat = pages[0].qMatrix;
        if (!mat.length) break;
        all.push(...mat);
        if (mat.length < rowsPerPage) break;
        top += rowsPerPage;
      }
      return all;
    }
    const rows = await fetchAll();

    // 4) group values by first dimension
    const grouped = {}, elemMap = {};
    rows.forEach(r => {
      const cat = r[0].qText, val = r[2].qNum;
      elemMap[cat] = r[0].qElemNumber;
      (grouped[cat] = grouped[cat] || []).push(val);
    });

    // 5) build violin traces
    const entries = Object.entries(grouped);
    const traces = entries.map(([cat, vals], idx) => {
      const themePrimary = layout.theme?.properties?.dataColors?.primaryColor;
      const color = palette.length
        ? palette[idx % palette.length]
        : themePrimary;

      const t = {
        type:     "violin",
        x:        Array(vals.length).fill(cat),
        y:        vals,
        name:     cat,
        box:      { visible: true },
        meanline: { visible: true },
		hoverinfo: "none",
        points:   layout.props.showSidePoints ? "all" : false,
        side:     layout.props.showSidePoints ? "positive" : "both"
      };
      if (color) {
        t.marker = { color, line: { color, width: 1 } };
        t.line   = { color };
      }
      return t;
    });

    // 6) axis titles
    const xTitle = layout.qHyperCube.qDimensionInfo[0].qFallbackTitle;
    const yTitle = layout.qHyperCube.qMeasureInfo[0].qFallbackTitle;

    // 7) build responsive layout
    const plotLayout = {
      autosize:      true,
      margin:        { t: 20, l: 80, r: 10, b: 60 },
      paper_bgcolor: "transparent",
      plot_bgcolor:  "transparent",
      violingap:     0.4,
      violingroupgap:0.2,
      xaxis: {
        title:    { text: xTitle, font: tickFont, standoff: 20 },
        tickfont: tickFont,
        linecolor: lineColor,
        gridcolor: gridColor
      },
      yaxis: {
        title:    { text: yTitle, font: tickFont, standoff: 30 },
        tickfont: tickFont,
        linecolor: lineColor,
        gridcolor: gridColor
      },
      legend: {
        title: { font: legendTitleFont },
        font:  legendFont,
        bgcolor:"rgba(0,0,0,0)"
      }
    };

    // 😎 render
    await Plotly.react(divId, traces, plotLayout, {
      displayModeBar: false,
      responsive:     true
    });

    // 9) bind click → Qlik confirm‐mode selection
    if (!plotDiv._clickBound) {
      plotDiv.on("plotly_click", ev => {
        const pts = ev.points || [];
        if (pts.length) {
          const cat   = pts[0].x;
          const qElem = elemMap[cat];
          this.selectValues(0, [qElem], true);
        }
      });
      plotDiv._clickBound = true;
    }
  }

  // ─── Export extension object ───────────────────────────────────────────────
  return {
    definition,
    initialProperties,
    paint
  };
});

 

Labels (5)
0 Replies