mirror of
https://github.com/balena-io/etcher.git
synced 2025-04-24 07:17:18 +00:00
refactor(GUI): replace the orange alert ribbon with a modal (#689)
As a way to simplify the design and make more use of available components rather than creating specialised ones, we replaced the `.alert-ribbon` component, which consisted of an orange alert appearing at the top of the screen for when a validation failed, the drive ran out of space, or other issues, with a modal. This change allowed us to remove the "warning"-related colors from the theme palette. Signed-off-by: Juan Cruz Viotti <jviotti@openmailbox.org>
This commit is contained in:
parent
ce1c1d724d
commit
951b8de9fc
@ -4460,25 +4460,25 @@ a.thumbnail:focus,
|
||||
a.thumbnail.active {
|
||||
border-color: #ddd; }
|
||||
|
||||
.alert, .alert-ribbon {
|
||||
padding: 13px;
|
||||
.alert {
|
||||
padding: 15px;
|
||||
margin-bottom: 18px;
|
||||
border: 1px solid transparent;
|
||||
border-radius: 4px; }
|
||||
.alert h4, .alert-ribbon h4 {
|
||||
.alert h4 {
|
||||
margin-top: 0;
|
||||
color: inherit; }
|
||||
.alert .alert-link, .alert-ribbon .alert-link {
|
||||
.alert .alert-link {
|
||||
font-weight: bold; }
|
||||
.alert > p, .alert-ribbon > p,
|
||||
.alert > ul, .alert-ribbon > ul {
|
||||
.alert > p,
|
||||
.alert > ul {
|
||||
margin-bottom: 0; }
|
||||
.alert > p + p, .alert-ribbon > p + p {
|
||||
.alert > p + p {
|
||||
margin-top: 5px; }
|
||||
|
||||
.alert-dismissable,
|
||||
.alert-dismissible {
|
||||
padding-right: 33px; }
|
||||
padding-right: 35px; }
|
||||
.alert-dismissable .close,
|
||||
.alert-dismissible .close {
|
||||
position: relative;
|
||||
@ -6153,16 +6153,6 @@ body {
|
||||
background-color: #2d78d2;
|
||||
color: #fff; }
|
||||
|
||||
.button-warning,
|
||||
.button-warning:focus {
|
||||
background-color: #e99852;
|
||||
color: #fff;
|
||||
outline: none; }
|
||||
|
||||
.button-warning:hover {
|
||||
background-color: #e37d25;
|
||||
color: #fff; }
|
||||
|
||||
.button-danger,
|
||||
.button-danger:focus {
|
||||
background-color: #d9534f;
|
||||
@ -6209,53 +6199,6 @@ body {
|
||||
background-color: #d9534f;
|
||||
border-color: #d9534f; }
|
||||
|
||||
/*
|
||||
* Copyright 2016 Resin.io
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
.alert-ribbon {
|
||||
background-color: #e99852;
|
||||
color: #fff;
|
||||
border-color: #f7dbc3;
|
||||
width: 60%;
|
||||
position: fixed;
|
||||
text-align: center;
|
||||
left: 0;
|
||||
right: 0;
|
||||
margin: 0 auto;
|
||||
border: 0;
|
||||
border-radius: 2px;
|
||||
border-top-left-radius: 0;
|
||||
border-top-right-radius: 0;
|
||||
top: -100%;
|
||||
transition: top .5s; }
|
||||
.alert-ribbon > .glyphicon:first-child, .alert-ribbon > .tick:first-child {
|
||||
margin-right: 5px; }
|
||||
.alert-ribbon > .glyphicon:last-child, .alert-ribbon > .tick:last-child {
|
||||
margin-left: 5px; }
|
||||
.alert-ribbon .button-link {
|
||||
padding: 0;
|
||||
font-size: inherit;
|
||||
vertical-align: baseline;
|
||||
border-radius: 0;
|
||||
border-bottom: 1px solid;
|
||||
border-color: #fff;
|
||||
color: #fff; }
|
||||
|
||||
.alert-ribbon--open {
|
||||
top: 0; }
|
||||
|
||||
/*
|
||||
* Copyright 2016 Resin.io
|
||||
*
|
||||
@ -6489,6 +6432,24 @@ body {
|
||||
background-color: #f2f2f2;
|
||||
word-wrap: break-word; }
|
||||
|
||||
/*
|
||||
* Copyright 2016 Resin.io
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
.modal-flash-error-modal .modal-title .glyphicon, .modal-flash-error-modal .modal-title .tick {
|
||||
color: #d9534f; }
|
||||
|
||||
/*
|
||||
* Copyright 2016 Resin.io
|
||||
*
|
||||
|
@ -0,0 +1,52 @@
|
||||
/*
|
||||
* Copyright 2016 Resin.io
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
module.exports = function(
|
||||
$uibModalInstance,
|
||||
SelectionStateModel,
|
||||
FlashStateModel,
|
||||
AnalyticsService,
|
||||
flashErrorData
|
||||
) {
|
||||
|
||||
/**
|
||||
* @summary Flash error data
|
||||
* @property
|
||||
* @public
|
||||
*/
|
||||
this.data = flashErrorData;
|
||||
|
||||
/**
|
||||
* @summary Retry flash process
|
||||
* @function
|
||||
* @public
|
||||
*
|
||||
* @example
|
||||
* FlashErrorModalController.retry();
|
||||
*/
|
||||
this.retry = () => {
|
||||
SelectionStateModel.clear({
|
||||
preserveImage: true
|
||||
});
|
||||
|
||||
FlashStateModel.resetState();
|
||||
AnalyticsService.logEvent('Restart after failure');
|
||||
$uibModalInstance.dismiss();
|
||||
};
|
||||
|
||||
};
|
35
lib/gui/components/flash-error-modal/flash-error-modal.js
Normal file
35
lib/gui/components/flash-error-modal/flash-error-modal.js
Normal file
@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright 2016 Resin.io
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* @module Etcher.Components.FlashErrorModal
|
||||
*/
|
||||
|
||||
const angular = require('angular');
|
||||
const MODULE_NAME = 'Etcher.Components.FlashErrorModal';
|
||||
const FlashErrorModal = angular.module(MODULE_NAME, [
|
||||
require('../modal/modal'),
|
||||
require('../../models/selection-state'),
|
||||
require('../../models/flash-state'),
|
||||
require('../../modules/analytics')
|
||||
]);
|
||||
|
||||
FlashErrorModal.controller('FlashErrorModalController', require('./controllers/flash-error-modal'));
|
||||
FlashErrorModal.service('FlashErrorModalService', require('./services/flash-error-modal'));
|
||||
|
||||
module.exports = MODULE_NAME;
|
@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Copyright 2016 Resin.io
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
const _ = require('lodash');
|
||||
|
||||
module.exports = function(ModalService) {
|
||||
|
||||
/**
|
||||
* @summary Open the flash error modal
|
||||
* @function
|
||||
* @public
|
||||
*
|
||||
* @param {String} message - flash error message
|
||||
* @returns {Promise}
|
||||
*
|
||||
* @example
|
||||
* FlashErrorModalService.show('The drive is not large enough!');
|
||||
*/
|
||||
this.show = (message) => {
|
||||
return ModalService.open({
|
||||
template: './components/flash-error-modal/templates/flash-error-modal.tpl.html',
|
||||
controller: 'FlashErrorModalController as modal',
|
||||
size: 'flash-error-modal',
|
||||
resolve: {
|
||||
flashErrorData: _.constant({
|
||||
message: message
|
||||
})
|
||||
}
|
||||
}).result;
|
||||
};
|
||||
|
||||
};
|
@ -0,0 +1,19 @@
|
||||
/*
|
||||
* Copyright 2016 Resin.io
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
.modal-flash-error-modal .modal-title .glyphicon {
|
||||
color: $palette-theme-danger-background;
|
||||
}
|
@ -0,0 +1,17 @@
|
||||
<div class="modal-header">
|
||||
<h4 class="modal-title">
|
||||
<span class="glyphicon glyphicon-exclamation-sign"></span>
|
||||
<span>An error ocurred!</span>
|
||||
</h4>
|
||||
|
||||
<button class="close" ng-click="modal.retry()">×</button>
|
||||
</div>
|
||||
|
||||
<div class="modal-body">
|
||||
<div class="modal-text">{{ ::modal.data.message }}</div>
|
||||
</div>
|
||||
|
||||
<div class="modal-footer">
|
||||
<button class="button button-danger button-block"
|
||||
ng-click="modal.retry()">Retry</button>
|
||||
</div>
|
@ -23,6 +23,7 @@ module.exports = function(
|
||||
DriveScannerService,
|
||||
ImageWriterService,
|
||||
AnalyticsService,
|
||||
FlashErrorModalService,
|
||||
ErrorService,
|
||||
OSNotificationService,
|
||||
OSWindowProgressService
|
||||
@ -72,10 +73,14 @@ module.exports = function(
|
||||
OSNotificationService.send('Oops!', 'Looks like your flash has failed');
|
||||
|
||||
if (error.code === 'EVALIDATION') {
|
||||
FlashErrorModalService.show('Your removable drive may be corrupted. Try inserting a different one and try again.');
|
||||
AnalyticsService.logEvent('Validation error');
|
||||
} else if (error.code === 'ENOSPC') {
|
||||
FlashErrorModalService.show('Not enough space on the drive. Please insert larger one and try again.');
|
||||
} else {
|
||||
AnalyticsService.logEvent('Flash error');
|
||||
FlashErrorModalService.show('Oops, seems something went wrong.');
|
||||
ErrorService.reportException(error);
|
||||
AnalyticsService.logEvent('Flash error');
|
||||
}
|
||||
|
||||
})
|
||||
|
@ -21,7 +21,6 @@ module.exports = function(
|
||||
DrivesModel,
|
||||
FlashStateModel,
|
||||
SettingsModel,
|
||||
AnalyticsService,
|
||||
TooltipModalService,
|
||||
OSOpenExternalService
|
||||
) {
|
||||
@ -34,23 +33,6 @@ module.exports = function(
|
||||
this.external = OSOpenExternalService;
|
||||
this.tooltipModal = TooltipModalService;
|
||||
|
||||
/**
|
||||
* @summary Restart after failure
|
||||
* @function
|
||||
* @public
|
||||
*
|
||||
* @example
|
||||
* MainController.restartAfterFailure();
|
||||
*/
|
||||
this.restartAfterFailure = () => {
|
||||
SelectionStateModel.clear({
|
||||
preserveImage: true
|
||||
});
|
||||
|
||||
FlashStateModel.resetState();
|
||||
AnalyticsService.logEvent('Restart after failure');
|
||||
};
|
||||
|
||||
/**
|
||||
* @summary Determine if the drive step should be disabled
|
||||
* @function
|
||||
|
@ -33,6 +33,7 @@ const MainPage = angular.module(MODULE_NAME, [
|
||||
|
||||
require('../../components/drive-selector/drive-selector'),
|
||||
require('../../components/tooltip-modal/tooltip-modal'),
|
||||
require('../../components/flash-error-modal/flash-error-modal'),
|
||||
require('../../components/progress-button/progress-button'),
|
||||
|
||||
require('../../os/window-progress/window-progress'),
|
||||
|
@ -1,28 +1,4 @@
|
||||
<div class="page-main row around-xs">
|
||||
|
||||
<div class="alert-ribbon" ng-class="{
|
||||
'alert-ribbon--open': main.state.getLastFlashErrorCode()
|
||||
}" ng-switch="main.state.getLastFlashErrorCode()">
|
||||
<span class="glyphicon glyphicon-warning-sign"></span>
|
||||
|
||||
<span ng-switch-when="ENOSPC">
|
||||
Not enough space on the drive.<br>
|
||||
Please insert larger one and
|
||||
<button class="button button-link" ng-click="main.restartAfterFailure()">try again</button>
|
||||
</span>
|
||||
|
||||
<span ng-switch-when="EVALIDATION">
|
||||
Your removable drive may be corrupted.
|
||||
<br>Try inserting a different one and
|
||||
<button class="button button-link" ng-click="main.restartAfterFailure()">press "retry"</button>
|
||||
</span>
|
||||
|
||||
<span ng-switch-default>
|
||||
Oops, seems something went wrong.
|
||||
Click <button class="button button-link" ng-click="main.restartAfterFailure()">here</button> to retry
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="col-xs" ng-controller="ImageSelectionController as image">
|
||||
<div class="box text-center" os-dropzone="image.selectImage($file)">
|
||||
<svg-icon class="center-block" path="{{ main.selection.getImageLogo() || '../../../assets/image.svg' }}"></svg-icon>
|
||||
@ -114,16 +90,11 @@
|
||||
percentage="main.state.getFlashState().percentage"
|
||||
striped="{{ main.state.getFlashState().type == 'check' }}"
|
||||
ng-attr-active="{{ main.state.isFlashing() }}"
|
||||
ng-show="!main.state.getLastFlashErrorCode()"
|
||||
ng-click="flash.flashImageToDrive(main.selection.getImagePath(), main.selection.getDrive())"
|
||||
ng-disabled="main.shouldFlashStepBeDisabled()">
|
||||
ng-disabled="main.shouldFlashStepBeDisabled() || main.state.getLastFlashErrorCode()">
|
||||
<span ng-bind="flash.getProgressButtonLabel()"></span>
|
||||
</progress-button>
|
||||
|
||||
<button class="button button-warning button-brick" ng-show="main.state.getLastFlashErrorCode()" ng-click="main.restartAfterFailure()">
|
||||
<span class="glyphicon glyphicon-repeat"></span> Retry
|
||||
</button>
|
||||
|
||||
<p class="step-footer step-footer-split" ng-show="main.state.getFlashState().speed && main.state.getFlashState().percentage != 100">
|
||||
<span>ETA: {{ main.state.getFlashState().eta | secondsToDate | amDateFormat:'m[m]ss[s]' }}</span>
|
||||
<span ng-bind="main.state.getFlashState().speed.toFixed(2) + ' MB/s'"></span>
|
||||
|
@ -1,66 +0,0 @@
|
||||
/*
|
||||
* Copyright 2016 Resin.io
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
.alert-ribbon {
|
||||
@extend .alert;
|
||||
|
||||
background-color: $palette-theme-warning-background;
|
||||
color: $palette-theme-warning-foreground;
|
||||
border-color: lighten($palette-theme-warning-background, 25%);
|
||||
|
||||
width: 60%;
|
||||
position: fixed;
|
||||
text-align: center;
|
||||
|
||||
left: 0;
|
||||
right: 0;
|
||||
margin: 0 auto;
|
||||
|
||||
border: 0;
|
||||
border-radius: 2px;
|
||||
border-top-left-radius: 0;
|
||||
border-top-right-radius: 0;
|
||||
|
||||
// Animate appearance
|
||||
top: -100%;
|
||||
transition: top .5s;
|
||||
|
||||
// Align alert icons a bit better
|
||||
> .glyphicon {
|
||||
&:first-child {
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
margin-left: 5px;
|
||||
}
|
||||
}
|
||||
|
||||
.button-link {
|
||||
padding: 0;
|
||||
font-size: inherit;
|
||||
vertical-align: baseline;
|
||||
border-radius: 0;
|
||||
border-bottom: 1px solid;
|
||||
|
||||
border-color: $palette-theme-warning-foreground;
|
||||
color: $palette-theme-warning-foreground;
|
||||
}
|
||||
}
|
||||
|
||||
.alert-ribbon--open {
|
||||
top: 0;
|
||||
}
|
@ -66,10 +66,6 @@ $button-types-styles: (
|
||||
bg: $palette-theme-primary-background,
|
||||
color: $palette-theme-primary-foreground
|
||||
),
|
||||
warning: (
|
||||
bg: $palette-theme-warning-background,
|
||||
color: $palette-theme-warning-foreground
|
||||
),
|
||||
danger: (
|
||||
bg: $palette-theme-danger-background,
|
||||
color: $palette-theme-danger-foreground
|
||||
|
@ -18,7 +18,6 @@ $icon-font-path: "../../node_modules/bootstrap-sass/assets/fonts/bootstrap/";
|
||||
$font-size-base: 13px;
|
||||
$cursor-disabled: initial;
|
||||
$link-hover-decoration: none;
|
||||
$alert-padding: 13px;
|
||||
$btn-min-width: 170px;
|
||||
$link-color: #ddd;
|
||||
|
||||
@ -31,12 +30,12 @@ $link-color: #ddd;
|
||||
@import "./components/caption";
|
||||
@import "./components/button";
|
||||
@import "./components/tick";
|
||||
@import "./components/alert-ribbon";
|
||||
@import "../components/modal/styles/modal";
|
||||
@import "../components/update-notifier/styles/update-notifier";
|
||||
@import "../components/progress-button/styles/progress-button";
|
||||
@import "../components/drive-selector/styles/drive-selector";
|
||||
@import "../components/tooltip-modal/styles/tooltip-modal";
|
||||
@import "../components/flash-error-modal/styles/flash-error-modal";
|
||||
@import "../pages/main/styles/main";
|
||||
@import "../pages/settings/styles/settings";
|
||||
@import "../pages/finish/styles/finish";
|
||||
|
@ -29,8 +29,6 @@ $palette-theme-default-background: #ececec;
|
||||
$palette-theme-default-foreground: #b3b3b3;
|
||||
$palette-theme-primary-background: #5793db;
|
||||
$palette-theme-primary-foreground: #fff;
|
||||
$palette-theme-warning-background: #e99852;
|
||||
$palette-theme-warning-foreground: #fff;
|
||||
$palette-theme-danger-background: #d9534f;
|
||||
$palette-theme-danger-foreground: #fff;
|
||||
$palette-theme-success-background: #5fb835;
|
||||
|
Loading…
x
Reference in New Issue
Block a user