ui: Tab Improvements (animations/branding) (#7772)

* ui: Adds a tab selection animation to our app tabs

1. Replace all mentions of `magenta` with a themeable CSS property.
2. Add an easy way to inline style DOM nodes
3. Use CSS properties to add tab animation

* Fix up rendering test

* Avoid DOM noodling as much as possible
pull/7344/head
John Cowen 2020-05-05 17:31:18 +01:00 committed by John Cowen
parent 12e2c93e6b
commit 135d22586b
15 changed files with 117 additions and 42 deletions

View File

@ -1,17 +1,21 @@
<nav role="tablist" class="tab-nav">
<nav
style={{if selectedWidth (concat '--selected-width:' selectedWidth ';--selected-left:' selectedLeft ';--selected-height:' selectedHeight ';--selected-top:' selectedTop) undefined}}
role="tablist"
class={{concat 'tab-nav' (if isAnimatable ' animatable' '')}}
id={{guid}}>
<ul>
{{#each items as |item|}}
<li
data-test-tab={{concat name '_' (if item.label (slugify item.label) (slugify item))}}
class={{if (or item.selected (eq selected (if item.label (slugify item.label) (slugify item)))) 'selected'}}
>
<label role="tab" onkeydown={{action 'keydown'}} tabindex="0" aria-controls="radiogroup_{{name}}_{{if item.label (slugify item.label) (slugify item)}}_panel" for="radiogroup_{{name}}_{{if item.label (slugify item.label) (slugify item)}}" data-test-radiobutton="{{name}}_{{if item.label (slugify item.label) (slugify item)}}">
{{#if item.href }}
<a href={{item.href}}>{{item.label}}</a>
{{else}}
<label role="tab" onkeydown={{action 'keydown'}} tabindex="0" aria-controls="radiogroup_{{name}}_{{if item.label (slugify item.label) (slugify item)}}_panel" for="radiogroup_{{name}}_{{if item.label (slugify item.label) (slugify item)}}" data-test-radiobutton="{{name}}_{{if item.label (slugify item.label) (slugify item)}}">
<a>{{item}}</a>
{{/if}}
</label>
{{/if}}
</li>
{{/each}}
</ul>

View File

@ -1,9 +1,39 @@
import Component from '@ember/component';
import { setProperties, set } from '@ember/object';
import { inject as service } from '@ember/service';
import { schedule } from '@ember/runloop';
const ENTER = 13;
const SELECTED = 'li.selected';
export default Component.extend({
name: 'tab',
tagName: '',
dom: service('dom'),
isAnimatable: false,
init: function() {
this._super(...arguments);
this.guid = this.dom.guid(this);
},
didInsertElement: function() {
this._super(...arguments);
this.$nav = this.dom.element(`#${this.guid}`);
this.select(this.dom.element(SELECTED, this.$nav));
set(this, 'isAnimatable', true);
},
didUpdateAttrs: function() {
schedule('afterRender', () => this.select(this.dom.element(SELECTED, this.$nav)));
},
select: function($el) {
if (!$el) {
return;
}
setProperties(this, {
selectedWidth: $el.offsetWidth,
selectedLeft: $el.offsetLeft,
selectedHeight: $el.offsetHeight,
selectedTop: $el.offsetTop,
});
},
actions: {
keydown: function(e) {
if (e.keyCode === ENTER) {

View File

@ -1,3 +1,7 @@
%with-transition-500 {
transition-duration: 0.15s;
transition-timing-function: ease-out;
}
%blink-in-fade-out {
transition-property: opacity;
transition-duration: 0.1s;

View File

@ -6,6 +6,12 @@
/* same as decor-border-000 - but need to think about being able to import color on its own*/
border-style: solid;
}
%frame-brand-300 {
@extend %frame-border-000;
background-color: $transparent;
border-color: var(--decor-brand-600, inherit);
color: var(--typo-brand-600, inherit);
}
/* possibly filter bar */
/* modal close button */

View File

@ -40,6 +40,5 @@
}
%form-element-note > code {
background-color: $gray-200;
/* consul color but its a code looking style?*/
color: $magenta-600;
color: var(--typo-brand-600, inherit);
}

View File

@ -31,5 +31,5 @@
}
%menu-panel .is-active > *::after {
@extend %with-check-plain-mask, %as-pseudo;
background-color: $magenta-600;
background-color: var(--swatch-brand-600, $black);
}

View File

@ -1,5 +1,9 @@
@import './skin';
@import './layout';
%with-animated-tab-selection ul::after,
%tab-button-selected {
@extend %frame-brand-300;
}
%tab-nav li a {
@extend %tab-button;
}

View File

@ -2,19 +2,25 @@
clear: both;
}
%tab-nav ul {
padding: 0;
margin: 0;
display: inline-flex;
align-items: center;
position: relative;
padding: 0;
margin: 0;
}
%tab-button {
padding-left: 16px;
padding-right: 16px;
%with-animated-tab-selection ul::after {
@extend %as-pseudo, %with-transition-500;
position: absolute;
bottom: 0;
height: 0;
border-top: 0;
width: calc(var(--selected-width, 0) * 1px);
transform: translate(calc(var(--selected-left, 0) * 1px), 0);
transition-property: transform, width;
}
%tab-button {
display: inline-block;
padding-top: 13px;
padding-bottom: 13px;
padding: 16px 13px;
}
%tab-section section h3 {
margin: 24px 0;

View File

@ -1,13 +1,3 @@
%tab-nav {
/* %frame-gray-something */
border-bottom: $decor-border-100;
/* TODO: structure tabs don't actually have a top border */
border-top: $decor-border-200;
}
%tab-nav {
/* %frame-gray-something */
border-color: $gray-200;
}
%tab-nav ul {
list-style-type: none;
}
@ -18,16 +8,29 @@
white-space: nowrap;
text-decoration: none;
}
%tab-nav {
/* %frame-transparent-something */
border-bottom: $decor-border-100;
}
%tab-nav {
/* %frame-transparent-something */
border-color: $gray-200;
}
%with-animated-tab-selection ul::after,
%tab-button {
border-bottom: $decor-border-300;
}
%tab-button {
border-color: $color-transparent;
@extend %with-transition-500;
transition-property: background-color;
border-color: $transparent;
color: $gray-500;
}
%tab-button-intent,
%tab-button-active {
/* %frame-gray-something */
border-color: $color-transparent;
background-color: $gray-100;
}
%tab-nav.animatable .selected a {
border-color: $transparent !important;
}

View File

@ -12,19 +12,19 @@
@extend %with-logo-github-monochrome-icon, %as-pseudo;
}
%main-header-horizontal::before {
background-color: $magenta-600;
background-color: var(--swatch-brand-600, $black);
}
%main-nav-horizontal-action,
%main-nav-horizontal-toggle-button {
color: $magenta-050;
color: var(--typo-brand-050, $black);
}
@media #{$--lt-horizontal-nav} {
%main-nav-horizontal-panel {
background-color: $magenta-600;
background-color: var(---swatch-brand-600, $black);
}
}
@media #{$--horizontal-nav} {
%main-nav-horizontal-action-active {
background-color: $magenta-800;
background-color: var(--swatch-brand-800, $black);
}
}

View File

@ -2,11 +2,11 @@
.tab-nav {
@extend %tab-nav;
}
%tab-nav.animatable {
@extend %with-animated-tab-selection;
}
.tab-section {
@extend %tab-section;
/* this keeps in-tab-section toolbars flush to the top, see Node Detail > Services */
margin-top: 0 !important;
}
%tab-button-selected {
@extend %frame-magenta-300;
}

View File

@ -1,6 +1,6 @@
td a.is-management::after {
@extend %with-star-fill-mask, %as-pseudo;
background-color: $magenta-600;
background-color: var(--swatch-brand-600, $black);
height: 16px;
top: 2px;
padding-left: 32px;

View File

@ -9,4 +9,23 @@
--decor-radius-200: #{$decor-radius-200};
--gray-500: #{$gray-500};
--decor-elevation-600: #{$decor-elevation-600};
/* base brand colors */
--brand-050: #{$magenta-050};
// --brand-100: #{$magenta-100};
// --brand-200: #{$magenta-200};
// --brand-300: #{$magenta-300};
// --brand-400: #{$magenta-400};
// --brand-500: #{$magenta-500};
--brand-600: #{$magenta-600};
// --brand-700: #{$magenta-700};
--brand-800: #{$magenta-800};
// --brand-900: #{$magenta-900};
/* themeable brand colors */
--typo-brand-050: var(--brand-050);
--typo-brand-600: var(--brand-600);
--decor-brand-600: var(--brand-600);
--swatch-brand-600: var(--brand-600);
--swatch-brand-800: var(--brand-800);
}

View File

@ -10,15 +10,15 @@ module('Integration | Component | tab nav', function(hooks) {
// Set any properties with this.set('myProperty', 'value');
// Handle any actions with this.on('myAction', function(val) { ... });
await render(hbs`{{tab-nav}}`);
assert.dom('*').hasText('');
// Template block usage:
await render(hbs`
{{#tab-nav}}{{/tab-nav}}
<TabNav @items={{array
(hash
label="Tab Label"
href="/"
)
}} />
`);
assert.dom('*').hasText('');
assert.dom('*').hasText('Tab Label');
});
});

View File

@ -17,7 +17,7 @@ export default function(name, items, blankKey = 'all') {
...prev,
...{
[`${key}IsSelected`]: is('.selected', `[data-test-tab="${name}_${item}"]`),
[key]: clickable(`[data-test-tab="${name}_${item}"] > label > a`),
[key]: clickable(`[data-test-tab="${name}_${item}"] a`),
},
};
}, {});