Skip to content

Commit

Permalink
kubernetes: Update the image layer display designs
Browse files Browse the repository at this point in the history
  • Loading branch information
stefwalter committed Apr 29, 2016
1 parent b1027fc commit dce7cd6
Show file tree
Hide file tree
Showing 7 changed files with 146 additions and 211 deletions.
1 change: 1 addition & 0 deletions pkg/kubernetes/Makefile.am
Expand Up @@ -9,6 +9,7 @@ shared_VIEWS = \
pkg/kubernetes/views/image-body.html \
pkg/kubernetes/views/image-config.html \
pkg/kubernetes/views/image-delete.html \
pkg/kubernetes/views/image-layers.html \
pkg/kubernetes/views/image-meta.html \
pkg/kubernetes/views/image-listing.html \
pkg/kubernetes/views/image-page.html \
Expand Down
210 changes: 33 additions & 177 deletions pkg/kubernetes/scripts/layers.js
Expand Up @@ -17,199 +17,61 @@
* along with Cockpit; If not, see <http://www.gnu.org/licenses/>.
*/

/* globals d3 */
/* globals cockpit */

(function() {
"use strict";


function layer_graph(selector, options) {
var outer = d3.select(selector);

/* Data we've been fed */
var layers = [ ];
var settings = {
height: 125,
width: null,
bar: 14,
gap: 2,
max: 90,
title: "Image layers",
};

var timeout;
var scale = null;
var selection;
var width;

var svg = outer.append("svg")
.attr("class", "image-layers")
.on("click", function() {
var datum = d3.select(d3.event.target).datum() || { id: null };
select(datum.id);
});

var label = svg.append("text")
.attr("class", "label");
var title = svg.append("text")
.attr("text-anchor", "end")
.attr("y", "1em");

var line = svg.append("polyline")
.attr("class", "line");

function select(id) {
if (id === null)
return;

selection = id;

var value = "";
var index = null;

svg.selectAll("g")
.classed("selected", function(d, i) {
if (d.id === id) {
value = d.label;
index = i;
return true;
}
return false;
});

label
.text(value)
.attr("y", settings.height);

var y = settings.max + 15;
var points = [];
points.push("0," + y);
if (index !== null) {
points.push((index * settings.bar) + "," + y);
points.push(((index * settings.bar) + (settings.bar / 2)) + "," + (y - 5));
points.push(((index + 1) * settings.bar) + "," + y);
}
points.push(width + "," + y);
line.attr("points", points.join(" "));
}

function digest() {
timeout = null;

width = settings.width;
if (width === null)
width = outer.node().clientWidth;

svg
.attr("width", width)
.attr("height", settings.height);

var max = d3.max(layers, function(d) { return d.size; });
scale = d3.scale.linear()
.domain([0, max])
.range([2, settings.max]);

var bar = svg.selectAll("g")
.data(layers)
.enter().append("g")
.attr("transform", function(d, i) {
return "translate(" + i * settings.bar + ",0)";
});

bar.append("rect")
.attr("class", "layer")
.attr("x", settings.gap)
.attr("y", function(d) { return scale(max - d.size); })
.attr("width", settings.bar - (settings.gap * 2))
.attr("height", function(d) { return scale(d.size); });
bar.append("rect")
.attr("class", "column")
.attr("width", settings.bar)
.attr("height", scale(max) + 15);

title
.text(settings.title)
.attr("x", width - 5);

select(selection);
}

function resized() {
window.clearTimeout(timeout);
timeout = window.setTimeout(digest, 150);
}

window.addEventListener('resize', resized);

digest();
resized();

return {
data: function(new_layers, options) {
if (!new_layers)
new_layers = [];
layers = new_layers.slice();
layers.reverse();
if (selection === undefined && layers.length)
selection = layers[0].id;
angular.extend(settings, options);
digest();
},
close: function() {
window.removeEventListener('resize', resized);
window.clearTimeout(timeout);
}
};
}

function configDiff(layer, lower) {
var x, ret = { };
for (x in layer) {
if (!angular.equals(layer[x], lower[x]))
ret[x] = layer[x];
}
return ret;
}

function v1CompatibilityLabel(layer, lower) {
var cmd, last;
if (layer.v1Compatibility.container_config) {
cmd = layer.v1Compatibility.container_config.Cmd;
if (cmd) {
last = cmd[cmd.length - 1];
if (last.indexOf("#(nop)") === 0)
return last.substring(6);
return last.substring(6).trim();
}
}

return layer.v1Compatibility.id;
}

function prepareLayer(layer, index, layers) {
var result;
/* DockerImageManifest */
if (layer.v1Compatibility) {
return {
result = {
id: layer.v1Compatibility.id,
size: layer.v1Compatibility.Size || 0,
label: v1CompatibilityLabel(layer, layers[index + 1])
};

/* DockerImageLayers */
} else if (layer.name && layer.size) {
return {
result = {
id: layer.name,
size: layer.size || 0,
label: layer.name,
};

/* Unsupported layer type */
} else {
return {
result = {
size: 0,
id: index,
label: "Unknown layer",
};
}

/* Some hints for coloring the display */
if (result.label.indexOf("RUN ") === 0)
result.hint = "run";
else if (result.label.indexOf("ADD ") === 0 || result.size > 8192)
result.hint = "add";
else
result.hint = "other";

return result;
}

return angular.module('registry.layers', [])
Expand All @@ -219,38 +81,32 @@
return {
restrict: 'E',
scope: {
layers: '=',
settings: '=',
prevent: '=',
data: '=layers',
},
templateUrl: 'views/image-layers.html',
link: function($scope, element, attributes) {

$scope.formatSize = function(bytes) {
if (!bytes)
return "";
else if (bytes > 1024 && typeof cockpit != "undefined")
return cockpit.format_bytes(bytes);
else
return bytes + " B";
};

/*
element.css("display", "block");
var outer = angular.element("<div/>");
element.append(outer);
var graph;

function update(layers, settings) {
*/
$scope.$watch('data', function(layers) {
if (layers && layers.length)
layers = layers.map(prepareLayer);
graph.data(layers, settings);
}

$scope.$watchCollection('[layers, settings]', function(values) {
if (graph)
update(values[0], values[1] || { });
});

$scope.$watch("prevent", function(prevent) {
if (!prevent && !graph) {
graph = layer_graph(outer[0]);
update($scope.layers, $scope.settings || { });
}
});

element.on("$destroy", function() {
graph.close();
layers = layers.map(prepareLayer).reverse();
$scope.layers = layers;
});
}
};
Expand Down
9 changes: 9 additions & 0 deletions pkg/kubernetes/styles/images.less
Expand Up @@ -106,3 +106,12 @@ pre {
color: @metadata-color;
padding: 0px 5px;
}

.image-metadata-layers {
padding-top: 15px;
clear: both;

p {
margin-bottom: 3px;
}
}
62 changes: 44 additions & 18 deletions pkg/kubernetes/styles/layers.less
@@ -1,34 +1,60 @@
image-layers {
border: 1px solid @sidebar-pf-border-color;
box-sizing: border-box;
padding: 10px 15px;
margin-bottom: 15px;
display: block;
overflow-y: auto;
max-height: 200px;
}

.image-layers {
overflow: hidden;
ul.image-layers {
list-style: none;
border-left: 2px solid #d3d3d3;
margin: 15px;
padding-left: 0px;
font-size: 13px;

.column {
fill-opacity: 0;
cursor: pointer;
li {
clear: both;
}

.layer {
fill: #4d4d4d;
li:before {
content: "\f111"; /* circle */
font-family: FontAwesome;
font-size: 18px;
line-height: 18px;
float: left;
display: block;
color: #0099ff;
position: relative;
left: -9px;
padding-bottom: 9px;
}

.selected .layer {
fill: @link-color;
li.hint-add:before {
color: #555753;
}

.line {
stroke: @link-color;
stroke-width: 1px;
fill: transparent;
li.hint-run:before {
color: #4e9a06;
}

.label {
li:first-child:before {
content: "\f0ab"; /* fa-circle-arrow */
}

li:last-child:before {
top: 2px;
}

span {
display: block;
float: right;
}

p {
font-family: monospace;
font-weight: normal;
margin-bottom: 0px;
overflow: hidden;
text-overflow: ellipsis;
padding-right: 15px;
}
}

0 comments on commit dce7cd6

Please sign in to comment.