mirror of
https://github.com/balena-io/etcher.git
synced 2025-07-19 09:16:38 +00:00
Merge pull request #2965 from balena-io/revamp-settings
Refactor settings page into modal
This commit is contained in:
commit
0ab967b7a4
2
.gitattributes
vendored
2
.gitattributes
vendored
@ -1,6 +1,8 @@
|
||||
# Javascript files must retain LF line-endings (to keep eslint happy)
|
||||
*.js text eol=lf
|
||||
*.jsx text eol=lf
|
||||
*.ts text eol=lf
|
||||
*.tsx text eol=lf
|
||||
# CSS and SCSS files must retain LF line-endings (to keep ensure-staged-sass.sh happy)
|
||||
*.css text eol=lf
|
||||
*.scss text eol=lf
|
||||
|
@ -94,7 +94,7 @@ const app = angular.module('Etcher', [
|
||||
// Pages
|
||||
require('./pages/main/main'),
|
||||
require('./pages/finish/finish'),
|
||||
require('./pages/settings/settings'),
|
||||
require('./components/settings/index.ts').MODULE_NAME,
|
||||
|
||||
// OS
|
||||
require('./os/open-external/open-external'),
|
||||
|
@ -189,7 +189,7 @@ const File = styled(UnstyledFile)`
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
text-align: center;
|
||||
font-size: 14px;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
> div:last-child {
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2016 resin.io
|
||||
* Copyright 2019 balena.io
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@ -14,14 +14,15 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
.page-settings .checkbox input[type="checkbox"] + * {
|
||||
color: $palette-theme-dark-foreground;
|
||||
}
|
||||
/**
|
||||
* @module Etcher.Components.FeaturedProject
|
||||
*/
|
||||
|
||||
.page-settings .checkbox input[type="checkbox"]:not(:checked) + * {
|
||||
color: $palette-theme-dark-soft-foreground;
|
||||
}
|
||||
import * as angular from 'angular';
|
||||
import { react2angular } from 'react2angular';
|
||||
import { SettingsButton } from './settings';
|
||||
|
||||
.page-settings .title {
|
||||
color: $palette-theme-dark-foreground;
|
||||
}
|
||||
export const MODULE_NAME = 'Etcher.Components.Settings';
|
||||
const Settings = angular.module(MODULE_NAME, []);
|
||||
|
||||
Settings.component('settings', react2angular(SettingsButton));
|
233
lib/gui/app/components/settings/settings.tsx
Normal file
233
lib/gui/app/components/settings/settings.tsx
Normal file
@ -0,0 +1,233 @@
|
||||
/*
|
||||
* Copyright 2019 balena.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.
|
||||
*/
|
||||
|
||||
import { faCog } from '@fortawesome/free-solid-svg-icons';
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||
import * as _ from 'lodash';
|
||||
import * as os from 'os';
|
||||
import * as propTypes from 'prop-types';
|
||||
import * as React from 'react';
|
||||
import { Badge, Button, Checkbox, Modal, Provider } from 'rendition';
|
||||
import styled from 'styled-components';
|
||||
import * as settings from '../../models/settings';
|
||||
import * as store from '../../models/store';
|
||||
import * as analytics from '../../modules/analytics';
|
||||
import { colors } from '../../theme';
|
||||
|
||||
const { useState } = React;
|
||||
const platform = os.platform();
|
||||
|
||||
export const SettingsButton = () => {
|
||||
const [hideModal, setHideModal] = useState(true);
|
||||
|
||||
return (
|
||||
<Provider>
|
||||
<Button
|
||||
icon={<FontAwesomeIcon icon={faCog} />}
|
||||
color={colors.secondary.background}
|
||||
fontSize={24}
|
||||
plain
|
||||
onClick={() => setHideModal(false)}
|
||||
tabIndex={5}
|
||||
></Button>
|
||||
{hideModal ? null : (
|
||||
<SettingsModal toggleModal={(value: boolean) => setHideModal(!value)} />
|
||||
)}
|
||||
</Provider>
|
||||
);
|
||||
};
|
||||
|
||||
SettingsButton.propTypes = {};
|
||||
|
||||
interface WarningModalProps {
|
||||
message: string;
|
||||
confirmLabel: string;
|
||||
cancel: () => void;
|
||||
done: () => void;
|
||||
}
|
||||
|
||||
const WarningModal = ({
|
||||
message,
|
||||
confirmLabel,
|
||||
cancel,
|
||||
done,
|
||||
}: WarningModalProps) => {
|
||||
return (
|
||||
<Modal
|
||||
title={confirmLabel}
|
||||
action={confirmLabel}
|
||||
cancel={cancel}
|
||||
done={done}
|
||||
style={{
|
||||
width: 420,
|
||||
height: 300,
|
||||
}}
|
||||
primaryButtonProps={{ warning: true }}
|
||||
>
|
||||
{message}
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
|
||||
interface Setting {
|
||||
name: string;
|
||||
label: string | JSX.Element;
|
||||
options?: any;
|
||||
hide?: boolean;
|
||||
}
|
||||
|
||||
const settingsList: Setting[] = [
|
||||
{
|
||||
name: 'errorReporting',
|
||||
label: 'Anonymously report errors and usage statistics to balena.io',
|
||||
},
|
||||
{
|
||||
name: 'unmountOnSuccess',
|
||||
/**
|
||||
* On Windows, "Unmounting" basically means "ejecting".
|
||||
* On top of that, Windows users are usually not even
|
||||
* familiar with the meaning of "unmount", which comes
|
||||
* from the UNIX world.
|
||||
*/
|
||||
label: `${platform === 'win32' ? 'Eject' : 'Auto-unmount'} on success`,
|
||||
},
|
||||
{
|
||||
name: 'validateWriteOnSuccess',
|
||||
label: 'Validate write on success',
|
||||
},
|
||||
{
|
||||
name: 'trim',
|
||||
label: 'Trim ext{2,3,4} partitions before writing (raw images only)',
|
||||
},
|
||||
{
|
||||
name: 'updatesEnabled',
|
||||
label: 'Auto-updates enabled',
|
||||
},
|
||||
{
|
||||
name: 'unsafeMode',
|
||||
label: (
|
||||
<span>
|
||||
Unsafe mode{' '}
|
||||
<Badge danger fontSize={12}>
|
||||
Dangerous
|
||||
</Badge>
|
||||
</span>
|
||||
),
|
||||
options: {
|
||||
description: `Are you sure you want to turn this on?
|
||||
You will be able to overwrite your system drives if you're not careful.`,
|
||||
confirmLabel: 'Enable unsafe mode',
|
||||
},
|
||||
hide: settings.get('disableUnsafeMode'),
|
||||
},
|
||||
];
|
||||
|
||||
interface SettingsModalProps {
|
||||
toggleModal: (value: boolean) => void;
|
||||
}
|
||||
|
||||
export const SettingsModal: any = styled(
|
||||
({ toggleModal }: SettingsModalProps) => {
|
||||
const [currentSettings, setCurrentSettings] = useState(settings.getAll());
|
||||
const [warning, setWarning]: [
|
||||
any,
|
||||
React.Dispatch<React.SetStateAction<any>>,
|
||||
] = useState({});
|
||||
|
||||
const toggleSetting = async (setting: string, options?: any) => {
|
||||
const value = currentSettings[setting];
|
||||
const dangerous = !_.isUndefined(options);
|
||||
|
||||
analytics.logEvent('Toggle setting', {
|
||||
setting,
|
||||
value,
|
||||
dangerous,
|
||||
applicationSessionUuid: store.getState().toJS().applicationSessionUuid,
|
||||
});
|
||||
|
||||
if (value || !dangerous) {
|
||||
await settings.set(setting, !value);
|
||||
setCurrentSettings({
|
||||
...currentSettings,
|
||||
[setting]: !value,
|
||||
});
|
||||
setWarning({});
|
||||
return;
|
||||
}
|
||||
|
||||
// Show warning since it's a dangerous setting
|
||||
setWarning({
|
||||
setting,
|
||||
settingValue: value,
|
||||
...options,
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<Modal
|
||||
id="settings-modal"
|
||||
title="Settings"
|
||||
done={() => toggleModal(false)}
|
||||
style={{
|
||||
width: 780,
|
||||
height: 420,
|
||||
}}
|
||||
>
|
||||
<div>
|
||||
{_.map(settingsList, (setting: Setting, i: number) => {
|
||||
return setting.hide ? null : (
|
||||
<div key={setting.name}>
|
||||
<Checkbox
|
||||
toggle
|
||||
tabIndex={6 + i}
|
||||
label={setting.label}
|
||||
checked={currentSettings[setting.name]}
|
||||
onChange={() => toggleSetting(setting.name, setting.options)}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
|
||||
{_.isEmpty(warning) ? null : (
|
||||
<WarningModal
|
||||
message={warning.description}
|
||||
confirmLabel={warning.confirmLabel}
|
||||
done={() => {
|
||||
settings.set(warning.setting, !warning.settingValue);
|
||||
setCurrentSettings({
|
||||
...currentSettings,
|
||||
[warning.setting]: true,
|
||||
});
|
||||
setWarning({});
|
||||
}}
|
||||
cancel={() => {
|
||||
setWarning({});
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</Modal>
|
||||
);
|
||||
},
|
||||
)`
|
||||
> div:nth-child(3) {
|
||||
justify-content: center;
|
||||
}
|
||||
`;
|
||||
|
||||
SettingsModal.propTypes = {
|
||||
toggleModal: propTypes.func,
|
||||
};
|
@ -11,57 +11,27 @@
|
||||
</head>
|
||||
<body>
|
||||
<header class="section-header" ng-controller="HeaderController as header">
|
||||
<button class="button button-link"
|
||||
ng-if="header.shouldShowHelp()"
|
||||
ng-click="header.openHelpPage()"
|
||||
tabindex="4">
|
||||
<span class="glyphicon glyphicon-question-sign"></span>
|
||||
</button>
|
||||
|
||||
<button class="button button-link"
|
||||
ui-sref="settings"
|
||||
hide-if-state="settings"
|
||||
tabindex="5">
|
||||
<span class="glyphicon glyphicon-cog"></span>
|
||||
</button>
|
||||
|
||||
<button class="button button-link"
|
||||
tabindex="5"
|
||||
ui-sref="main"
|
||||
show-if-state="settings">
|
||||
<span class="glyphicon glyphicon-chevron-left"></span> Back
|
||||
</button>
|
||||
</header>
|
||||
|
||||
<main class="wrapper" ui-view></main>
|
||||
|
||||
<footer class="section-footer-main" ng-controller="StateController as state"
|
||||
ng-hide="state.currentName === 'success'">
|
||||
<span os-open-external="https://www.balena.io/etcher?ref=etcher_footer"
|
||||
<span
|
||||
id="app-logo"
|
||||
os-open-external="https://www.balena.io/etcher?ref=etcher_footer"
|
||||
tabindex="100">
|
||||
<svg-icon paths="[ '../../assets/etcher.svg' ]"
|
||||
width="'123px'"
|
||||
height="'22px'"></svg-icon>
|
||||
</span>
|
||||
|
||||
<span class="caption">
|
||||
is <span class="caption"
|
||||
tabindex="101"
|
||||
os-open-external="https://github.com/balena-io/etcher">an open source project</span> by
|
||||
</span>
|
||||
<settings tabindex="4">
|
||||
</settings>
|
||||
|
||||
<span os-open-external="https://www.balena.io?ref=etcher"
|
||||
tabindex="102">
|
||||
<svg-icon paths="[ '../../assets/balena.svg' ]"
|
||||
width="'79px'"
|
||||
height="'23px'"></svg-icon>
|
||||
</span>
|
||||
<button class="button button-link"
|
||||
ng-if="header.shouldShowHelp()"
|
||||
ng-click="header.openHelpPage()"
|
||||
tabindex="5">
|
||||
<span class="glyphicon glyphicon-question-sign"></span>
|
||||
</button>
|
||||
</header>
|
||||
|
||||
<span class="caption footer-right"
|
||||
tabindex="103"
|
||||
manifest-bind="version"
|
||||
os-open-external="https://github.com/balena-io/etcher/blob/master/CHANGELOG.md"></span>
|
||||
</footer>
|
||||
<main class="wrapper" ui-view></main>
|
||||
|
||||
<div class="section-loader"
|
||||
ng-controller="StateController as state"
|
||||
|
@ -120,7 +120,7 @@ svg-icon > img[disabled] {
|
||||
}
|
||||
|
||||
.page-main .button.step-footer {
|
||||
font-size: 14px;
|
||||
font-size: 16px;
|
||||
color: $palette-theme-primary-background;
|
||||
border-radius: 0;
|
||||
padding: 0;
|
||||
@ -166,7 +166,7 @@ svg-icon > img[disabled] {
|
||||
.page-main .step-size {
|
||||
color: $palette-theme-dark-disabled-foreground;
|
||||
margin: 0 0 8px 0;
|
||||
font-size: 14px;
|
||||
font-size: 16px;
|
||||
line-height: 1.5;
|
||||
height: 21px;
|
||||
width: 100%;
|
||||
@ -191,7 +191,7 @@ svg-icon > img[disabled] {
|
||||
.target-status-line {
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
font-size: 14px;
|
||||
font-size: 16px;
|
||||
font-family: inherit;
|
||||
|
||||
> .target-status-dot {
|
||||
@ -226,3 +226,17 @@ svg-icon > img[disabled] {
|
||||
.space-vertical-large {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
body.rendition-modal-open > div:last-child > div > div > div:last-child {
|
||||
top: unset;
|
||||
bottom: -200px;
|
||||
}
|
||||
|
||||
#app-logo {
|
||||
position: fixed;
|
||||
left: 0;
|
||||
right: 0;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
width: 123px;
|
||||
}
|
||||
|
@ -1,123 +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.
|
||||
*/
|
||||
|
||||
'use strict'
|
||||
|
||||
const os = require('os')
|
||||
const _ = require('lodash')
|
||||
const store = require('../../../models/store')
|
||||
const settings = require('../../../models/settings')
|
||||
const analytics = require('../../../modules/analytics')
|
||||
const exceptionReporter = require('../../../modules/exception-reporter')
|
||||
|
||||
module.exports = function (WarningModalService) {
|
||||
/**
|
||||
* @summary Client platform
|
||||
* @type {String}
|
||||
* @constant
|
||||
* @public
|
||||
*/
|
||||
this.platform = os.platform()
|
||||
|
||||
/**
|
||||
* @summary Refresh current settings
|
||||
* @function
|
||||
* @public
|
||||
*
|
||||
* @example
|
||||
* SettingsController.refreshSettings();
|
||||
*/
|
||||
this.refreshSettings = () => {
|
||||
this.currentData = settings.getAll()
|
||||
}
|
||||
|
||||
/**
|
||||
* @summary Current settings value
|
||||
* @type {Object}
|
||||
* @public
|
||||
*/
|
||||
this.currentData = {}
|
||||
this.refreshSettings()
|
||||
|
||||
/**
|
||||
* @summary Settings model
|
||||
* @type {Object}
|
||||
* @public
|
||||
*/
|
||||
this.model = settings
|
||||
|
||||
/**
|
||||
* @summary Toggle setting
|
||||
* @function
|
||||
* @public
|
||||
*
|
||||
* @description
|
||||
* If warningOptions is given, it should be an object having `description` and `confirmationLabel`;
|
||||
* these will be used to present a user confirmation modal before enabling the setting.
|
||||
* If warningOptions is missing, no confirmation modal is displayed.
|
||||
*
|
||||
* @param {String} setting - setting key
|
||||
* @param {Object} [options] - options
|
||||
* @param {String} [options.description] - warning modal description
|
||||
* @param {String} [options.confirmationLabel] - warning modal confirmation label
|
||||
* @returns {Undefined}
|
||||
*
|
||||
* @example
|
||||
* SettingsController.toggle('unsafeMode', {
|
||||
* description: 'Don\'t do this!',
|
||||
* confirmationLabel: 'Do it!'
|
||||
* });
|
||||
*/
|
||||
this.toggle = (setting, options) => {
|
||||
const value = this.currentData[setting]
|
||||
const dangerous = !_.isUndefined(options)
|
||||
|
||||
analytics.logEvent('Toggle setting', {
|
||||
setting,
|
||||
value,
|
||||
dangerous,
|
||||
applicationSessionUuid: store.getState().toJS().applicationSessionUuid
|
||||
})
|
||||
|
||||
if (!value || !dangerous) {
|
||||
return this.model.set(setting, value)
|
||||
}
|
||||
|
||||
// Keep the checkbox unchecked until the user confirms
|
||||
this.currentData[setting] = false
|
||||
|
||||
return WarningModalService.display(options).then((userAccepted) => {
|
||||
if (userAccepted) {
|
||||
this.model.set(setting, true)
|
||||
this.refreshSettings()
|
||||
}
|
||||
}).catch(exceptionReporter.report)
|
||||
}
|
||||
|
||||
/**
|
||||
* @summary Show unsafe mode based on an env var
|
||||
* @function
|
||||
* @public
|
||||
*
|
||||
* @returns {Boolean}
|
||||
*
|
||||
* @example
|
||||
* SettingsController.shouldShowUnsafeMode()
|
||||
*/
|
||||
this.shouldShowUnsafeMode = () => {
|
||||
return !settings.get('disableUnsafeMode')
|
||||
}
|
||||
}
|
@ -1,41 +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.
|
||||
*/
|
||||
|
||||
'use strict'
|
||||
|
||||
/**
|
||||
* @module Etcher.Pages.Settings
|
||||
*/
|
||||
|
||||
const angular = require('angular')
|
||||
const MODULE_NAME = 'Etcher.Pages.Settings'
|
||||
const SettingsPage = angular.module(MODULE_NAME, [
|
||||
require('angular-ui-router'),
|
||||
require('../../components/warning-modal/warning-modal')
|
||||
])
|
||||
|
||||
SettingsPage.controller('SettingsController', require('./controllers/settings'))
|
||||
|
||||
SettingsPage.config(($stateProvider) => {
|
||||
$stateProvider
|
||||
.state('settings', {
|
||||
url: '/settings',
|
||||
controller: 'SettingsController as settings',
|
||||
template: require('./templates/settings.tpl.html')
|
||||
})
|
||||
})
|
||||
|
||||
module.exports = MODULE_NAME
|
@ -1,80 +0,0 @@
|
||||
<div class="page-settings text-left">
|
||||
<h1 class="title space-bottom-large">Settings</h1>
|
||||
|
||||
<div class="checkbox">
|
||||
<label>
|
||||
<input type="checkbox"
|
||||
tabindex="6"
|
||||
ng-model="settings.currentData.errorReporting"
|
||||
ng-change="settings.toggle('errorReporting')">
|
||||
|
||||
<span>Anonymously report errors and usage statistics to balena.io</span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="checkbox">
|
||||
<label>
|
||||
<input type="checkbox"
|
||||
tabindex="7"
|
||||
ng-model="settings.currentData.unmountOnSuccess"
|
||||
ng-change="settings.toggle('unmountOnSuccess')">
|
||||
|
||||
<!-- On Windows, "Unmounting" basically means "ejecting". -->
|
||||
<!-- On top of that, Windows users are usually not even -->
|
||||
<!-- familiar with the meaning of "unmount", which comes -->
|
||||
<!-- from the UNIX world. -->
|
||||
|
||||
<span>
|
||||
<span ng-show="settings.platform == 'win32'">Eject</span>
|
||||
<span ng-hide="settings.platform == 'win32'">Auto-unmount</span>
|
||||
on success
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="checkbox">
|
||||
<label>
|
||||
<input type="checkbox"
|
||||
tabindex="8"
|
||||
ng-model="settings.currentData.validateWriteOnSuccess"
|
||||
ng-change="settings.toggle('validateWriteOnSuccess')">
|
||||
|
||||
<span>Validate write on success</span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="checkbox">
|
||||
<label>
|
||||
<input type="checkbox"
|
||||
tabindex="8"
|
||||
ng-model="settings.currentData.trim"
|
||||
ng-change="settings.toggle('trim')">
|
||||
|
||||
<span>Trim ext{2,3,4} partitions before writing (raw images only)</span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="checkbox">
|
||||
<label>
|
||||
<input type="checkbox"
|
||||
tabindex="9"
|
||||
ng-model="settings.currentData.updatesEnabled"
|
||||
ng-change="settings.toggle('updatesEnabled')">
|
||||
|
||||
<span>Auto-updates enabled</span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="checkbox" ng-if="settings.shouldShowUnsafeMode()">
|
||||
<label>
|
||||
<input type="checkbox"
|
||||
tabindex="10"
|
||||
ng-model="settings.currentData.unsafeMode"
|
||||
ng-change="settings.toggle('unsafeMode', {
|
||||
description: 'Are you sure you want to turn this on? You will be able to overwrite your system drives if you\'re not careful.',
|
||||
confirmationLabel: 'Enable unsafe mode'
|
||||
})">
|
||||
<span>Unsafe mode <span class="label label-danger">Dangerous</span></span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
@ -29,8 +29,9 @@
|
||||
position: relative;
|
||||
|
||||
> .glyphicon {
|
||||
top: 2px;
|
||||
margin-right: 2px;
|
||||
top: 0;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
}
|
||||
|
||||
&.button-primary{
|
||||
|
@ -38,7 +38,6 @@ $disabled-opacity: 0.2;
|
||||
@import "../components/warning-modal/styles/warning-modal";
|
||||
@import "../components/file-selector/styles/file-selector";
|
||||
@import "../pages/main/styles/main";
|
||||
@import "../pages/settings/styles/settings";
|
||||
@import "../pages/finish/styles/finish";
|
||||
|
||||
$fa-font-path: "../../../node_modules/@fortawesome/fontawesome-free-webfonts/webfonts";
|
||||
@ -212,11 +211,21 @@ body {
|
||||
|
||||
.section-header {
|
||||
text-align: right;
|
||||
padding: 5px 8px;
|
||||
padding: 13px 14px;
|
||||
|
||||
> .button {
|
||||
padding-left: 3px;
|
||||
padding-right: 3px;
|
||||
padding: 0;
|
||||
|
||||
> .glyphicon {
|
||||
font-size: 24px;
|
||||
}
|
||||
}
|
||||
|
||||
> * {
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
height: 24px;
|
||||
margin: 0 10px;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -48,6 +48,10 @@ exports.colors = {
|
||||
foreground: '#fff',
|
||||
background: '#2297de'
|
||||
},
|
||||
secondary: {
|
||||
foreground: '#000',
|
||||
background: '#ddd'
|
||||
},
|
||||
warning: {
|
||||
foreground: '#fff',
|
||||
background: '#fca321'
|
||||
|
@ -6035,8 +6035,9 @@ body {
|
||||
outline: none;
|
||||
position: relative; }
|
||||
.button > .glyphicon, .button > .tick {
|
||||
top: 2px;
|
||||
margin-right: 2px; }
|
||||
top: 0;
|
||||
width: 24px;
|
||||
height: 24px; }
|
||||
.button.button-primary {
|
||||
width: 200px;
|
||||
height: 48px; }
|
||||
@ -6452,7 +6453,7 @@ svg-icon > img[disabled] {
|
||||
padding-bottom: 2px; }
|
||||
|
||||
.page-main .button.step-footer {
|
||||
font-size: 14px;
|
||||
font-size: 16px;
|
||||
color: #2297de;
|
||||
border-radius: 0;
|
||||
padding: 0;
|
||||
@ -6489,7 +6490,7 @@ svg-icon > img[disabled] {
|
||||
.page-main .step-size {
|
||||
color: #787c7f;
|
||||
margin: 0 0 8px 0;
|
||||
font-size: 14px;
|
||||
font-size: 16px;
|
||||
line-height: 1.5;
|
||||
height: 21px;
|
||||
width: 100%; }
|
||||
@ -6511,7 +6512,7 @@ svg-icon > img[disabled] {
|
||||
.target-status-line {
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
font-size: 14px;
|
||||
font-size: 16px;
|
||||
font-family: inherit; }
|
||||
.target-status-line > .target-status-dot {
|
||||
width: 12px;
|
||||
@ -6535,29 +6536,17 @@ svg-icon > img[disabled] {
|
||||
.space-vertical-large {
|
||||
position: relative; }
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
.page-settings .checkbox input[type="checkbox"] + * {
|
||||
color: #fff; }
|
||||
body.rendition-modal-open > div:last-child > div > div > div:last-child {
|
||||
top: unset;
|
||||
bottom: -200px; }
|
||||
|
||||
.page-settings .checkbox input[type="checkbox"]:not(:checked) + * {
|
||||
color: #ddd; }
|
||||
|
||||
.page-settings .title {
|
||||
color: #fff; }
|
||||
#app-logo {
|
||||
position: fixed;
|
||||
left: 0;
|
||||
right: 0;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
width: 123px; }
|
||||
|
||||
/*
|
||||
* Copyright 2016 resin.io
|
||||
@ -9973,10 +9962,16 @@ body {
|
||||
|
||||
.section-header {
|
||||
text-align: right;
|
||||
padding: 5px 8px; }
|
||||
padding: 13px 14px; }
|
||||
.section-header > .button {
|
||||
padding-left: 3px;
|
||||
padding-right: 3px; }
|
||||
padding: 0; }
|
||||
.section-header > .button > .glyphicon, .section-header > .button > .tick {
|
||||
font-size: 24px; }
|
||||
.section-header > * {
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
height: 24px;
|
||||
margin: 0 10px; }
|
||||
|
||||
featured-project webview {
|
||||
flex: 0 1;
|
||||
|
657
npm-shrinkwrap.json
generated
657
npm-shrinkwrap.json
generated
File diff suppressed because it is too large
Load Diff
@ -40,6 +40,9 @@
|
||||
],
|
||||
"dependencies": {
|
||||
"@fortawesome/fontawesome-free-webfonts": "^1.0.9",
|
||||
"@fortawesome/fontawesome-svg-core": "^1.2.25",
|
||||
"@fortawesome/free-solid-svg-icons": "^5.11.2",
|
||||
"@fortawesome/react-fontawesome": "^0.1.7",
|
||||
"angular": "1.7.6",
|
||||
"angular-if-state": "^1.0.0",
|
||||
"angular-moment": "^1.0.1",
|
||||
@ -68,7 +71,7 @@
|
||||
"react-dom": "^16.8.5",
|
||||
"react2angular": "^4.0.2",
|
||||
"redux": "^3.5.2",
|
||||
"rendition": "^8.7.2",
|
||||
"rendition": "^11.24.0",
|
||||
"request": "^2.81.0",
|
||||
"resin-corvus": "^2.0.3",
|
||||
"roboto-fontface": "^0.9.0",
|
||||
|
@ -25,6 +25,9 @@ angularValidate.validate(
|
||||
path.join(PROJECT_ROOT, 'lib', 'gui', '**/*.html')
|
||||
],
|
||||
{
|
||||
customtags: [
|
||||
'settings'
|
||||
],
|
||||
customattrs: [
|
||||
|
||||
// Internal
|
||||
|
@ -1,46 +0,0 @@
|
||||
/*
|
||||
* Copyright 2018 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 m = require('mochainon')
|
||||
const fs = require('fs')
|
||||
const angular = require('angular')
|
||||
require('angular-mocks')
|
||||
|
||||
describe('Browser: SettingsPage', function () {
|
||||
beforeEach(angular.mock.module(
|
||||
require('../../../lib/gui/app/pages/settings/settings')
|
||||
))
|
||||
|
||||
describe('page template', function () {
|
||||
let $state
|
||||
|
||||
beforeEach(angular.mock.inject(function (_$state_) {
|
||||
$state = _$state_
|
||||
}))
|
||||
|
||||
it('should match the file contents', function () {
|
||||
const {
|
||||
template
|
||||
} = $state.get('settings')
|
||||
const contents = fs.readFileSync('lib/gui/app/pages/settings/templates/settings.tpl.html', {
|
||||
encoding: 'utf-8'
|
||||
})
|
||||
m.chai.expect(template).to.equal(contents)
|
||||
})
|
||||
})
|
||||
})
|
@ -9,7 +9,8 @@
|
||||
"moduleResolution": "node",
|
||||
"module": "commonjs",
|
||||
"target": "es2017",
|
||||
"jsx": "react"
|
||||
"jsx": "react",
|
||||
"allowSyntheticDefaultImports": true
|
||||
},
|
||||
"include": [
|
||||
"lib/**/*.ts",
|
||||
|
Loading…
x
Reference in New Issue
Block a user