Merge
commit
53005e715b
|
@ -127,8 +127,8 @@ func (s *Service) RemoveDashboard(w http.ResponseWriter, r *http.Request) {
|
|||
w.WriteHeader(http.StatusNoContent)
|
||||
}
|
||||
|
||||
// UpdateDashboard replaces a dashboard
|
||||
func (s *Service) UpdateDashboard(w http.ResponseWriter, r *http.Request) {
|
||||
// ReplaceDashboard completely replaces a dashboard
|
||||
func (s *Service) ReplaceDashboard(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
idParam, err := strconv.Atoi(httprouter.GetParamFromContext(ctx, "id"))
|
||||
if err != nil {
|
||||
|
@ -165,6 +165,52 @@ func (s *Service) UpdateDashboard(w http.ResponseWriter, r *http.Request) {
|
|||
encodeJSON(w, http.StatusOK, res, s.Logger)
|
||||
}
|
||||
|
||||
// UpdateDashboard completely updates either the dashboard name or the cells
|
||||
func (s *Service) UpdateDashboard(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
idParam, err := strconv.Atoi(httprouter.GetParamFromContext(ctx, "id"))
|
||||
if err != nil {
|
||||
msg := fmt.Sprintf("Could not parse dashboard ID: %s", err)
|
||||
Error(w, http.StatusInternalServerError, msg, s.Logger)
|
||||
}
|
||||
id := chronograf.DashboardID(idParam)
|
||||
|
||||
orig, err := s.DashboardsStore.Get(ctx, id)
|
||||
if err != nil {
|
||||
Error(w, http.StatusNotFound, fmt.Sprintf("ID %d not found", id), s.Logger)
|
||||
return
|
||||
}
|
||||
|
||||
var req chronograf.Dashboard
|
||||
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||||
invalidJSON(w, s.Logger)
|
||||
return
|
||||
}
|
||||
req.ID = id
|
||||
|
||||
if req.Name != "" {
|
||||
orig.Name = req.Name
|
||||
} else if len(req.Cells) > 0 {
|
||||
if err := ValidDashboardRequest(&req); err != nil {
|
||||
invalidData(w, err, s.Logger)
|
||||
return
|
||||
}
|
||||
orig.Cells = req.Cells
|
||||
} else {
|
||||
invalidData(w, fmt.Errorf("Update must include either name or cells"), s.Logger)
|
||||
return
|
||||
}
|
||||
|
||||
if err := s.DashboardsStore.Update(ctx, orig); err != nil {
|
||||
msg := fmt.Sprintf("Error updating dashboard ID %d: %v", id, err)
|
||||
Error(w, http.StatusInternalServerError, msg, s.Logger)
|
||||
return
|
||||
}
|
||||
|
||||
res := newDashboardResponse(orig)
|
||||
encodeJSON(w, http.StatusOK, res, s.Logger)
|
||||
}
|
||||
|
||||
// ValidDashboardRequest verifies that the dashboard cells have a query
|
||||
func ValidDashboardRequest(d *chronograf.Dashboard) error {
|
||||
if len(d.Cells) == 0 {
|
||||
|
|
|
@ -117,7 +117,8 @@ func NewMux(opts MuxOpts, service Service) http.Handler {
|
|||
|
||||
router.GET("/chronograf/v1/dashboards/:id", service.DashboardID)
|
||||
router.DELETE("/chronograf/v1/dashboards/:id", service.RemoveDashboard)
|
||||
router.PUT("/chronograf/v1/dashboards/:id", service.UpdateDashboard)
|
||||
router.PUT("/chronograf/v1/dashboards/:id", service.ReplaceDashboard)
|
||||
router.PATCH("/chronograf/v1/dashboards/:id", service.UpdateDashboard)
|
||||
|
||||
/* Authentication */
|
||||
if opts.UseAuth {
|
||||
|
|
|
@ -1519,6 +1519,51 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"patch": {
|
||||
"tags": [
|
||||
"layouts"
|
||||
],
|
||||
"summary": "Update dashboard information.",
|
||||
"description": "Update either the dashboard name or the dashboard cells",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "id",
|
||||
"in": "path",
|
||||
"type": "integer",
|
||||
"description": "ID of a dashboard",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"name": "config",
|
||||
"in": "body",
|
||||
"description": "dashboard configuration update parameters. Must be either name or cells",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/Dashboard"
|
||||
},
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Dashboard has been updated and the new dashboard is returned.",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/Dashboard"
|
||||
}
|
||||
},
|
||||
"404": {
|
||||
"description": "Happens when trying to access a non-existent dashboard.",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/Error"
|
||||
}
|
||||
},
|
||||
"default": {
|
||||
"description": "A processing or an unexpected error.",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/Error"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
|
@ -18,7 +18,7 @@ const Dashboard = ({
|
|||
|
||||
return (
|
||||
<div className={classnames({'page-contents': true, 'presentation-mode': inPresentationMode})}>
|
||||
<div className="container-fluid full-width">
|
||||
<div className="container-fluid full-width dashboard-view">
|
||||
{isEditMode ? <Visualizations/> : null}
|
||||
{Dashboard.renderDashboard(dashboard, timeRange, source, onPositionChange)}
|
||||
</div>
|
||||
|
|
|
@ -172,7 +172,7 @@ export const HostPage = React.createClass({
|
|||
'page-contents': true,
|
||||
'presentation-mode': inPresentationMode,
|
||||
})}>
|
||||
<div className="container-fluid full-width">
|
||||
<div className="container-fluid full-width dashboard-view">
|
||||
{ (layouts.length > 0) ? this.renderLayouts(layouts) : '' }
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -90,7 +90,7 @@ export const KubernetesDashboard = React.createClass({
|
|||
'page-contents': true,
|
||||
'presentation-mode': inPresentationMode,
|
||||
})}>
|
||||
<div className="container-fluid full-width">
|
||||
<div className="container-fluid full-width dashboard-view">
|
||||
{layouts.length ? this.renderLayouts(layouts) : emptyState}
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -108,6 +108,7 @@ export default React.createClass({
|
|||
const legendWidth = legendRect.width;
|
||||
const legendMaxLeft = graphWidth - (legendWidth / 2);
|
||||
const trueGraphX = (e.pageX - graphRect.left);
|
||||
const legendTop = graphRect.height + 0
|
||||
let legendLeft = trueGraphX;
|
||||
// Enforcing max & min legend offsets
|
||||
if (trueGraphX < (legendWidth / 2)) {
|
||||
|
@ -117,6 +118,7 @@ export default React.createClass({
|
|||
}
|
||||
|
||||
legendContainerNode.style.left = `${legendLeft}px`;
|
||||
legendContainerNode.style.top = `${legendTop}px`;
|
||||
setMarker(points);
|
||||
},
|
||||
unhighlightCallback() {
|
||||
|
|
|
@ -12,7 +12,6 @@ const RefreshingSingleStat = AutoRefresh(SingleStat);
|
|||
const {
|
||||
arrayOf,
|
||||
func,
|
||||
node,
|
||||
number,
|
||||
shape,
|
||||
string,
|
||||
|
@ -106,8 +105,8 @@ export const LayoutRenderer = React.createClass({
|
|||
if (cell.type === 'single-stat') {
|
||||
return (
|
||||
<div key={cell.i}>
|
||||
<h2 className="hosts-graph-heading">{cell.name}</h2>
|
||||
<div className="hosts-graph graph-container ">
|
||||
<h2 className="hosts-graph-heading">{cell.name || `Graph`}</h2>
|
||||
<div className="hosts-graph graph-container">
|
||||
<RefreshingSingleStat queries={[qs[0]]} autoRefresh={autoRefreshMs} />
|
||||
</div>
|
||||
</div>
|
||||
|
@ -115,9 +114,12 @@ export const LayoutRenderer = React.createClass({
|
|||
}
|
||||
|
||||
return (
|
||||
<Wrapper key={cell.i}>
|
||||
<RefreshingLineGraph queries={qs} autoRefresh={autoRefreshMs} showSingleStat={cell.type === "line-plus-single-stat"} />
|
||||
</Wrapper>
|
||||
<div key={cell.i}>
|
||||
<h2 className="hosts-graph-heading">{cell.name || `Graph`}</h2>
|
||||
<div className="hosts-graph graph-container">
|
||||
<RefreshingLineGraph queries={qs} autoRefresh={autoRefreshMs} showSingleStat={cell.type === "line-plus-single-stat"} />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
},
|
||||
|
@ -142,9 +144,10 @@ export const LayoutRenderer = React.createClass({
|
|||
rowHeight={83.5}
|
||||
margin={[layoutMargin, layoutMargin]}
|
||||
containerPadding={[0, 0]}
|
||||
useCSSTransforms={false}
|
||||
onResize={this.triggerWindowResize}
|
||||
onLayoutChange={this.handleLayoutChange}
|
||||
useCSSTransforms={true}
|
||||
draggableHandle={'.hosts-graph-heading'}
|
||||
>
|
||||
{this.generateVisualizations()}
|
||||
</GridLayout>
|
||||
|
@ -160,24 +163,4 @@ export const LayoutRenderer = React.createClass({
|
|||
},
|
||||
});
|
||||
|
||||
const Wrapper = React.createClass({
|
||||
propTypes: {
|
||||
children: node.isRequired,
|
||||
},
|
||||
render() {
|
||||
const that = this;
|
||||
const newChildren = React.Children.map(this.props.children, (child) => {
|
||||
return React.cloneElement(child, {
|
||||
height: that.props.style.height,
|
||||
})
|
||||
})
|
||||
|
||||
return (
|
||||
<div {...this.props}>
|
||||
{newChildren}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
export default LayoutRenderer;
|
||||
|
|
|
@ -25,7 +25,6 @@ export default React.createClass({
|
|||
y: arrayOf(number),
|
||||
y2: arrayOf(number),
|
||||
}),
|
||||
height: string,
|
||||
title: string,
|
||||
isFetchingInitially: bool,
|
||||
isRefreshing: bool,
|
||||
|
@ -101,22 +100,20 @@ export default React.createClass({
|
|||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className={classNames({"graph--hasYLabel": !!(options.ylabel || options.y2label)})}>
|
||||
{isRefreshing ? this.renderSpinner() : null}
|
||||
<Dygraph
|
||||
containerStyle={{width: '95%', height: this.props.height && parseInt(this.props.height, 10) - 20 + 'px' || '100%'}}
|
||||
overrideLineColors={overrideLineColors}
|
||||
isGraphFilled={isGraphFilled}
|
||||
timeSeries={timeSeries}
|
||||
labels={labels}
|
||||
options={options}
|
||||
dygraphSeries={dygraphSeries}
|
||||
ranges={ranges || this.getRanges()}
|
||||
ruleValues={ruleValues}
|
||||
/>
|
||||
{showSingleStat ? <div className="graph-single-stat single-stat">{roundedValue}</div> : null}
|
||||
</div>
|
||||
<div className={classNames({"graph--hasYLabel": !!(options.ylabel || options.y2label)})}>
|
||||
{isRefreshing ? this.renderSpinner() : null}
|
||||
<Dygraph
|
||||
containerStyle={{width: '100%', height: '100%'}}
|
||||
overrideLineColors={overrideLineColors}
|
||||
isGraphFilled={isGraphFilled}
|
||||
timeSeries={timeSeries}
|
||||
labels={labels}
|
||||
options={options}
|
||||
dygraphSeries={dygraphSeries}
|
||||
ranges={ranges || this.getRanges()}
|
||||
ruleValues={ruleValues}
|
||||
/>
|
||||
{showSingleStat ? <div className="graph-single-stat single-stat">{roundedValue}</div> : null}
|
||||
</div>
|
||||
);
|
||||
},
|
||||
|
|
|
@ -45,6 +45,7 @@
|
|||
@import 'pages/hosts';
|
||||
@import 'pages/kapacitor';
|
||||
@import 'pages/data-explorer';
|
||||
@import 'pages/dashboards';
|
||||
|
||||
// TODO
|
||||
@import 'unsorted';
|
||||
|
|
|
@ -15,9 +15,8 @@
|
|||
filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='fade-out($g20-white, 0.71)', endColorstr='fade-out($g20-white, 0.71)',GradientType=0 );
|
||||
}
|
||||
.container--dygraph-legend {
|
||||
top: 300px !important;
|
||||
transform: translate(-50%,-6px);
|
||||
background-color: $g1-raven;
|
||||
transform: translateX(-50%);
|
||||
background-color: $g0-obsidian;
|
||||
display: block;
|
||||
position: absolute;
|
||||
padding: 11px;
|
||||
|
@ -119,9 +118,9 @@
|
|||
.graph--hasYLabel {
|
||||
|
||||
.dygraph-axis-label-y {
|
||||
padding: 0 1px 0 12px !important;
|
||||
padding: 0 1px 0 10px !important;
|
||||
}
|
||||
.dygraph-axis-label-y2 {
|
||||
padding: 0 12px 0 1px !important;
|
||||
padding: 0 10px 0 1px !important;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,9 +13,31 @@
|
|||
background: linear-gradient(to right, $startColor 0%,$endColor 100%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='$startColor', endColorstr='$endColor',GradientType=1 );
|
||||
}
|
||||
@mixin gradient-diag-up($startColor, $endColor) {
|
||||
background: $startColor;
|
||||
background: -moz-linear-gradient(45deg, $startColor 0%, $endColor 100%);
|
||||
background: -webkit-linear-gradient(45deg, $startColor 0%,$endColor 100%);
|
||||
background: linear-gradient(45deg, $startColor 0%,$endColor 100%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='$startColor', endColorstr='$endColor',GradientType=1 );
|
||||
}
|
||||
@mixin gradient-diag-down($startColor, $endColor) {
|
||||
background: $startColor;
|
||||
background: -moz-linear-gradient(135deg, $startColor 0%, $endColor 100%);
|
||||
background: -webkit-linear-gradient(135deg, $startColor 0%,$endColor 100%);
|
||||
background: linear-gradient(135deg, $startColor 0%,$endColor 100%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='$startColor', endColorstr='$endColor',GradientType=1 );
|
||||
}
|
||||
@mixin gradient-r($startColor, $endColor) {
|
||||
background: $startColor;
|
||||
background: -moz-radial-gradient(center, ellipse cover, $startColor 0%, $endColor 100%);
|
||||
background: -webkit-radial-gradient(center, ellipse cover, $startColor 0%,$endColor 100%);
|
||||
background: radial-gradient(ellipse at center, $startColor 0%,$endColor 100%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='$startColor', endColorstr='$endColor',GradientType=1 );
|
||||
}
|
||||
|
||||
// Custom Scrollbars (Chrome Only)
|
||||
$scrollbar-width: 16px;
|
||||
$scrollbar-offset: 3px;
|
||||
@mixin custom-scrollbar($trackColor, $handleColor) {
|
||||
&::-webkit-scrollbar {
|
||||
width: $scrollbar-width;
|
||||
|
@ -30,12 +52,12 @@ $scrollbar-width: 16px;
|
|||
}
|
||||
&-track-piece {
|
||||
background-color: $trackColor;
|
||||
border: 6px solid $trackColor;
|
||||
border: $scrollbar-offset solid $trackColor;
|
||||
border-radius: ($scrollbar-width / 2);
|
||||
}
|
||||
&-thumb {
|
||||
background-color: $handleColor;
|
||||
border: 6px solid $trackColor;
|
||||
border: $scrollbar-offset solid $trackColor;
|
||||
border-radius: ($scrollbar-width / 2);
|
||||
}
|
||||
&-corner {
|
||||
|
|
|
@ -0,0 +1,153 @@
|
|||
$dashboard-item-heading: 30px;
|
||||
|
||||
.dashboard-view {
|
||||
.react-grid-item {
|
||||
background-color: $g3-castle;
|
||||
border-radius: $radius;
|
||||
border: 2px solid $g3-castle;
|
||||
transition-property: left, top, border-color, background-color;
|
||||
|
||||
&.cssTransforms {
|
||||
transition-property: transform, border-color, background-color;
|
||||
}
|
||||
|
||||
&.resizing {
|
||||
background-color: fade-out($g3-castle,0.09);
|
||||
border-color: $c-pool;
|
||||
border-image-slice: 3%;
|
||||
border-image-repeat: initial;
|
||||
border-image-outset: 0;
|
||||
border-image-width: 2px;
|
||||
border-image-source: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGQAAABkCAYAAABw4pVUAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyhpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuNi1jMDY3IDc5LjE1Nzc0NywgMjAxNS8wMy8zMC0yMzo0MDo0MiAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENDIDIwMTUgKE1hY2ludG9zaCkiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6MTg0NjVDRkVGMEVFMTFFNkE0QjVFRTJGNEI1ODc0RDMiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6MTg0NjVDRkZGMEVFMTFFNkE0QjVFRTJGNEI1ODc0RDMiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDoxODQ2NUNGQ0YwRUUxMUU2QTRCNUVFMkY0QjU4NzREMyIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDoxODQ2NUNGREYwRUUxMUU2QTRCNUVFMkY0QjU4NzREMyIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/PpeetfIAAAMnSURBVHja7N1PatwwFMfxJ5NlKT1DIfQKWZfSA/Q0hexDL9McoOQAPUKglwhp6dZ9Ho/HfyTZs6l+b/E1GDm27IH5oH9Pyji9//7XfLtNZt88/eT722TzlvrFseXHaXFmypuO8vd5nmW6nyeNefrKfZv7i9f75blU/NzafXvns2dV7tl8zqsnT55+9f3Xjf/xwQ9+evou+xLB+N8Ydi4AX3z/6PnvOj94AEOGMV/rB4P00J2rKTC0GNOTPne0GWEwhv1NB0YYjNPWgREHI00gYMTAOIGAEQdjuKcDIw7GXGWBEQJjrLLACIORrFBlgaHDsG2VBYYWY1VlgaHHSH3WqIOhxLB1ow6GGmPRqIMRAeMMAkYUDFuGTsDQYwxP6MCIg1Hp9oKhwih0e8FQYthuLAuM5hj1WBYYEoxUjGWBIcOwrFEHQ4qxLiFgyDFOvSww4mCM8yFghMEoDgzB0GGk2owhGBoMq5UQMDQYxRIChg4ja0PA0GLYMrgIhh7jUkLAiIExV1lghMA4GBiC0RrjNIULRhyMysAQDBVGYWAIhhJjM6cOhhpjUULAiIAxr1wEIwTGPDAEIwTGWGWBEQajHu0FQ4JRjvaCIcPIo71gSDHW0V4w5Bj5SB0MKUZxoRwYOoxsPgQMLcZqPgQMPUaxUQdDh2HVcQgYEoxUHIeAIcPIqywwpBjrKgsMOcb8f+pghMDIwu9gaDFWI3Uw9Bg2N+pgRMA497LAiIJRXf0OhgajuPodDB3G1dFeMNpgXBXtBaMdxmG0F4y2GLvRXjDaY2wGhmCoMawU7QVDh5G20V4wtBjzwBCMEBiXVx6BEQPjsJcFRluM3V4WGO0xqr0sMDQYVuplgaHDWL1YEgw9hi17WWDoMVJ1ChcMCYYVp3DBkGFUl5KCocGw6deAwIiBYUfBRTDaYmTdXjC0GFYLLoKhwSj+cAAYOgzbBhfB0GKsgotg6DGuWrkIRjuMudsLRgiMsQ0BIwzG5ZVHYMTAmKqsVzBiYPj2Z+j2PoERAmM4/2MoIfe+v4Ahx3jx5H4AefYLd37q0Y9/g9EcY/jOHz11A3v+J8AA9wisahRCWTQAAAAASUVORK5CYII=);
|
||||
z-index: 3;
|
||||
|
||||
& > .react-resizable-handle {
|
||||
&:before, &:after {
|
||||
background-color: $c-comet;
|
||||
}
|
||||
}
|
||||
}
|
||||
&.react-draggable-dragging {
|
||||
background-color: fade-out($g3-castle,0.09);
|
||||
border-color: $c-pool;
|
||||
border-image-slice: 3%;
|
||||
border-image-repeat: initial;
|
||||
border-image-outset: 0;
|
||||
border-image-width: 2px;
|
||||
border-image-source: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGQAAABkCAYAAABw4pVUAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyhpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuNi1jMDY3IDc5LjE1Nzc0NywgMjAxNS8wMy8zMC0yMzo0MDo0MiAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENDIDIwMTUgKE1hY2ludG9zaCkiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6MTg0NjVDRkVGMEVFMTFFNkE0QjVFRTJGNEI1ODc0RDMiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6MTg0NjVDRkZGMEVFMTFFNkE0QjVFRTJGNEI1ODc0RDMiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDoxODQ2NUNGQ0YwRUUxMUU2QTRCNUVFMkY0QjU4NzREMyIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDoxODQ2NUNGREYwRUUxMUU2QTRCNUVFMkY0QjU4NzREMyIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/PpeetfIAAAMnSURBVHja7N1PatwwFMfxJ5NlKT1DIfQKWZfSA/Q0hexDL9McoOQAPUKglwhp6dZ9Ho/HfyTZs6l+b/E1GDm27IH5oH9Pyji9//7XfLtNZt88/eT722TzlvrFseXHaXFmypuO8vd5nmW6nyeNefrKfZv7i9f75blU/NzafXvns2dV7tl8zqsnT55+9f3Xjf/xwQ9+evou+xLB+N8Ydi4AX3z/6PnvOj94AEOGMV/rB4P00J2rKTC0GNOTPne0GWEwhv1NB0YYjNPWgREHI00gYMTAOIGAEQdjuKcDIw7GXGWBEQJjrLLACIORrFBlgaHDsG2VBYYWY1VlgaHHSH3WqIOhxLB1ow6GGmPRqIMRAeMMAkYUDFuGTsDQYwxP6MCIg1Hp9oKhwih0e8FQYthuLAuM5hj1WBYYEoxUjGWBIcOwrFEHQ4qxLiFgyDFOvSww4mCM8yFghMEoDgzB0GGk2owhGBoMq5UQMDQYxRIChg4ja0PA0GLYMrgIhh7jUkLAiIExV1lghMA4GBiC0RrjNIULRhyMysAQDBVGYWAIhhJjM6cOhhpjUULAiIAxr1wEIwTGPDAEIwTGWGWBEQajHu0FQ4JRjvaCIcPIo71gSDHW0V4w5Bj5SB0MKUZxoRwYOoxsPgQMLcZqPgQMPUaxUQdDh2HVcQgYEoxUHIeAIcPIqywwpBjrKgsMOcb8f+pghMDIwu9gaDFWI3Uw9Bg2N+pgRMA497LAiIJRXf0OhgajuPodDB3G1dFeMNpgXBXtBaMdxmG0F4y2GLvRXjDaY2wGhmCoMawU7QVDh5G20V4wtBjzwBCMEBiXVx6BEQPjsJcFRluM3V4WGO0xqr0sMDQYVuplgaHDWL1YEgw9hi17WWDoMVJ1ChcMCYYVp3DBkGFUl5KCocGw6deAwIiBYUfBRTDaYmTdXjC0GFYLLoKhwSj+cAAYOgzbBhfB0GKsgotg6DGuWrkIRjuMudsLRgiMsQ0BIwzG5ZVHYMTAmKqsVzBiYPj2Z+j2PoERAmM4/2MoIfe+v4Ahx3jx5H4AefYLd37q0Y9/g9EcY/jOHz11A3v+J8AA9wisahRCWTQAAAAASUVORK5CYII=);
|
||||
cursor: grabbing;
|
||||
cursor: -moz-grabbing;
|
||||
cursor: -webkit-grabbing;
|
||||
&:hover {
|
||||
cursor: grabbing;
|
||||
cursor: -moz-grabbing;
|
||||
cursor: -webkit-grabbing;
|
||||
}
|
||||
|
||||
& > .hosts-graph-heading,
|
||||
& > .hosts-graph-heading:hover {
|
||||
background-color: $g4-onyx;
|
||||
color: $g18-cloud;
|
||||
cursor: grabbing;
|
||||
cursor: -moz-grabbing;
|
||||
cursor: -webkit-grabbing;
|
||||
}
|
||||
}
|
||||
}
|
||||
.react-grid-placeholder {
|
||||
@include gradient-diag-down($c-pool,$c-comet);
|
||||
border: 0;
|
||||
opacity: 0.3;
|
||||
z-index: 2;
|
||||
}
|
||||
.graph-empty {
|
||||
background-color: transparent;
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
top: 0;
|
||||
left: 0;
|
||||
}
|
||||
.hosts-graph {
|
||||
background-color: transparent;
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: calc(100% - #{$dashboard-item-heading});
|
||||
top: $dashboard-item-heading;
|
||||
left: 0;
|
||||
padding: 0;
|
||||
|
||||
& > div:not(.graph-empty) {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
||||
& > div:not(.graph-panel__refreshing) {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
padding: 8px 16px;
|
||||
}
|
||||
}
|
||||
.graph-panel__refreshing {
|
||||
top: (-$dashboard-item-heading + 5px) !important;
|
||||
}
|
||||
|
||||
}
|
||||
.hosts-graph-heading {
|
||||
background-color: transparent;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: $dashboard-item-heading;
|
||||
padding: 0 16px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
border-radius: $radius;
|
||||
transition:
|
||||
color 0.25s ease,
|
||||
background-color 0.25s ease;
|
||||
|
||||
&:hover {
|
||||
background-color: $g4-onyx;
|
||||
color: $g18-cloud;
|
||||
cursor: move; /* fallback if grab cursor is unsupported */
|
||||
cursor: grab;
|
||||
cursor: -moz-grab;
|
||||
cursor: -webkit-grab;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.react-grid-item > .react-resizable-handle {
|
||||
background-image: none;
|
||||
cursor: nwse-resize;
|
||||
|
||||
&:before,
|
||||
&:after {
|
||||
content: '';
|
||||
display: block;
|
||||
position: absolute;
|
||||
height: 2px;
|
||||
background-color: $g6-smoke;
|
||||
transition: background-color 0.25s ease;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
}
|
||||
&:before {
|
||||
width: 20px;
|
||||
transform: translate(-50%,-50%) rotate(-45deg);
|
||||
}
|
||||
&:after {
|
||||
width: 12px;
|
||||
transform: translate(-3px,2px) rotate(-45deg);
|
||||
}
|
||||
&:hover {
|
||||
&:before, &:after {
|
||||
background-color: $c-comet;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -25,7 +25,9 @@
|
|||
padding: 8px 16px;
|
||||
|
||||
.single-stat {
|
||||
font-size: 32px;
|
||||
font-size: 60px;
|
||||
font-weight: 300;
|
||||
color: $c-pool;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
|
Loading…
Reference in New Issue