Bin
2025-12-16 9e0b2ba2c317b1a86212f24cbae3195ad1f3dbfa
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
import { observer } from "mobx-react";
import { types } from "mobx-state-tree";
import Types from "../../../core/Types";
import { TagParentMixin } from "../../../mixins/TagParentMixin";
import Registry from "../../../core/Registry";
import TimeSeriesVisualizer from "../../../components/TimeSeries/TimeSeriesVisualizer";
import ChannelLegend from "./ChannelLegend";
import { getChannelColor } from "./palette";
 
/**
 * MultiChannel tag for grouped display of channels on the same plot
 * @name MultiChannel
 * @param {number} [height=200] height of the plot
 * @param {boolean} [showAxis=true] whether to show both axes
 * @param {boolean} [showYAxis=true] whether to show the y-axis
 * @param {boolean} [fixedScale] whether to use a fixed scale for all channels. If not set, inherits from parent TimeSeries tag
 * @example
 * <TimeSeries name="ts" value="$timeseries" valueType="json">
 *   <MultiChannel height="300" showAxis={true} showYAxis={true}>
 *     <Channel column="velocity" legend="Velocity"/>
 *     <Channel column="acceleration" legend="Acceleration"/>
 *   </MultiChannel>
 * </TimeSeries>
 */
const Model = types
  .model("MultiChannelModel", {
    id: types.optional(types.identifier, () => Math.random().toString(36).slice(2)),
    type: "multichannel",
    children: Types.unionArray(["channel"]),
    parentTypes: Types.tagsTypes(["TimeSeries"]),
    height: types.optional(types.string, "200"),
 
    showaxis: types.optional(types.boolean, true),
    showyaxis: types.optional(types.boolean, true),
 
    fixedscale: types.maybe(types.boolean),
  })
  .volatile((self) => ({
    isChannelHiddenMap: {},
    highlightedChannelId: null,
  }))
  .views((self) => ({
    get channels() {
      return self.children.filter((child) => child.type === "channel");
    },
 
    get margin() {
      const channelsWithYAxis = self.channels.filter((channel) => channel.showaxis && channel.showyaxis);
      if (channelsWithYAxis.length > 1) {
        return {
          ...self.parent?.margin,
          right: self.parent?.margin.left,
        };
      }
      return self.parent?.margin;
    },
  }))
  .actions((self) => ({
    afterCreate() {
      self.channels.forEach((channel, idx) => {
        if (channel.strokecolor === "") {
          channel.strokecolor = getChannelColor(idx);
        }
        if (channel.markercolor === "") {
          channel.markercolor = getChannelColor(idx);
        }
      });
    },
    toggleChannelVisibility(channelId) {
      self.isChannelHiddenMap = {
        ...self.isChannelHiddenMap,
        [channelId]: !self.isChannelHiddenMap[channelId],
      };
    },
 
    setHighlightedChannel(channelId) {
      self.highlightedChannelId = channelId;
    },
 
    clearHighlightedChannel() {
      self.highlightedChannelId = null;
    },
  }));
 
// MultiChannel component for rendering multiple channels in a single visualization
const HtxMultiChannel = observer(({ item }) => {
  if (!item.parent?.dataObj) return null;
 
  return (
    <div className="htx-timeseries-multichannel">
      <div className="htx-timeseries-channel-legend-container">
        <ChannelLegend item={item} />
      </div>
      <TimeSeriesVisualizer
        time={item.parent?.keyColumn}
        channels={item.channels}
        item={item}
        data={item.parent?.dataObj}
        series={item.parent?.dataHash}
        range={item.parent?.brushRange}
        ranges={item.parent?.regs}
        cursorTime={item.parent?.cursorTime}
      />
    </div>
  );
});
 
const MultiChannelModel = types.compose("MultiChannelModel", TagParentMixin, Model);
 
Registry.addTag("multichannel", MultiChannelModel, HtxMultiChannel);
 
export { MultiChannelModel, HtxMultiChannel };