diff --git a/ui/app/components/download-button.js b/ui/app/components/download-button.js index 2e2847c887fa..4ba0c338e12f 100644 --- a/ui/app/components/download-button.js +++ b/ui/app/components/download-button.js @@ -4,7 +4,7 @@ import hbs from 'htmlbars-inline-precompile'; const { computed } = Ember; export default Ember.Component.extend({ - layout: hbs`{{actionText}}`, + layout: hbs`{{#if hasBlock}} {{yield}} {{else}} {{actionText}} {{/if}}`, tagName: 'a', role: 'button', attributeBindings: ['role', 'download', 'href'], diff --git a/ui/app/components/hover-copy-button.js b/ui/app/components/hover-copy-button.js new file mode 100644 index 000000000000..52c59f2f3323 --- /dev/null +++ b/ui/app/components/hover-copy-button.js @@ -0,0 +1,10 @@ +import Ember from 'ember'; + +export default Ember.Component.extend({ + 'data-test-hover-copy': true, + classNameBindings: 'alwaysShow:hover-copy-button-static:hover-copy-button', + copyValue: null, + alwaysShow: false, + + tooltipText: 'Copy', +}); diff --git a/ui/app/components/i-con.js b/ui/app/components/i-con.js index ff6a10f9dbb2..a4063be406ba 100644 --- a/ui/app/components/i-con.js +++ b/ui/app/components/i-con.js @@ -3,6 +3,7 @@ import hbs from 'htmlbars-inline-precompile'; const { computed } = Ember; const GLYPHS_WITH_SVG_TAG = [ + 'download', 'folder', 'file', 'hidden', diff --git a/ui/app/components/nav-header.js b/ui/app/components/nav-header.js new file mode 100644 index 000000000000..f36a904a1926 --- /dev/null +++ b/ui/app/components/nav-header.js @@ -0,0 +1,6 @@ +import Ember from 'ember'; + +export default Ember.Component.extend({ + 'data-test-navheader': true, + tagName: 'header', +}); diff --git a/ui/app/components/nav-header/home.js b/ui/app/components/nav-header/home.js new file mode 100644 index 000000000000..e3ac4fb5c0a7 --- /dev/null +++ b/ui/app/components/nav-header/home.js @@ -0,0 +1,5 @@ +import Ember from 'ember'; + +export default Ember.Component.extend({ + tagName: '', +}); diff --git a/ui/app/components/nav-header/items.js b/ui/app/components/nav-header/items.js new file mode 100644 index 000000000000..e3ac4fb5c0a7 --- /dev/null +++ b/ui/app/components/nav-header/items.js @@ -0,0 +1,5 @@ +import Ember from 'ember'; + +export default Ember.Component.extend({ + tagName: '', +}); diff --git a/ui/app/components/nav-header/main.js b/ui/app/components/nav-header/main.js new file mode 100644 index 000000000000..e3ac4fb5c0a7 --- /dev/null +++ b/ui/app/components/nav-header/main.js @@ -0,0 +1,5 @@ +import Ember from 'ember'; + +export default Ember.Component.extend({ + tagName: '', +}); diff --git a/ui/app/components/radial-progress.js b/ui/app/components/radial-progress.js new file mode 100644 index 000000000000..4ad0d8ddb348 --- /dev/null +++ b/ui/app/components/radial-progress.js @@ -0,0 +1,29 @@ +import Ember from 'ember'; +const { computed } = Ember; + +export default Ember.Component.extend({ + 'data-test-radial-progress': true, + tagName: 'svg', + classNames: 'radial-progress', + attributeBindings: ['size:width', 'size:height', 'viewBox'], + progressDecimal: null, + size: 20, + strokeWidth: 1, + + viewBox: computed('size', function() { + let s = this.get('size'); + return `0 0 ${s} ${s}`; + }), + centerValue: computed('size', function() { + return this.get('size') / 2; + }), + r: computed('size', 'strokeWidth', function() { + return (this.get('size') - this.get('strokeWidth')) / 2; + }), + c: computed('r', function() { + return 2 * Math.PI * this.get('r'); + }), + dashArrayOffset: computed('c', 'progressDecimal', function() { + return this.get('c') * (1 - this.get('progressDecimal')); + }), +}); diff --git a/ui/app/components/replication-mode-summary.js b/ui/app/components/replication-mode-summary.js index 6c26d98ec7a8..5035f459adf6 100644 --- a/ui/app/components/replication-mode-summary.js +++ b/ui/app/components/replication-mode-summary.js @@ -34,7 +34,7 @@ export default Ember.Component.extend({ }), isPerformance: computed.equal('mode', 'performance'), replicationEnabled: replicationAttr('replicationEnabled'), - replicationUnsupported: replicationAttr('replicationUnsupported'), + replicationUnsupported: computed.equal('cluster.mode', 'unsupported'), replicationDisabled: replicationAttr('replicationDisabled'), syncProgressPercent: replicationAttr('syncProgressPercent'), syncProgress: replicationAttr('syncProgress'), diff --git a/ui/app/components/shamir-flow.js b/ui/app/components/shamir-flow.js index 5df2bc38d708..585e0942b29f 100644 --- a/ui/app/components/shamir-flow.js +++ b/ui/app/components/shamir-flow.js @@ -9,6 +9,8 @@ const DEFAULTS = { errors: [], threshold: null, progress: null, + pgp_key: null, + haveSavedPGPKey: false, started: false, generateWithPGP: false, pgpKeyFile: { value: '' }, @@ -32,7 +34,7 @@ export default Component.extend(DEFAULTS, { return this._super(...arguments); }, - onShamirSuccess: _ => _, + onShamirSuccess() {}, // can be overridden w/an attr isComplete(data) { return data.complete === true; @@ -76,6 +78,28 @@ export default Component.extend(DEFAULTS, { } }, + generateStep: computed('generateWithPGP', 'haveSavedPGPKey', 'otp', 'pgp_key', function() { + let { generateWithPGP, otp, pgp_key, haveSavedPGPKey } = this.getProperties( + 'generateWithPGP', + 'otp', + 'pgp_key', + 'haveSavedPGPKey' + ); + if (!generateWithPGP && !pgp_key && !otp) { + return 'chooseMethod'; + } + if (otp) { + return 'beginGenerationWithOTP'; + } + if (generateWithPGP) { + if (pgp_key && haveSavedPGPKey) { + return 'beginGenerationWithPGP'; + } else { + return 'providePGPKey'; + } + } + }), + extractData(data) { const isGenerate = this.get('generateAction'); const hasStarted = this.get('started'); @@ -113,6 +137,12 @@ export default Component.extend(DEFAULTS, { }, actions: { + reset() { + this.reset(); + this.set('encoded_token', null); + this.set('otp', null); + }, + onSubmit(data) { if (!data.key) { return; @@ -135,8 +165,10 @@ export default Component.extend(DEFAULTS, { this.set('pgpKeyFile', keyFile); }, - clearToken() { - this.set('encoded_token', null); + savePGPKey() { + if (this.get('pgp_key')) { + this.set('haveSavedPGPKey', true); + } }, }, }); diff --git a/ui/app/components/shamir-progress.js b/ui/app/components/shamir-progress.js index c470cd0a1d3a..bd0fe758d2ec 100644 --- a/ui/app/components/shamir-progress.js +++ b/ui/app/components/shamir-progress.js @@ -1,13 +1,14 @@ import Ember from 'ember'; +const { computed } = Ember; export default Ember.Component.extend({ threshold: null, progress: null, classNames: ['shamir-progress'], - progressPercent: Ember.computed('threshold', 'progress', function() { + progressDecimal: computed('threshold', 'progress', function() { const { threshold, progress } = this.getProperties('threshold', 'progress'); if (threshold && progress) { - return progress / threshold * 100; + return progress / threshold; } return 0; }), diff --git a/ui/app/components/splash-page.js b/ui/app/components/splash-page.js index e3ac4fb5c0a7..708ff64b7d25 100644 --- a/ui/app/components/splash-page.js +++ b/ui/app/components/splash-page.js @@ -1,5 +1,14 @@ import Ember from 'ember'; +const { computed, inject } = Ember; + export default Ember.Component.extend({ + version: inject.service(), + auth: inject.service(), + store: inject.service(), tagName: '', + + activeCluster: computed('auth.activeCluster', function() { + return this.get('store').peekRecord('cluster', this.get('auth.activeCluster')); + }), }); diff --git a/ui/app/components/status-menu.js b/ui/app/components/status-menu.js index 79928a285601..205448a3bb43 100644 --- a/ui/app/components/status-menu.js +++ b/ui/app/components/status-menu.js @@ -7,14 +7,17 @@ export default Ember.Component.extend({ cluster: computed.alias('currentCluster.cluster'), auth: inject.service(), type: 'cluster', + itemTag: null, partialName: computed('type', function() { - return `partials/status/${this.get('type')}`; + let type = this.get('type'); + let partial = type === 'replication-status' ? 'replication' : type; + return `partials/status/${partial}`; }), glyphName: computed('type', function() { const glyphs = { cluster: 'unlocked', user: 'android-person', - replication: 'replication', + 'replication-status': 'replication', }; return glyphs[this.get('type')]; }), diff --git a/ui/app/components/upgrade-link.js b/ui/app/components/upgrade-link.js index 01bc9ea11d79..f02f5a79f344 100644 --- a/ui/app/components/upgrade-link.js +++ b/ui/app/components/upgrade-link.js @@ -3,6 +3,9 @@ import Ember from 'ember'; const { computed } = Ember; export default Ember.Component.extend({ + modalContainer: computed(function() { + return document.getElementById('modal-wormhole'); + }), isAnimated: false, isActive: false, tagName: 'span', diff --git a/ui/app/helpers/message-types.js b/ui/app/helpers/message-types.js index a42e8f2f1931..ea01ef1bbb3c 100644 --- a/ui/app/helpers/message-types.js +++ b/ui/app/helpers/message-types.js @@ -23,7 +23,7 @@ const MESSAGE_TYPES = { class: 'is-highlight', glyphClass: 'has-text-highlight', glyph: 'alert-circled', - text: 'Attention', + text: 'Warning', }, }; diff --git a/ui/app/styles/components/console-ui-panel.scss b/ui/app/styles/components/console-ui-panel.scss index 7507762e8402..ecc558015a51 100644 --- a/ui/app/styles/components/console-ui-panel.scss +++ b/ui/app/styles/components/console-ui-panel.scss @@ -142,7 +142,7 @@ } } -.page-container > header { +.page-container > header:not(.page-header) { background: linear-gradient(to right, #191a1c, #1b212d); } diff --git a/ui/app/styles/components/hover-copy-button.scss b/ui/app/styles/components/hover-copy-button.scss new file mode 100644 index 000000000000..5d62d334b919 --- /dev/null +++ b/ui/app/styles/components/hover-copy-button.scss @@ -0,0 +1,24 @@ +.has-copy-button { + position: relative; + color: $grey; +} +.hover-copy-button, +.hover-copy-button-static { + position: absolute; + top: 0.5rem; + right: 0.5rem; +} + +.hover-copy-button { + opacity: 0; + pointer-events: none; + transition: opacity $speed ease-in-out; + will-change: opacity; +} + +.has-copy-button:hover .hover-copy-button, +.has-copy-button:focus .hover-copy-button, +.hover-copy-button .copy-button:focus { + opacity: 1; + pointer-events: auto; +} diff --git a/ui/app/styles/components/init-illustration.scss b/ui/app/styles/components/init-illustration.scss index 5cc72e28e926..1f18018b8c1d 100644 --- a/ui/app/styles/components/init-illustration.scss +++ b/ui/app/styles/components/init-illustration.scss @@ -1,5 +1,18 @@ +.init-box { + position: relative; + z-index: 10; +} .init-illustration { + bottom: 0; + right: 0; + overflow: hidden; + position: absolute; + height: 200px; + width: 200px; +} +.init-illustration svg { position: absolute; - left: calc(50% - 100px); - top: -94px; + right: -50px; + bottom: -50px; + opacity: 0.8; } diff --git a/ui/app/styles/components/message-in-page.scss b/ui/app/styles/components/message-in-page.scss index 936e2273e190..e766d8826fdf 100644 --- a/ui/app/styles/components/message-in-page.scss +++ b/ui/app/styles/components/message-in-page.scss @@ -1,5 +1,5 @@ .message-in-page { - margin-bottom: 2rem; + margin-bottom: 1rem; position: relative; .close-button { diff --git a/ui/app/styles/components/popup-menu.scss b/ui/app/styles/components/popup-menu.scss index 3594d830f930..928b94b148b1 100644 --- a/ui/app/styles/components/popup-menu.scss +++ b/ui/app/styles/components/popup-menu.scss @@ -28,7 +28,6 @@ background: transparent; box-shadow: none; border: none; - color: $menu-item-color; display: block; height: auto; font-size: $size-7; @@ -37,7 +36,11 @@ text-align: left; text-decoration: none; width: 100%; + } + button.link, + a { + color: $menu-item-color; &:hover { background-color: $menu-item-hover-background-color; color: $menu-item-hover-color; @@ -79,6 +82,15 @@ } } +.popup-menu-content p { + box-shadow: none; + padding-top: $size-10; + font-weight: $font-weight-semibold; +} + +.popup-menu-content .level-left { + flex-shrink: 1; +} .popup-menu-trigger { height: 2rem; min-width: 0; diff --git a/ui/app/styles/components/radial-progress.scss b/ui/app/styles/components/radial-progress.scss new file mode 100644 index 000000000000..4f17bd6cd215 --- /dev/null +++ b/ui/app/styles/components/radial-progress.scss @@ -0,0 +1,12 @@ +.radial-progress { + transform: rotate(-90deg) translateX(-20%); +} +.radial-progress circle { + stroke: rgba($grey-light, 0.5); + transition: stroke-dashoffset $speed ease-in; + will-change: stroke-dashoffset; + stroke-linecap: round; +} +.radial-progress circle.progress-fill { + stroke: $green; +} diff --git a/ui/app/styles/components/splash-page.scss b/ui/app/styles/components/splash-page.scss new file mode 100644 index 000000000000..a361a6a41a76 --- /dev/null +++ b/ui/app/styles/components/splash-page.scss @@ -0,0 +1,15 @@ +a.splash-page-logo { + color: $white; + svg { + transform: scale(.5); + transform-origin: left; + fill: currentColor; + } +} + +.splash-page-container { + margin: $size-2 0; +} +.splash-page-header { + padding: .75rem 1.5rem; +} diff --git a/ui/app/styles/components/sub-nav.scss b/ui/app/styles/components/sub-nav.scss index 630ac3398ef9..3be16a876a13 100644 --- a/ui/app/styles/components/sub-nav.scss +++ b/ui/app/styles/components/sub-nav.scss @@ -23,8 +23,7 @@ color: $grey-dark; font-weight: $font-weight-semibold; text-decoration: none; - padding: $size-6 0; - margin: 0 $size-4; + padding: $size-6 $size-8 $size-8; border-bottom: 2px solid transparent; transition: border-color $speed; diff --git a/ui/app/styles/components/unseal-warning.scss b/ui/app/styles/components/unseal-warning.scss new file mode 100644 index 000000000000..817a67a372c9 --- /dev/null +++ b/ui/app/styles/components/unseal-warning.scss @@ -0,0 +1,5 @@ +.unseal-warning.message, +.unseal-warning .message-body { + border-radius: 0; + margin-bottom: 0; +} diff --git a/ui/app/styles/core.scss b/ui/app/styles/core.scss index dabbb6525709..0caed8431ab8 100644 --- a/ui/app/styles/core.scss +++ b/ui/app/styles/core.scss @@ -50,6 +50,7 @@ @import "./components/env-banner"; @import "./components/form-section"; @import "./components/global-flash"; +@import "./components/hover-copy-button"; @import "./components/init-illustration"; @import "./components/info-table-row"; @import "./components/input-hint"; @@ -61,12 +62,15 @@ @import "./components/message-in-page"; @import "./components/page-header"; @import "./components/popup-menu"; +@import "./components/radial-progress"; @import "./components/role-item"; @import "./components/shamir-progress"; @import "./components/sidebar"; +@import "./components/splash-page"; @import "./components/status-menu"; @import "./components/sub-nav"; @import "./components/token-expire-warning"; @import "./components/tool-tip"; +@import "./components/unseal-warning"; @import "./components/upgrade-overlay"; @import "./components/vault-loading"; diff --git a/ui/app/styles/core/footer.scss b/ui/app/styles/core/footer.scss index b176a8366630..fc3b45182738 100644 --- a/ui/app/styles/core/footer.scss +++ b/ui/app/styles/core/footer.scss @@ -1,6 +1,7 @@ .footer { border-top: $base-border; padding: $size-3 1.5rem; + margin-top: auto; span:not(:first-child) { display: inline-block; diff --git a/ui/app/templates/application.hbs b/ui/app/templates/application.hbs index 9a0e3abd207a..b00dafb52c97 100644 --- a/ui/app/templates/application.hbs +++ b/ui/app/templates/application.hbs @@ -1,73 +1,73 @@