// ## C3 Swimlane Timelines // _Demonstrate how to create timelines with swimlanes._ type SwimlaneDatum = { swimlane: number time: number duration: number color: string } // ## Prepare the Segment Data // A function to generate random swimlane data function generate_segment_data(data: SwimlaneDatum[]) { data.length = 0; let color_scale = d3.scale.category10(); let color = color_scale('seed'); for (let swimlane = 0; swimlane < 4; swimlane++) for (let time = Math.random()*5; time < 100; time += Math.random() * 15) { let duration = Math.random() * 15; if (Math.random() > 0.75) color = color_scale(Math.random().toString()); data.push({ swimlane: swimlane, time: time, duration: time + duration > 100 ? 100 - time : duration, color: color, }); time += duration; } c3.array.sort_up(data, (d) => d.time); } // ## Create the Segment Swimlane Chart // Create a `c3.Plot` chart. var segment_layer: c3.Plot.Layer.Swimlane.Segment; var swimlane_timeline = new c3.Plot.Zoomable({ anchor: '#swimlanes_segment_plot', width: '95%', height: 300, zoomable: 'h', data: [], // Setup the **scales** to go from 0-100 horizontally and 4 swimlanes vertically. h: d3.scale.linear().domain([0, 100]), v: d3.scale.linear().domain([0, 4]), // Add an **x axis** with grid lines. axes: [ new c3.Axis.X({ grid: true, ticks: false, }), ], layers: [ // Add a _segment swimlane_ *layer* segment_layer = new c3.Plot.Layer.Swimlane.Segment({ // Accessor functions which describe how to get **x**, **dx** and **y** values from the data elements. x: (d) => d.time, dx: (d) => d.duration, y: (d) => d.swimlane, rect_options: { // Static styles are more efficiently handled in a CSS file, this is just an example. styles: { 'fill': (d) => d.color, 'stroke': (d) => d3.rgb(d.color).darker().toString(), 'stroke-width': 2, 'rx': 5, // rounded corners (Doesn't work in IE11) 'shape-rendering': 'geometricPrecision', }, }, // Create **lables** for each segment label_options: { text: (d) => Math.round(d.duration), styles: { 'font-weight': 'bold', 'text-shadow': (d)=> '1px 1px 1px '+d3.rgb(d.color).brighter().toString(), }, }, // An HTML tooltip hover: (d) => d ? Math.round(d.duration) : null, // Add a border between swimlanes lane_options: { styles: { 'fill': 'none', 'stroke': 'gray', }, }, }), ], }); // ## Create the Sampled Swimlane Charts // Create a `c3.Plot` chart. var sampled_svg_timeline = new c3.Plot.Zoomable({ anchor: '#swimlanes_sampled_svg_plot', width: '95%', height: 300, zoomable: 'h', data: [], // Setup the **scales** to go from 0-100 horizontally and 4 swimlanes vertically. h: d3.scale.linear().domain([0, 100]), v: d3.scale.linear().domain([0, 4]), // Add an **x axis** with grid lines. axes: [ new c3.Axis.X({ grid: true, ticks: false, }), ], margins: { bottom: 20 }, layers: [ // Add a _sampled swimlane_ *layer* new c3.Plot.Layer.Swimlane.Sampled.SVG({ // Accessor functions which describe how to get **x**, **dx** and **y** values from the data elements. x: (d) => d.time, dx: (d) => d.duration, y: (d) => d.swimlane, line_options: { title: (d) => Math.round(d.duration), styles: { 'stroke': (d) => d.color, }, }, // Add a border between swimlanes lane_options: { styles: { 'fill': 'none', 'stroke': 'gray', }, }, }), ], }); var sampled_canvas_timeline = new c3.Plot.Zoomable({ anchor: '#swimlanes_sampled_canvas_plot', width: '95%', height: 300, zoomable: 'h', data: [], // Setup the **scales** to go from 0-100 horizontally and 4 swimlanes vertically. h: d3.scale.linear().domain([0, 100]), v: d3.scale.linear().domain([0, 4]), // Add an **x axis** with grid lines. axes: [ new c3.Axis.X({ grid: true, ticks: false, }), ], layers: [ // Add a _sampled swimlane_ *layer* new c3.Plot.Layer.Swimlane.Sampled.Canvas({ // Accessor functions which describe how to get **x**, **dx** and **y** values from the data elements. x: (d) => d.time, dx: (d) => d.duration, y: (d) => d.swimlane, safe: false, line_options: { styles: { 'stroke': (d) => d.color, }, }, // Add a border between swimlanes lane_options: { styles: { 'fill': 'none', 'stroke': 'gray', }, }, // HTML tooltip hover: (d) => d ? Math.round(d.duration) : null, }), ], }); // ## Render the Charts // Generate initial data generate_segment_data(swimlane_timeline.data); sampled_svg_timeline.data = swimlane_timeline.data; sampled_canvas_timeline.data = swimlane_timeline.data; // Call `render()` on the charts to initially render them. swimlane_timeline.render(); sampled_svg_timeline.render(); sampled_canvas_timeline.render(); // Allow the user generate new data by clicking on a button. We need to `redraw()` the charts // to reflect the updated data. _Note_: if we had cleared random_data via: `random_data = []` // in the `generate_data()` function, then this wouldn't work. It would just have updated the // variable and not the array that the charts are working with. That's why `random_data.length=0` is used. $('#sync_generate_button').on('click', () => { generate_segment_data(swimlane_timeline.data); swimlane_timeline.redraw(); sampled_svg_timeline.redraw(); sampled_canvas_timeline.redraw(); }); // Set the chart to limit the number of segments rendered. Note that it will prioritize drawing the // longest segments first. $('#limit_segments').on('change', function () { if (this.checked) { $('#limit_segment_count').prop('disabled', false); segment_layer.limit_elements = +$('#limit_segment_count').val(); } else { $('#limit_segment_count').prop('disabled', true); segment_layer.limit_elements = null; } swimlane_timeline.redraw(); }); // Adjust the number of elements to limit as the slider is changed. // The `input` event not supported in IE for (var event_name of ['input', 'change']) { $('#limit_segment_count').on(event_name, function () { segment_layer.limit_elements = +$(this).val(); swimlane_timeline.redraw(); }); } // Resize the chart to fit the window window.onresize = () => { swimlane_timeline.resize(); sampled_svg_timeline.resize(); sampled_canvas_timeline.resize(); }