{
  "Name": "Step Chart — Standard",
  "Code": "(function() {\n  var container = document.getElementById(w.general.renderTo);\n  var props = w.props || {};\n  var items = w.data?.primaryData?.items || [];\n\n  if (!items.length) {\n    container.innerHTML = '<div style=\"display:flex;align-items:center;justify-content:center;height:100%;color:#999;\">Нет данных</div>';\n    return;\n  }\n\n  var existingChart = echarts.getInstanceByDom(container);\n  if (existingChart) existingChart.dispose();\n\n  var showMarkers     = props.showMarkers !== false;\n  var showValueLabels = props.showValueLabels === true;\n  var stepPosition    = props.stepPosition || 'end';\n  var lineColor        = props.lineColor  || '#000000';\n  var markerColor      = props.markerColor || '#000000';\n\n  var autoScale = props.autoScale !== false;\n  var yAxisMin  = parseFloat(props.yAxisMin);\n  var yAxisMax  = parseFloat(props.yAxisMax);\n\n  var showXAxis       = props.showXAxis !== false;\n  var showXAxisLabels = props.showXAxisLabels !== false;\n  var xAxisLabelColor = props.xAxisLabelColor || '#666666';\n  var showYAxis       = props.showYAxis !== false;\n  var showYAxisLabels = props.showYAxisLabels !== false;\n  var yAxisLabelColor = props.yAxisLabelColor || '#666666';\n\n  var showLegend     = props.showLegend !== false;\n  var legendPosition = props.legendPosition || 'top-right';\n\n  var showXGrid = props.showXGrid === true;\n  var xGridType = props.xGridType || 'dashed';\n  var showYGrid = props.showYGrid !== false;\n  var yGridType = props.yGridType || 'dashed';\n\n  var yReferenceLine     = props.yReferenceLine ? parseFloat(props.yReferenceLine) : null;\n  var xReferenceLine     = props.xReferenceLine ? parseInt(props.xReferenceLine)   : null;\n  var referenceLineColor = props.referenceLineColor || '#ff0000';\n\n  if (['start','middle','end'].indexOf(stepPosition) === -1) stepPosition = 'end';\n  if (['solid','dashed','dotted'].indexOf(xGridType)  === -1) xGridType  = 'dashed';\n  if (['solid','dashed','dotted'].indexOf(yGridType)  === -1) yGridType  = 'dashed';\n\n  var hasCategory    = items[0].keys.length > 1;\n  var dataByCategory = {};\n  var allDates       = [];\n  var categories     = [];\n  var allValues      = [];\n\n  items.forEach(function(item) {\n    var date     = item.keys[0];\n    var category = hasCategory ? item.keys[1] : 'Value';\n    var value    = item.values[0];\n\n    if (!dataByCategory[category]) {\n      dataByCategory[category] = {};\n      categories.push(category);\n    }\n    dataByCategory[category][date] = value;\n    if (allDates.indexOf(date) === -1) allDates.push(date);\n    if (value !== null && value !== undefined) allValues.push(value);\n  });\n\n  var calculatedMin = null;\n  var calculatedMax = null;\n\n  if (autoScale && allValues.length > 0) {\n    var minVal  = Math.min.apply(null, allValues);\n    var maxVal  = Math.max.apply(null, allValues);\n    var range   = maxVal - minVal || 1;\n    var padding = range * 0.1;\n    if (minVal > 0 && minVal > range * 0.3) {\n      calculatedMin = Math.floor((minVal - padding) / 10) * 10;\n      if (calculatedMin < 0) calculatedMin = 0;\n    }\n    calculatedMax = Math.ceil((maxVal + padding) / 10) * 10;\n  } else if (!autoScale) {\n    calculatedMin = !isNaN(yAxisMin) ? yAxisMin : null;\n    calculatedMax = !isNaN(yAxisMax) ? yAxisMax : null;\n  }\n\n  var categoryColors = ['#000000','#666666','#333333','#999999','#4a4a4a'];\n  var series = [];\n\n  categories.forEach(function(category, catIndex) {\n    var categoryData = dataByCategory[category];\n    var values = allDates.map(function(d) {\n      return categoryData[d] !== undefined ? categoryData[d] : null;\n    });\n\n    var seriesColor = categories.length > 1\n      ? categoryColors[catIndex % categoryColors.length]\n      : lineColor;\n\n    // ── Step-линия без встроенных символов ──\n    series.push({\n      name: category,\n      type: 'line',\n      step: stepPosition,\n      data: values,\n      showSymbol: false,\n      symbol: 'none',\n      lineStyle: { color: seriesColor, width: 2 },\n      itemStyle: { color: seriesColor },\n      emphasis: { focus: 'series' },\n      z: 10 - catIndex\n    });\n\n    if (showMarkers) {\n      var breakPoints = [];\n      var firstIdx = -1, lastIdx = -1;\n\n      for (var i = 0; i < values.length; i++) {\n        if (values[i] !== null) { firstIdx = i; break; }\n      }\n      for (var i = values.length - 1; i >= 0; i--) {\n        if (values[i] !== null) { lastIdx = i; break; }\n      }\n\n      if (firstIdx >= 0) {\n        breakPoints.push([firstIdx, values[firstIdx]]);\n\n        for (var i = firstIdx + 1; i < values.length; i++) {\n          if (values[i] === null || values[i - 1] === null) continue;\n          if (values[i] === values[i - 1]) continue;\n\n          var bx;\n          if (stepPosition === 'start') {\n            bx = i - 1;\n          } else if (stepPosition === 'middle') {\n            bx = i - 0.5;\n          } else {\n            bx = i;\n          }\n\n          breakPoints.push([bx, values[i - 1]]);\n          breakPoints.push([bx, values[i]]);\n        }\n\n        if (lastIdx > firstIdx) {\n          var lastBP = breakPoints[breakPoints.length - 1];\n          if (lastBP[0] !== lastIdx || lastBP[1] !== values[lastIdx]) {\n            breakPoints.push([lastIdx, values[lastIdx]]);\n          }\n        }\n      }\n\n      var _sc  = seriesColor;\n      var _mc  = markerColor;\n      var _sml = showValueLabels;\n\n      series.push({\n        type: 'custom',\n        name: category + '_brk',\n        renderItem: function(params, api) {\n          var xVal  = api.value(0);\n          var yVal  = api.value(1);\n          var point = api.coord([xVal, yVal]);\n\n          var children = [{\n            type: 'circle',\n            shape: { cx: point[0], cy: point[1], r: 5 },\n            style: {\n              fill: _mc,\n              stroke: _sc,\n              lineWidth: 2\n            }\n          }];\n\n          if (_sml) {\n            var txt = yVal % 1 === 0 ? String(yVal) : (+yVal).toFixed(1);\n            children.push({\n              type: 'text',\n              style: {\n                text: txt,\n                x: point[0],\n                y: point[1] - 14,\n                fill: '#333',\n                fontSize: 10,\n                fontWeight: 'bold',\n                textAlign: 'center',\n                textVerticalAlign: 'bottom'\n              }\n            });\n          }\n\n          return { type: 'group', children: children };\n        },\n        data: breakPoints,\n        silent: true,\n        tooltip: { show: false },\n        z: 20 - catIndex\n      });\n    }\n  });\n\n  // ── IBCS разделители + референсные линии ──\n  if (series.length > 0) {\n    var markLineData = [];\n    allDates.forEach(function(d, idx) {\n      markLineData.push({\n        xAxis: idx,\n        lineStyle: { color: '#e0e0e0', type: 'solid', width: 1 }\n      });\n    });\n\n    if (yReferenceLine !== null && !isNaN(yReferenceLine)) {\n      markLineData.push({\n        yAxis: yReferenceLine,\n        lineStyle: { color: referenceLineColor, type: 'solid', width: 2 },\n        label: { show: true, position: 'end', formatter: String(yReferenceLine), color: referenceLineColor }\n      });\n    }\n\n    if (xReferenceLine !== null && !isNaN(xReferenceLine) &&\n        xReferenceLine >= 0 && xReferenceLine < allDates.length) {\n      markLineData.push({\n        xAxis: xReferenceLine,\n        lineStyle: { color: referenceLineColor, type: 'solid', width: 2 },\n        label: { show: true, position: 'end', formatter: allDates[xReferenceLine] || '', color: referenceLineColor }\n      });\n    }\n\n    series[0].markLine = {\n      silent: true, symbol: 'none',\n      label: { show: false },\n      data: markLineData\n    };\n  }\n\n  // ── Легенда ──\n  var legendTop = 'auto', legendBottom = 'auto', legendLeft = 'auto', legendRight = 'auto';\n  var posParts = legendPosition.split('-');\n  var vertPos  = posParts[0] || 'top';\n  var horizPos = posParts[1] || 'right';\n\n  if (vertPos === 'top')         legendTop    = 5;\n  else if (vertPos === 'bottom') legendBottom = 5;\n  if (horizPos === 'left')        legendLeft  = 10;\n  else if (horizPos === 'center') legendLeft  = 'center';\n  else if (horizPos === 'right')  legendRight = 20;\n\n  var option = {\n    backgroundColor: 'transparent',\n    tooltip: {\n      trigger: 'axis',\n      backgroundColor: 'rgba(255,255,255,0.95)',\n      borderColor: '#ddd', borderWidth: 1,\n      textStyle: { color: '#333', fontSize: 12 },\n      formatter: function(params) {\n        var result = '<b>' + params[0].axisValue + '</b><br/>';\n        params.forEach(function(p) {\n          if (p.seriesName && p.seriesName.indexOf('_brk') !== -1) return;\n          if (p.value !== null && p.value !== undefined) {\n            result += '<span style=\"color:' + p.color + ';\">●</span> ' +\n                      p.seriesName + ': <b>' +\n                      (p.value % 1 === 0 ? p.value : p.value.toFixed(1)) +\n                      '</b><br/>';\n          }\n        });\n        return result;\n      }\n    },\n    legend: {\n      show: showLegend && categories.length > 1,\n      data: categories,\n      top: legendTop, bottom: legendBottom,\n      left: legendLeft, right: legendRight,\n      textStyle: { fontSize: 10, color: '#666' }\n    },\n    grid: {\n      left: 40, right: 30,\n      top:    (showLegend && categories.length > 1 && vertPos === 'top')    ? 40 : 20,\n      bottom: (showLegend && categories.length > 1 && vertPos === 'bottom') ? 50 : 40,\n      containLabel: true\n    },\n    xAxis: {\n      type: 'category',\n      data: allDates,\n      boundaryGap: false,\n      show: showXAxis,\n      axisLine:  { show: showXAxis, lineStyle: { color: '#ccc' } },\n      axisTick:  { show: false },\n      axisLabel: { show: showXAxisLabels, fontSize: 10, color: xAxisLabelColor, interval: 0, rotate: allDates.length > 12 ? 45 : 0 },\n      splitLine: { show: showXGrid, lineStyle: { color: '#f0f0f0', type: xGridType } }\n    },\n    yAxis: {\n      type: 'value',\n      show: showYAxis,\n      min: calculatedMin,\n      max: calculatedMax,\n      axisLine:  { show: showYAxis, lineStyle: { color: '#ccc' } },\n      axisTick:  { show: false },\n      axisLabel: { show: showYAxisLabels, fontSize: 10, color: yAxisLabelColor },\n      splitLine: { show: showYGrid, lineStyle: { color: '#f0f0f0', type: yGridType } }\n    },\n    series: series\n  };\n\n  var chart = echarts.init(container);\n  chart.setOption(option);\n  new ResizeObserver(function() { chart.resize(); }).observe(container);\n})();\n",
  "DataRoles": [
    {
      "Name": "category",
      "DisplayName": "Дата / Категория",
      "Description": "Дата или период (например: месяц). Используется как ось времени. Второе поле — категория для нескольких step-линий.",
      "Kind": 0,
      "Type": 0,
      "Optional": false
    },
    {
      "Name": "value",
      "DisplayName": "Значение",
      "Description": "Числовое значение показателя. Используется для построения step-линии.",
      "Kind": 1,
      "Type": 0,
      "Optional": false
    }
  ],
  "Properties": [
    {
      "Value": true,
      "Type": 2,
      "ElementType": "UserWidgetProperty",
      "DisplayName": "Показывать маркеры",
      "PropertyName": "showMarkers"
    },
    {
      "Value": false,
      "Type": 2,
      "ElementType": "UserWidgetProperty",
      "DisplayName": "Показывать подписи значений",
      "PropertyName": "showValueLabels"
    },
    {
      "Value": "end",
      "Type": 0,
      "ElementType": "UserWidgetProperty",
      "DisplayName": "Позиция ступеньки (start/middle/end)",
      "PropertyName": "stepPosition"
    },
    {
      "Value": "#000000",
      "Type": 3,
      "ElementType": "UserWidgetProperty",
      "DisplayName": "Цвет линии",
      "PropertyName": "lineColor"
    },
    {
      "Value": "#000000",
      "Type": 3,
      "ElementType": "UserWidgetProperty",
      "DisplayName": "Цвет маркеров",
      "PropertyName": "markerColor"
    },
    {
      "Value": "",
      "Type": 0,
      "ElementType": "UserWidgetProperty",
      "DisplayName": "═══ МАСШТАБ ОСИ Y ═══",
      "PropertyName": "_scaleHeader"
    },
    {
      "Value": true,
      "Type": 2,
      "ElementType": "UserWidgetProperty",
      "DisplayName": "Автомасштабирование",
      "PropertyName": "autoScale"
    },
    {
      "Value": 0,
      "Type": 1,
      "ElementType": "UserWidgetProperty",
      "DisplayName": "Минимум Y (если не авто)",
      "PropertyName": "yAxisMin"
    },
    {
      "Value": 100,
      "Type": 1,
      "ElementType": "UserWidgetProperty",
      "DisplayName": "Максимум Y (если не авто)",
      "PropertyName": "yAxisMax"
    },
    {
      "Value": "",
      "Type": 0,
      "ElementType": "UserWidgetProperty",
      "DisplayName": "═══ ОСИ ═══",
      "PropertyName": "_axisHeader"
    },
    {
      "Value": true,
      "Type": 2,
      "ElementType": "UserWidgetProperty",
      "DisplayName": "Показывать ось X",
      "PropertyName": "showXAxis"
    },
    {
      "Value": true,
      "Type": 2,
      "ElementType": "UserWidgetProperty",
      "DisplayName": "Показывать значения оси X",
      "PropertyName": "showXAxisLabels"
    },
    {
      "Value": "#666666",
      "Type": 3,
      "ElementType": "UserWidgetProperty",
      "DisplayName": "Цвет значений оси X",
      "PropertyName": "xAxisLabelColor"
    },
    {
      "Value": true,
      "Type": 2,
      "ElementType": "UserWidgetProperty",
      "DisplayName": "Показывать ось Y",
      "PropertyName": "showYAxis"
    },
    {
      "Value": true,
      "Type": 2,
      "ElementType": "UserWidgetProperty",
      "DisplayName": "Показывать значения оси Y",
      "PropertyName": "showYAxisLabels"
    },
    {
      "Value": "#666666",
      "Type": 3,
      "ElementType": "UserWidgetProperty",
      "DisplayName": "Цвет значений оси Y",
      "PropertyName": "yAxisLabelColor"
    },
    {
      "Value": "",
      "Type": 0,
      "ElementType": "UserWidgetProperty",
      "DisplayName": "═══ ЛЕГЕНДА ═══",
      "PropertyName": "_legendHeader"
    },
    {
      "Value": true,
      "Type": 2,
      "ElementType": "UserWidgetProperty",
      "DisplayName": "Показывать легенду",
      "PropertyName": "showLegend"
    },
    {
      "Value": "top-right",
      "Type": 0,
      "ElementType": "UserWidgetProperty",
      "DisplayName": "Позиция легенды (top/bottom)-(left/center/right)",
      "PropertyName": "legendPosition"
    },
    {
      "Value": "",
      "Type": 0,
      "ElementType": "UserWidgetProperty",
      "DisplayName": "═══ СЕТКА ═══",
      "PropertyName": "_gridHeader"
    },
    {
      "Value": false,
      "Type": 2,
      "ElementType": "UserWidgetProperty",
      "DisplayName": "Показывать сетку X",
      "PropertyName": "showXGrid"
    },
    {
      "Value": "dashed",
      "Type": 0,
      "ElementType": "UserWidgetProperty",
      "DisplayName": "Тип сетки X (solid/dashed/dotted)",
      "PropertyName": "xGridType"
    },
    {
      "Value": true,
      "Type": 2,
      "ElementType": "UserWidgetProperty",
      "DisplayName": "Показывать сетку Y",
      "PropertyName": "showYGrid"
    },
    {
      "Value": "dashed",
      "Type": 0,
      "ElementType": "UserWidgetProperty",
      "DisplayName": "Тип сетки Y (solid/dashed/dotted)",
      "PropertyName": "yGridType"
    },
    {
      "Value": "",
      "Type": 0,
      "ElementType": "UserWidgetProperty",
      "DisplayName": "═══ РЕФЕРЕНСНЫЕ ЛИНИИ ═══",
      "PropertyName": "_refHeader"
    },
    {
      "Value": "",
      "Type": 0,
      "ElementType": "UserWidgetProperty",
      "DisplayName": "Горизонтальная линия (значение Y)",
      "PropertyName": "yReferenceLine"
    },
    {
      "Value": "",
      "Type": 0,
      "ElementType": "UserWidgetProperty",
      "DisplayName": "Вертикальная линия (индекс X)",
      "PropertyName": "xReferenceLine"
    },
    {
      "Value": "#ff0000",
      "Type": 3,
      "ElementType": "UserWidgetProperty",
      "DisplayName": "Цвет референсных линий",
      "PropertyName": "referenceLineColor"
    }
  ],
  "PropertiesJson": ""
}