Merge pull request #4468 from hashicorp/f-ui-styleguide-progress

UI: Fill out the styleguide
This commit is contained in:
Michael Lange
2018-07-06 16:03:41 -07:00
committed by GitHub
37 changed files with 2208 additions and 11 deletions

View File

@@ -0,0 +1,6 @@
import Component from '@ember/component';
import productMetadata from 'nomad-ui/utils/styleguide/product-metadata';
export default Component.extend({
products: productMetadata,
});

View File

@@ -0,0 +1,355 @@
import Component from '@ember/component';
const generateDiff = changeset => ({
Fields: null,
ID: 'insertions-only',
Objects: null,
TaskGroups: [
{
Fields: [{ Annotations: null, Name: 'Count', New: '2', Old: '2', Type: 'None' }],
Name: 'cache',
Objects: [
{
Fields: changeset,
Name: 'RestartPolicy',
Objects: null,
Type: 'Edited',
},
],
Type: 'Edited',
Updates: null,
},
],
Type: 'Edited',
});
export default Component.extend({
insertionsOnly: generateDiff([
{ Annotations: null, Name: 'Attempts', New: '15', Old: '15', Type: 'None' },
{ Annotations: null, Name: 'Delay', New: '25000000000', Old: '', Type: 'Added' },
{ Annotations: null, Name: 'Interval', New: '900000000000', Old: '', Type: 'Added' },
{ Annotations: null, Name: 'Mode', New: 'delay', Old: 'delay', Type: 'None' },
]),
deletionsOnly: generateDiff([
{ Annotations: null, Name: 'Attempts', New: '15', Old: '15', Type: 'None' },
{
Annotations: null,
Name: 'Delay',
New: '25000000000',
Old: '25000000000',
Type: 'None',
},
{
Annotations: null,
Name: 'Interval',
New: '900000000000',
Old: '900000000000',
Type: 'None',
},
{ Annotations: null, Name: 'Mode', New: '', Old: 'delay', Type: 'Deleted' },
]),
editsOnly: generateDiff([
{ Annotations: null, Name: 'Attempts', New: '15', Old: '15', Type: 'None' },
{
Annotations: null,
Name: 'Delay',
New: '25000000000',
Old: '25000000000',
Type: 'None',
},
{
Annotations: null,
Name: 'Interval',
New: '900000000000',
Old: '250000000000',
Type: 'Edited',
},
{ Annotations: null, Name: 'Mode', New: 'delay', Old: 'delay', Type: 'None' },
]),
largeDiff: {
Fields: null,
ID: 'example',
Objects: null,
TaskGroups: [
{
Fields: null,
Name: 'cache',
Objects: null,
Tasks: [
{
Annotations: null,
Fields: [
{
Annotations: null,
Name: 'Meta[one]',
New: "flew over the cuckoo's nest",
Old: '',
Type: 'Added',
},
{
Annotations: null,
Name: 'Meta[two]',
New: 'birds on a wire',
Old: '',
Type: 'Added',
},
],
Name: 'redis',
Objects: [
{
Fields: [
{
Annotations: null,
Name: 'image',
New: 'redis:3.4',
Old: 'redis:3.2',
Type: 'Edited',
},
{
Annotations: null,
Name: 'port_map[0][db]',
New: '6380',
Old: '6379',
Type: 'Edited',
},
],
Name: 'Config',
Objects: null,
Type: 'Edited',
},
{
Fields: [
{ Annotations: null, Name: 'CPU', New: '1000', Old: '500', Type: 'Edited' },
{ Annotations: null, Name: 'DiskMB', New: '0', Old: '0', Type: 'None' },
{ Annotations: null, Name: 'IOPS', New: '0', Old: '0', Type: 'None' },
{ Annotations: null, Name: 'MemoryMB', New: '512', Old: '256', Type: 'Edited' },
],
Name: 'Resources',
Objects: [
{
Fields: [
{ Annotations: null, Name: 'MBits', New: '100', Old: '', Type: 'Added' },
],
Name: 'Network',
Objects: [
{
Fields: [
{ Annotations: null, Name: 'Label', New: 'db', Old: '', Type: 'Added' },
],
Name: 'Dynamic Port',
Objects: null,
Type: 'Added',
},
],
Type: 'Added',
},
{
Fields: [
{ Annotations: null, Name: 'MBits', New: '', Old: '10', Type: 'Deleted' },
],
Name: 'Network',
Objects: [
{
Fields: [
{ Annotations: null, Name: 'Label', New: '', Old: 'db', Type: 'Deleted' },
],
Name: 'Dynamic Port',
Objects: null,
Type: 'Deleted',
},
],
Type: 'Deleted',
},
],
Type: 'Edited',
},
{
Fields: [
{
Annotations: null,
Name: 'AddressMode',
New: 'auto',
Old: 'auto',
Type: 'None',
},
{
Annotations: null,
Name: 'Name',
New: 'redis-cache',
Old: 'redis-cache',
Type: 'None',
},
{ Annotations: null, Name: 'PortLabel', New: 'db', Old: 'db', Type: 'None' },
],
Name: 'Service',
Objects: [
{
Fields: [
{ Annotations: null, Name: 'Tags', New: 'redis', Old: '', Type: 'Added' },
{ Annotations: null, Name: 'Tags', New: 'cache', Old: 'cache', Type: 'None' },
{
Annotations: null,
Name: 'Tags',
New: 'global',
Old: 'global',
Type: 'None',
},
],
Name: 'Tags',
Objects: null,
Type: 'Added',
},
{
Fields: [
{ Annotations: null, Name: 'AddressMode', New: '', Old: '', Type: 'None' },
{ Annotations: null, Name: 'Command', New: '', Old: '', Type: 'None' },
{ Annotations: null, Name: 'GRPCService', New: '', Old: '', Type: 'None' },
{
Annotations: null,
Name: 'GRPCUseTLS',
New: 'false',
Old: 'false',
Type: 'None',
},
{ Annotations: null, Name: 'InitialStatus', New: '', Old: '', Type: 'None' },
{
Annotations: null,
Name: 'Interval',
New: '15000000000',
Old: '10000000000',
Type: 'Edited',
},
{ Annotations: null, Name: 'Method', New: '', Old: '', Type: 'None' },
{ Annotations: null, Name: 'Name', New: 'alive', Old: 'alive', Type: 'None' },
{ Annotations: null, Name: 'Path', New: '', Old: '', Type: 'None' },
{ Annotations: null, Name: 'PortLabel', New: '', Old: '', Type: 'None' },
{ Annotations: null, Name: 'Protocol', New: '', Old: '', Type: 'None' },
{
Annotations: null,
Name: 'TLSSkipVerify',
New: 'false',
Old: 'false',
Type: 'None',
},
{
Annotations: null,
Name: 'Timeout',
New: '7000000000',
Old: '2000000000',
Type: 'Edited',
},
{ Annotations: null, Name: 'Type', New: 'tcp', Old: 'tcp', Type: 'None' },
],
Name: 'Check',
Objects: null,
Type: 'Edited',
},
],
Type: 'Edited',
},
],
Type: 'Edited',
},
],
Type: 'Edited',
Updates: null,
},
{
Fields: [
{ Annotations: null, Name: 'Count', New: '1', Old: '', Type: 'Added' },
{ Annotations: null, Name: 'Meta[key]', New: 'value', Old: '', Type: 'Added' },
{ Annotations: null, Name: 'Meta[red]', New: 'fish', Old: '', Type: 'Added' },
],
Name: 'cache2',
Objects: [
{
Fields: [
{ Annotations: null, Name: 'Attempts', New: '2', Old: '', Type: 'Added' },
{ Annotations: null, Name: 'Delay', New: '15000000000', Old: '', Type: 'Added' },
{ Annotations: null, Name: 'Interval', New: '1800000000000', Old: '', Type: 'Added' },
{ Annotations: null, Name: 'Mode', New: 'fail', Old: '', Type: 'Added' },
],
Name: 'RestartPolicy',
Objects: null,
Type: 'Added',
},
{
Fields: [
{ Annotations: null, Name: 'Migrate', New: 'false', Old: '', Type: 'Added' },
{ Annotations: null, Name: 'SizeMB', New: '300', Old: '', Type: 'Added' },
{ Annotations: null, Name: 'Sticky', New: 'false', Old: '', Type: 'Added' },
],
Name: 'EphemeralDisk',
Objects: null,
Type: 'Added',
},
],
Tasks: [
{
Annotations: null,
Fields: [
{ Annotations: null, Name: 'Driver', New: 'docker', Old: '', Type: 'Added' },
{ Annotations: null, Name: 'KillTimeout', New: '5000000000', Old: '', Type: 'Added' },
{ Annotations: null, Name: 'Leader', New: 'false', Old: '', Type: 'Added' },
{ Annotations: null, Name: 'ShutdownDelay', New: '0', Old: '', Type: 'Added' },
],
Name: 'redis',
Objects: [
{
Fields: [
{ Annotations: null, Name: 'image', New: 'redis:3.2', Old: '', Type: 'Added' },
{
Annotations: null,
Name: 'port_map[0][db]',
New: '6379',
Old: '',
Type: 'Added',
},
],
Name: 'Config',
Objects: null,
Type: 'Added',
},
{
Fields: [
{ Annotations: null, Name: 'CPU', New: '500', Old: '', Type: 'Added' },
{ Annotations: null, Name: 'DiskMB', New: '0', Old: '', Type: 'Added' },
{ Annotations: null, Name: 'IOPS', New: '0', Old: '', Type: 'Added' },
{ Annotations: null, Name: 'MemoryMB', New: '256', Old: '', Type: 'Added' },
],
Name: 'Resources',
Objects: [
{
Fields: [
{ Annotations: null, Name: 'MBits', New: '10', Old: '', Type: 'Added' },
],
Name: 'Network',
Objects: [
{
Fields: [
{ Annotations: null, Name: 'Label', New: 'db', Old: '', Type: 'Added' },
],
Name: 'Dynamic Port',
Objects: null,
Type: 'Added',
},
],
Type: 'Added',
},
],
Type: 'Added',
},
],
Type: 'Added',
},
],
Type: 'Added',
Updates: null,
},
],
Type: 'Edited',
},
});

View File

@@ -0,0 +1,30 @@
import Component from '@ember/component';
export default Component.extend({
options: [
{ name: 'Consul' },
{ name: 'Nomad' },
{ name: 'Packer' },
{ name: 'Terraform' },
{ name: 'Vagrant' },
{ name: 'Vault' },
],
manyOptions: [
'One',
'Two',
'Three',
'Four',
'Five',
'Six',
'Seven',
'Eight',
'Nine',
'Ten',
'Eleven',
'Twelve',
'Thirteen',
'Fourteen',
'Fifteen',
].map(name => ({ name })),
});

View File

@@ -0,0 +1,148 @@
import Component from '@ember/component';
export default Component.extend({
jsonSmall: {
foo: 'bar',
number: 123456789,
products: ['Consul', 'Nomad', 'Packer', 'Terraform', 'Vagrant', 'Vault'],
currentTime: new Date().toISOString(),
nested: {
obj: 'ject',
},
nonexistent: null,
huh: undefined,
isTrue: false,
},
jsonLarge: {
Stop: false,
Region: 'global',
Namespace: 'default',
ID: 'syslog',
ParentID: '',
Name: 'syslog',
Type: 'system',
Priority: 50,
AllAtOnce: false,
Datacenters: ['dc1', 'dc2'],
Constraints: null,
TaskGroups: [
{
Name: 'syslog',
Count: 1,
Update: {
Stagger: 10000000000,
MaxParallel: 1,
HealthCheck: 'checks',
MinHealthyTime: 10000000000,
HealthyDeadline: 300000000000,
ProgressDeadline: 600000000000,
AutoRevert: false,
Canary: 0,
},
Migrate: null,
Constraints: [
{
LTarget: '',
RTarget: '',
Operand: 'distinct_hosts',
},
],
RestartPolicy: {
Attempts: 10,
Interval: 300000000000,
Delay: 25000000000,
Mode: 'delay',
},
Tasks: [
{
Name: 'syslog',
Driver: 'docker',
User: '',
Config: {
port_map: [
{
tcp: 601.0,
udp: 514.0,
},
],
image: 'balabit/syslog-ng:latest',
},
Env: null,
Services: null,
Vault: null,
Templates: null,
Constraints: null,
Resources: {
CPU: 500,
MemoryMB: 256,
DiskMB: 0,
IOPS: 0,
Networks: [
{
Device: '',
CIDR: '',
IP: '',
MBits: 10,
ReservedPorts: [
{
Label: 'udp',
Value: 514,
},
{
Label: 'tcp',
Value: 601,
},
],
DynamicPorts: null,
},
],
},
DispatchPayload: null,
Meta: null,
KillTimeout: 5000000000,
LogConfig: {
MaxFiles: 10,
MaxFileSizeMB: 10,
},
Artifacts: null,
Leader: false,
ShutdownDelay: 0,
KillSignal: '',
},
],
EphemeralDisk: {
Sticky: false,
SizeMB: 300,
Migrate: false,
},
Meta: null,
ReschedulePolicy: null,
},
],
Update: {
Stagger: 10000000000,
MaxParallel: 1,
HealthCheck: '',
MinHealthyTime: 0,
HealthyDeadline: 0,
ProgressDeadline: 0,
AutoRevert: false,
Canary: 0,
},
Periodic: null,
ParameterizedJob: null,
Dispatched: false,
Payload: null,
Meta: null,
VaultToken: '',
Status: 'running',
StatusDescription: '',
Stable: false,
Version: 0,
SubmitTime: 1530052201331477665,
CreateIndex: 27,
ModifyIndex: 27,
JobModifyIndex: 27,
},
});

View File

@@ -0,0 +1,33 @@
import Component from '@ember/component';
export default Component.extend({
mode1: 'stdout',
isPlaying1: true,
sampleOutput: `Sample output
> 1
> 2
> 3
[00:12:58] Log output here
[00:15:29] [ERR] Uh oh
Loading.
Loading..
Loading...
>> Done! <<
`,
sampleError: `Sample error
[====|--------------------] 20%
!!! Unrecoverable error:
Cannot continue beyond this point. Exception should be caught.
This is not a mistake. You did something wrong. Check the code.
No, you will not receive any more details or guidance from this
error message.
`,
});

View File

@@ -0,0 +1,35 @@
import Component from '@ember/component';
import { computed } from '@ember/object';
export default Component.extend({
timerTicks: 0,
startTimer: function() {
this.set(
'timer',
setInterval(() => {
this.incrementProperty('timerTicks');
}, 1000)
);
}.on('init'),
willDestroy() {
clearInterval(this.get('timer'));
},
denominator: computed('timerTicks', function() {
return Math.round(Math.random() * 1000);
}),
percentage: computed('timerTicks', function() {
return Math.round(Math.random() * 100) / 100;
}),
numerator: computed('denominator', 'percentage', function() {
return Math.round(this.get('denominator') * this.get('percentage') * 100) / 100;
}),
liveDetails: computed('denominator', 'numerator', 'percentage', function() {
return this.getProperties('denominator', 'numerator', 'percentage');
}),
});

View File

@@ -0,0 +1,24 @@
import Component from '@ember/component';
export default Component.extend({
attributes: {
key: 'val',
deep: {
key: 'val',
more: 'stuff',
},
array: ['one', 'two', 'three', 'four'],
very: {
deep: {
key: {
incoming: {
one: 1,
two: 2,
three: 3,
four: 'surprisingly long value that is unlike the other properties in this object',
},
},
},
},
},
});

View File

@@ -0,0 +1,118 @@
import Component from '@ember/component';
import { computed } from '@ember/object';
import productMetadata from 'nomad-ui/utils/styleguide/product-metadata';
export default Component.extend({
searchTerm: '',
currentPage: 1,
sortProperty: 'name',
sortDescending: false,
shortList: productMetadata,
longList: [
{ city: 'New York', growth: 0.048, population: '8405837', rank: '1', state: 'New York' },
{ city: 'Los Angeles', growth: 0.048, population: '3884307', rank: '2', state: 'California' },
{ city: 'Chicago', growth: -0.061, population: '2718782', rank: '3', state: 'Illinois' },
{ city: 'Houston', growth: 0.11, population: '2195914', rank: '4', state: 'Texas' },
{
city: 'Philadelphia',
growth: 0.026,
population: '1553165',
rank: '5',
state: 'Pennsylvania',
},
{ city: 'Phoenix', growth: 0.14, population: '1513367', rank: '6', state: 'Arizona' },
{ city: 'San Antonio', growth: 0.21, population: '1409019', rank: '7', state: 'Texas' },
{ city: 'San Diego', growth: 0.105, population: '1355896', rank: '8', state: 'California' },
{ city: 'Dallas', growth: 0.056, population: '1257676', rank: '9', state: 'Texas' },
{ city: 'San Jose', growth: 0.105, population: '998537', rank: '10', state: 'California' },
{ city: 'Austin', growth: 0.317, population: '885400', rank: '11', state: 'Texas' },
{ city: 'Indianapolis', growth: 0.078, population: '843393', rank: '12', state: 'Indiana' },
{ city: 'Jacksonville', growth: 0.143, population: '842583', rank: '13', state: 'Florida' },
{
city: 'San Francisco',
growth: 0.077,
population: '837442',
rank: '14',
state: 'California',
},
{ city: 'Columbus', growth: 0.148, population: '822553', rank: '15', state: 'Ohio' },
{
city: 'Charlotte',
growth: 0.391,
population: '792862',
rank: '16',
state: 'North Carolina',
},
{ city: 'Fort Worth', growth: 0.451, population: '792727', rank: '17', state: 'Texas' },
{ city: 'Detroit', growth: -0.271, population: '688701', rank: '18', state: 'Michigan' },
{ city: 'El Paso', growth: 0.194, population: '674433', rank: '19', state: 'Texas' },
{ city: 'Memphis', growth: -0.053, population: '653450', rank: '20', state: 'Tennessee' },
{ city: 'Seattle', growth: 0.156, population: '652405', rank: '21', state: 'Washington' },
{ city: 'Denver', growth: 0.167, population: '649495', rank: '22', state: 'Colorado' },
{
city: 'Washington',
growth: 0.13,
population: '646449',
rank: '23',
state: 'District of Columbia',
},
{ city: 'Boston', growth: 0.094, population: '645966', rank: '24', state: 'Massachusetts' },
{
city: 'Nashville-Davidson',
growth: 0.162,
population: '634464',
rank: '25',
state: 'Tennessee',
},
{ city: 'Baltimore', growth: -0.04, population: '622104', rank: '26', state: 'Maryland' },
{ city: 'Oklahoma City', growth: 0.202, population: '610613', rank: '27', state: 'Oklahoma' },
{
city: 'Louisville/Jefferson County',
growth: 0.1,
population: '609893',
rank: '28',
state: 'Kentucky',
},
{ city: 'Portland', growth: 0.15, population: '609456', rank: '29', state: 'Oregon' },
{ city: 'Las Vegas', growth: 0.245, population: '603488', rank: '30', state: 'Nevada' },
{ city: 'Milwaukee', growth: 0.003, population: '599164', rank: '31', state: 'Wisconsin' },
{ city: 'Albuquerque', growth: 0.235, population: '556495', rank: '32', state: 'New Mexico' },
{ city: 'Tucson', growth: 0.075, population: '526116', rank: '33', state: 'Arizona' },
{ city: 'Fresno', growth: 0.183, population: '509924', rank: '34', state: 'California' },
{ city: 'Sacramento', growth: 0.172, population: '479686', rank: '35', state: 'California' },
{ city: 'Long Beach', growth: 0.015, population: '469428', rank: '36', state: 'California' },
{ city: 'Kansas City', growth: 0.055, population: '467007', rank: '37', state: 'Missouri' },
{ city: 'Mesa', growth: 0.135, population: '457587', rank: '38', state: 'Arizona' },
{ city: 'Virginia Beach', growth: 0.051, population: '448479', rank: '39', state: 'Virginia' },
{ city: 'Atlanta', growth: 0.062, population: '447841', rank: '40', state: 'Georgia' },
{
city: 'Colorado Springs',
growth: 0.214,
population: '439886',
rank: '41',
state: 'Colorado',
},
{ city: 'Omaha', growth: 0.059, population: '434353', rank: '42', state: 'Nebraska' },
{ city: 'Raleigh', growth: 0.487, population: '431746', rank: '43', state: 'North Carolina' },
{ city: 'Miami', growth: 0.149, population: '417650', rank: '44', state: 'Florida' },
{ city: 'Oakland', growth: 0.013, population: '406253', rank: '45', state: 'California' },
{ city: 'Minneapolis', growth: 0.045, population: '400070', rank: '46', state: 'Minnesota' },
{ city: 'Tulsa', growth: 0.013, population: '398121', rank: '47', state: 'Oklahoma' },
{ city: 'Cleveland', growth: -0.181, population: '390113', rank: '48', state: 'Ohio' },
{ city: 'Wichita', growth: 0.097, population: '386552', rank: '49', state: 'Kansas' },
{ city: 'Arlington', growth: 0.133, population: '379577', rank: '50', state: 'Texas' },
],
filteredShortList: computed('searchTerm', 'shortList.[]', function() {
const term = this.get('searchTerm').toLowerCase();
return this.get('shortList').filter(product => product.name.toLowerCase().includes(term));
}),
sortedShortList: computed('shortList.[]', 'sortProperty', 'sortDescending', function() {
const sorted = this.get('shortList').sortBy(this.get('sortProperty'));
return this.get('sortDescending') ? sorted.reverse() : sorted;
}),
});

View File

@@ -0,0 +1,8 @@
import Component from '@ember/component';
import { computed } from '@ember/object';
import moment from 'moment';
export default Component.extend({
yesterday: computed(() => moment().subtract(1, 'd')),
today: computed(() => moment()),
});

View File

@@ -2,5 +2,15 @@ import { inject as service } from '@ember/service';
import FreestyleController from 'ember-freestyle/controllers/freestyle';
export default FreestyleController.extend({
queryParams: {
currentPage: 'page',
sortProperty: 'sort',
sortDescending: 'desc',
},
currentPage: 1,
sortProperty: 'name',
sortDescending: true,
emberFreestyle: service(),
});

View File

@@ -1,12 +1,16 @@
.two-step-button {
display: inline;
display: inline-block;
vertical-align: middle;
position: relative;
font-size: $body-size;
line-height: 1;
.confirmation-text {
position: absolute;
left: 0;
top: -1.2em;
top: -1.5em;
font-size: $body-size;
line-height: 1;
font-weight: $weight-normal;
color: darken($grey-blue, 20%);
white-space: nowrap;

View File

@@ -13,6 +13,15 @@
background: transparent;
box-shadow: inset -3px 0 0 $blue;
color: $blue;
.icon {
fill: $blue;
}
}
.icon {
margin-right: 0.5em;
fill: lighten($text, 30%);
}
}

View File

@@ -1,11 +1,12 @@
#styleguide {
.mock-content {
display: flex;
height: 250px;
min-height: 250px;
height: 100%;
.mock-image,
.mock-copy {
height: 100%;
min-height: 100%;
width: 100%;
margin: 1em;
}
@@ -40,5 +41,15 @@
transparent 14px
);
}
.mock-vague {
background: lighten($grey-blue, 15%);
width: 100%;
height: 100%;
}
}
.mock-spacing {
padding: 2em;
}
}

View File

@@ -0,0 +1,53 @@
{{#freestyle-usage "accordion" title="Accordion"}}
{{#list-accordion source=products key="name" as |a|}}
{{#a.head buttonLabel="details"}}
<div class="columns inline-definitions">
<div class="column is-1">{{a.item.name}}</div>
<div class="column is-1">
<span class="bumper-left badge is-light">{{a.item.lang}}</span>
</div>
</div>
{{/a.head}}
{{#a.body}}
<h1 class="title is-4">{{a.item.name}}</h1>
<p>{{a.item.desc}}</p>
<p><a href="{{a.item.link}}">Learn more...</a></p>
{{/a.body}}
{{/list-accordion}}
{{/freestyle-usage}}
{{#freestyle-usage "accordion-single" title="Accordion, One Item"}}
{{#list-accordion source=(take 1 products) key="name" as |a|}}
{{#a.head buttonLabel="details"}}
<div class="columns inline-definitions">
<div class="column is-1">{{a.item.name}}</div>
<div class="column is-1">
<span class="bumper-left badge is-light">{{a.item.lang}}</span>
</div>
</div>
{{/a.head}}
{{#a.body}}
<h1 class="title is-4">{{a.item.name}}</h1>
<p>{{a.item.desc}}</p>
<p><a href="{{a.item.link}}">Learn more...</a></p>
{{/a.body}}
{{/list-accordion}}
{{/freestyle-usage}}
{{#freestyle-usage "accordion-not-expandable" title="Accordion, Not Expandable"}}
{{#list-accordion source=products key="name" as |a|}}
{{#a.head buttonLabel="details" isExpandable=(eq a.item.lang "golang")}}
<div class="columns inline-definitions">
<div class="column is-1">{{a.item.name}}</div>
<div class="column is-1">
<span class="bumper-left badge is-light">{{a.item.lang}}</span>
</div>
</div>
{{/a.head}}
{{#a.body}}
<h1 class="title is-4">{{a.item.name}}</h1>
<p>{{a.item.desc}}</p>
<p><a href="{{a.item.link}}">Learn more...</a></p>
{{/a.body}}
{{/list-accordion}}
{{/freestyle-usage}}

View File

@@ -0,0 +1,84 @@
{{#freestyle-usage "alert-standard" title="Alert"}}
<div class="notification is-info">
<h3 class="title is-4">This is an alert</h3>
<p>Alerts are used for both situational and reactionary information.</p>
</div>
{{/freestyle-usage}}
{{#freestyle-annotation}}
<p>Alerts use Bulma's notification component.</p>
{{/freestyle-annotation}}
{{#freestyle-usage "alert-colors" title="Alert colors"}}
<div class="notification is-info">
<h3 class="title is-4">This is an alert</h3>
<p>Alerts are used for both situational and reactionary information.</p>
</div>
<div class="notification is-success">
<h3 class="title is-4">This is an alert</h3>
<p>Alerts are used for both situational and reactionary information.</p>
</div>
<div class="notification is-warning">
<h3 class="title is-4">This is an alert</h3>
<p>Alerts are used for both situational and reactionary information.</p>
</div>
<div class="notification is-danger">
<h3 class="title is-4">This is an alert</h3>
<p>Alerts are used for both situational and reactionary information.</p>
</div>
{{/freestyle-usage}}
{{#freestyle-annotation}}
<p>Alerts are always paired with an emotive color. If there is no emotive association with the content of the alert, then an alert is the wrong component to use.</p>
{{/freestyle-annotation}}
{{#freestyle-usage "alert-dismissal" title="Alert dismissal"}}
<div class="notification is-info">
<div class="columns">
<div class="column">
<h3 class="title is-4">This is an alert</h3>
<p>Alerts are used for both situational and reactionary information.</p>
</div>
<div class="column is-centered is-minimum">
<button class="button is-info">Okay</button>
</div>
</div>
</div>
<div class="notification is-success">
<div class="columns">
<div class="column">
<h3 class="title is-4">This is an alert</h3>
<p>Alerts are used for both situational and reactionary information.</p>
</div>
<div class="column is-centered is-minimum">
<button class="button is-success">Okay</button>
</div>
</div>
</div>
<div class="notification is-warning">
<div class="columns">
<div class="column">
<h3 class="title is-4">This is an alert</h3>
<p>Alerts are used for both situational and reactionary information.</p>
</div>
<div class="column is-centered is-minimum">
<button class="button is-warning">Okay</button>
</div>
</div>
</div>
<div class="notification is-danger">
<div class="columns">
<div class="column">
<h3 class="title is-4">This is an alert</h3>
<p>Alerts are used for both situational and reactionary information.</p>
</div>
<div class="column is-centered is-minimum">
<button class="button is-danger">Okay</button>
</div>
</div>
</div>
{{/freestyle-usage}}

View File

@@ -0,0 +1,33 @@
{{#freestyle-usage "diff-viewer-insertions" title="Diff Viewer with Insertions"}}
<div class="boxed-section">
<div class="boxed-section-body is-dark">
{{job-diff diff=insertionsOnly}}
</div>
</div>
{{/freestyle-usage}}
{{#freestyle-usage "diff-viewer-deletion" title="Diff Viewer with Deletions"}}
<div class="boxed-section">
<div class="boxed-section-body is-dark">
{{job-diff diff=deletionsOnly}}
</div>
</div>
{{/freestyle-usage}}
{{#freestyle-usage "diff-viewer-insertions-verbose" title="Diff Viewer with Edits"}}
<div class="boxed-section">
<div class="boxed-section-body is-dark">
{{job-diff diff=editsOnly}}
</div>
</div>
{{/freestyle-usage}}
{{#freestyle-annotation}}
Often times a diff will only have a couple lines. Minor tweaks to a job spec result in small diffs.
{{/freestyle-annotation}}
{{#freestyle-usage "diff-viewer-large" title="Diff Viewer with Many Changes"}}
<div class="boxed-section">
<div class="boxed-section-body is-dark">
{{job-diff diff=largeDiff}}
</div>
</div>
{{/freestyle-usage}}

View File

@@ -0,0 +1,55 @@
{{#freestyle-usage "dropdown" title="Simple Dropdown"}}
{{#power-select
options=options
selected=selectedOption
searchField="name"
searchEnabled=(gt options.length 10)
onchange=(action (mut selectedOption))
as |option|}}
{{option.name}}
{{/power-select}}
{{/freestyle-usage}}
{{#freestyle-annotation}}
Power Select currently fulfills all of Nomad's dropdown needs out of the box.
{{/freestyle-annotation}}
{{#freestyle-usage "dropdown-sized" title="Dropdown Resized"}}
<div class="columns">
<div class="column is-3">
{{#power-select
options=options
selected=selectedOption2
searchField="name"
searchEnabled=(gt options.length 10)
onchange=(action (mut selectedOption2))
as |option|}}
{{option.name}}
{{/power-select}}
</div>
</div>
{{/freestyle-usage}}
{{#freestyle-annotation}}
Dropdowns are always 100% wide. To control the width of a dropdown, adjust the dimensions of its container. One way to achieve this is using columns.
{{/freestyle-annotation}}
{{#freestyle-usage "dropdown-search" title="Dropdown with Search"}}
<div class="columns">
<div class="column is-3">
{{#power-select
options=manyOptions
selected=selectedOption3
searchField="name"
searchEnabled=(gt manyOptions.length 10)
onchange=(action (mut selectedOption3))
as |option|}}
{{option.name}}
{{/power-select}}
</div>
</div>
{{/freestyle-usage}}
{{#freestyle-annotation}}
Whether or not the dropdown has a search box is configurable. Typically the default is to show a search once a dropdown has more than 10 options.
{{/freestyle-annotation}}

View File

@@ -0,0 +1,156 @@
{{#freestyle-usage "gutter-nav" title="Gutter Menu"}}
<div class="columns">
<div class="column is-4">
<div class="gutter">
<aside class="menu">
<p class="menu-label">Places</p>
<ul class="menu-list">
<li><a href="#" class="is-active">Place One</a></li>
<li><a href="#">Place Two</a></li>
</ul>
<p class="menu-label">Features</p>
<ul class="menu-list">
<li><a href="#">Feature One</a></li>
<li><a href="#">Feature Two</a></li>
</ul>
</aside>
</div>
</div>
<div class="column">
<div class="mock-content">
<div class="mock-vague"></div>
</div>
</div>
</div>
{{/freestyle-usage}}
{{#freestyle-usage "gutter-nav-rich-components" title="Gutter Navigation with Rich Components"}}
<div class="columns">
<div class="column is-4">
<div class="gutter">
<aside class="menu">
<p class="menu-label">Places</p>
<ul class="menu-list">
<li>
<div class="menu-item">
{{#power-select
selected=(or selection "One")
options=(array "One" "Two" "Three")
onchange=(action (mut selection))
as |option|}}
{{option}}
{{/power-select}}
</div>
</li>
<li><a href="#" class="is-active">Place One</a></li>
<li><a href="#">Place Two</a></li>
</ul>
<p class="menu-label">Features</p>
<ul class="menu-list">
<li><a href="#">Feature One</a></li>
<li><a href="#">Feature Two</a></li>
</ul>
</aside>
</div>
</div>
<div class="column">
<div class="mock-content">
<div class="mock-vague"></div>
</div>
</div>
</div>
{{/freestyle-usage}}
{{#freestyle-annotation}}
In order to keep the gutter navigation streamlined and easy to navigation, rich components should be avoided when possible. When not possible, they should be kept near the top.
{{/freestyle-annotation}}
{{#freestyle-usage "gutter-nav-many-items" title="Hypothetical Gutter Navigation with Many Items"}}
<div class="columns">
<div class="column is-4">
<div class="gutter">
<aside class="menu">
<p class="menu-label">Places</p>
<ul class="menu-list">
{{#each (array "One Two" "Three" "Four" "Five" "Six" "Seven") as |item|}}
<li><a href="#">Place {{item}}</a></li>
{{/each}}
</ul>
<p class="menu-label">Features</p>
<ul class="menu-list">
{{#each (array "One Two" "Three" "Four" "Five" "Six" "Seven") as |item|}}
<li><a href="#">Feature {{item}}</a></li>
{{/each}}
</ul>
<p class="menu-label">Other</p>
<ul class="menu-list">
<li><a href="#" class="is-active">The one that didn't fit in</a></li>
</ul>
<p class="menu-label">Things</p>
<ul class="menu-list">
{{#each (array "One Two" "Three" "Four" "Five" "Six" "Seven") as |item|}}
<li><a href="#">Thing {{item}}</a></li>
{{/each}}
</ul>
</aside>
</div>
</div>
<div class="column">
<div class="mock-content">
<div class="mock-vague"></div>
</div>
</div>
</div>
{{/freestyle-usage}}
{{#freestyle-annotation}}
There will only ever be one gutter menu in the Nomad UI, but it helps to imagine a situation where there are many navigation items in the gutter.
{{/freestyle-annotation}}
{{#freestyle-usage "gutter-nav-icon-items" title="Hypothetical Gutter Navigation with Icon Items"}}
<div class="columns">
<div class="column is-4">
<div class="gutter">
<aside class="menu">
<p class="menu-label">Places</p>
<ul class="menu-list">
<li><a href="#">{{x-icon "clock"}} Place One</a></li>
<li><a href="#" class="is-active">{{x-icon "history"}} Place Two</a></li>
</ul>
<p class="menu-label">Features</p>
<ul class="menu-list">
<li><a href="#">{{x-icon "warning"}} Feature One</a></li>
<li><a href="#">{{x-icon "media-pause"}} Feature Two</a></li>
</ul>
</aside>
</div>
</div>
<div class="column">
<div class="mock-content">
<div class="mock-vague"></div>
</div>
</div>
</div>
{{/freestyle-usage}}
{{#freestyle-annotation}}
In the future, the gutter menu may have icons.
{{/freestyle-annotation}}
{{#freestyle-usage "gutter-nav-global" title="Global Gutter Navigation"}}
<div class="columns">
<div class="column is-4">
{{#gutter-menu}}
{{!-- Page content here --}}
{{/gutter-menu}}
</div>
</div>
{{/freestyle-usage}}
{{#freestyle-annotation}}
<p>Since there will only ever be one gutter menu in the UI, it makes sense to express the menu as a singleton component. This is what that singleton component looks like.</p>
<p><strong>Note:</strong> Normally the gutter menu is rendered within a page layout and is fixed position. The columns shown in this example are only to imitate the actual width without applying fixed positioning.</p>
{{/freestyle-annotation}}

View File

@@ -0,0 +1,14 @@
{{#freestyle-usage "header" title="Global Header"}}
<nav class="navbar is-primary">
<div class="navbar-brand">
<span class="navbar-item is-logo">
<img src="/ui/images/nomad-logo.svg" alt="Nomad" />
</span>
</div>
<div class="navbar-end">
<a class="navbar-item">Secondary</a>
<a class="navbar-item">Links</a>
<a class="navbar-item">Here</a>
</div>
</nav>
{{/freestyle-usage}}

View File

@@ -0,0 +1,80 @@
{{#freestyle-usage "inline-definitions" title="Inline Definitions"}}
<div class="boxed-section is-small">
<div class="boxed-section-body inline-definitions">
<span class="label">Some Label</span>
<span class="pair">
<span class="term">Term Name</span>
<span>Term Value</span>
</span>
<span class="pair">
<span class="term">Running?</span>
<span>Yes</span>
</span>
<span class="pair">
<span class="term">Last Updated</span>
<span>{{moment-format (now)}}</span>
</span>
</div>
</div>
{{/freestyle-usage}}
{{#freestyle-annotation}}
A way to tightly display key/value information. Typically seen at the top of pages.
{{/freestyle-annotation}}
{{#freestyle-usage "inline-definitions-variants" title="Inline Definitions Variants"}}
<div class="boxed-section is-small is-success">
<div class="boxed-section-body inline-definitions">
<span class="label">Success Label</span>
<span class="pair">
<span class="term">Term Name</span>
<span>Term Value</span>
</span>
<span class="pair">
<span class="term">Last Updated</span>
<span>{{moment-format (now)}}</span>
</span>
</div>
</div>
<div class="boxed-section is-small is-warning">
<div class="boxed-section-body inline-definitions">
<span class="label">Warning Label</span>
<span class="pair">
<span class="term">Term Name</span>
<span>Term Value</span>
</span>
<span class="pair">
<span class="term">Last Updated</span>
<span>{{moment-format (now)}}</span>
</span>
</div>
</div>
<div class="boxed-section is-small is-danger">
<div class="boxed-section-body inline-definitions">
<span class="label">Danger Label</span>
<span class="pair">
<span class="term">Term Name</span>
<span>Term Value</span>
</span>
<span class="pair">
<span class="term">Last Updated</span>
<span>{{moment-format (now)}}</span>
</span>
</div>
</div>
<div class="boxed-section is-small is-info">
<div class="boxed-section-body inline-definitions">
<span class="label">Info Label</span>
<span class="pair">
<span class="term">Term Name</span>
<span>Term Value</span>
</span>
<span class="pair">
<span class="term">Last Updated</span>
<span>{{moment-format (now)}}</span>
</span>
</div>
</div>
{{/freestyle-usage}}
{{#freestyle-annotation}}
Inline definitions are meant to pair well with emotive color variations.
{{/freestyle-annotation}}

View File

@@ -0,0 +1,31 @@
{{#freestyle-usage "json-viewer" title="JSON Viewer"}}
<div class="boxed-section">
<div class="boxed-section-body is-dark">
{{json-viewer json=jsonSmall}}
</div>
</div>
{{/freestyle-usage}}
{{#freestyle-usage "json-viewer-full" title="JSON Viewer for Full Document"}}
<div class="boxed-section">
<div class="boxed-section-body is-dark">
{{json-viewer json=jsonLarge}}
</div>
</div>
{{/freestyle-usage}}
{{#freestyle-collection defaultKey=0 as |collection|}}
{{#each (array 0 1 2 3 4 5) as |depth|}}
{{#collection.variant key=depth}}
{{#freestyle-usage
(concat "json-viewer-truncated-" depth)
title=(concat "JSON Viewer Expand Depth " depth)}}
<div class="boxed-section">
<div class="boxed-section-body is-dark">
{{json-viewer json=jsonLarge expandDepth=depth}}
</div>
</div>
{{/freestyle-usage}}
{{/collection.variant}}
{{/each}}
{{/freestyle-collection}}

View File

@@ -0,0 +1,24 @@
{{#freestyle-usage "log-stream" title="Log Stream"}}
<div class="boxed-section">
<div class="boxed-section-head">
<span>
<button
class="button {{if (eq mode1 "stdout") "is-info"}}"
onclick={{action (mut mode1) "stdout"}}>stdout</button>
<button
class="button {{if (eq mode1 "stderr") "is-danger"}}"
onclick={{action (mut mode1) "stderr"}}>stderr</button>
</span>
<span class="pull-right">
<button class="button is-white">Head</button>
<button class="button is-white">Tail</button>
<button class="button is-white" onclick={{toggle "isPlaying1" this}}>
{{x-icon (if isPlaying1 "media-play" "media-pause") class="is-text"}}
</button>
</span>
</div>
<div class="boxed-section-body is-dark is-full-bleed">
<pre class="cli-window"><code>{{if (eq mode1 "stdout") sampleOutput sampleError}}</code></pre>
</div>
</div>
{{/freestyle-usage}}

View File

@@ -0,0 +1,125 @@
{{#freestyle-usage "metrics" title="Metrics"}}
<div class="metric-group">
<div class="metric">
<h3 class="label">Label</h3>
<p class="value">12</p>
</div>
</div>
{{/freestyle-usage}}
{{#freestyle-annotation}}
<p>Metrics are a way to show simple values (generally numbers). Labels are smaller than numbers to put emphasis on the data.</p>
{{/freestyle-annotation}}
{{#freestyle-usage "metric-groups" title="Metric Groups"}}
<div class="metric-group">
<div class="metric">
<h3 class="label">Label</h3>
<p class="value">1 / 2</p>
</div>
<div class="metric">
<h3 class="label">Number</h3>
<p class="value">1,300</p>
</div>
<div class="metric">
<h3 class="label">Datacenter</h3>
<p class="value">dc1</p>
</div>
</div>
<div class="metric-group">
<div class="metric">
<h3 class="label">Today</h3>
<p class="value">81º</p>
</div>
<div class="metric">
<h3 class="label">Tomorrow</h3>
<p class="value">73º</p>
</div>
</div>
{{/freestyle-usage}}
{{#freestyle-annotation}}
<p>Related metrics should be lumped together in metric groups. All metrics have to be in a metric group. By putting multiple metrics in a single group, they will be visually lumped together.</p>
{{/freestyle-annotation}}
{{#freestyle-usage "metric-colors" title="Metric Colors"}}
<div class="metric-group">
<div class="metric is-info">
<h3 class="label">Info</h3>
<p class="value">1</p>
</div>
<div class="metric is-success">
<h3 class="label">Success</h3>
<p class="value">2</p>
</div>
<div class="metric is-warning">
<h3 class="label">Warning</h3>
<p class="value">3</p>
</div>
<div class="metric is-danger">
<h3 class="label">Danger</h3>
<p class="value">4</p>
</div>
</div>
<div class="metric-group">
<div class="metric is-white">
<h3 class="label">White</h3>
<p class="value">5</p>
</div>
<div class="metric is-light">
<h3 class="label">Light</h3>
<p class="value">6</p>
</div>
<div class="metric is-primary">
<h3 class="label">Primary</h3>
<p class="value">7</p>
</div>
<div class="metric is-dark">
<h3 class="label">Dark</h3>
<p class="value">8</p>
</div>
<div class="metric is-black">
<h3 class="label">Black</h3>
<p class="value">9</p>
</div>
</div>
{{/freestyle-usage}}
{{#freestyle-annotation}}
<p>All color-modifiers work for metrics, but some work better than others.</p>
<p>Emotive colors work well and are put to use when applicable. Other colors have worse support and less utility.</p>
{{/freestyle-annotation}}
{{#freestyle-usage "metric-states" title="Metric States"}}
<div class="metric-group">
<div class="metric is-primary is-faded">
<h3 class="label">One</h3>
<p class="value">A</p>
</div>
<div class="metric is-primary">
<h3 class="label">Two</h3>
<p class="value">B</p>
</div>
<div class="metric is-primary is-faded">
<h3 class="label">Three</h3>
<p class="value">C</p>
</div>
</div>
<div class="metric-group">
<div class="metric is-danger is-faded">
<h3 class="label">One</h3>
<p class="value">A</p>
</div>
<div class="metric is-danger is-faded">
<h3 class="label">Two</h3>
<p class="value">B</p>
</div>
<div class="metric is-danger">
<h3 class="label">Three</h3>
<p class="value">C</p>
</div>
</div>
{{/freestyle-usage}}
{{#freestyle-annotation}}
<p>Metrics have a disabled state. This is used when a metric is non-existent or irrelevant. It's just as important to show the lack of value as it is to show a value, so simply not rendering non-existent or irrelevant metrics would be worse.</p>
{{/freestyle-annotation}}

View File

@@ -0,0 +1,18 @@
{{#freestyle-usage "page-tabs" title="Page Tabs"}}
<div class="tabs">
<ul>
<li><a href="#">Overview</a></li>
<li><a href="#" class="is-active">Definition</a></li>
<li><a href="#">Versions</a></li>
<li><a href="#">Deployments</a></li>
</ul>
</div>
{{/freestyle-usage}}
{{#freestyle-usage "page-tabs-single" title="Single Page Tab"}}
<div class="tabs">
<ul>
<li><a href="#" class="is-active">Overview</a></li>
</ul>
</div>
{{/freestyle-usage}}

View File

@@ -0,0 +1,51 @@
{{#freestyle-usage "page-title" title="Page Title"}}
<div class="mock-spacing">
<h1 class="title">This is the Page Title</h1>
</div>
{{/freestyle-usage}}
{{#freestyle-annotation}}
<p>In its simplest form, a page title is just an H1.</p>
{{/freestyle-annotation}}
{{#freestyle-usage "page-title-after-elements" title="Page Title with After Elements"}}
<div class="mock-spacing">
<h1 class="title">
This is the Page Title
<span class="bumper-left tag is-running">Running</span>
<span class="tag is-hollow is-small no-text-transform">237aedcb8982fe09bcee0877acedd</span>
</h1>
</div>
{{/freestyle-usage}}
{{#freestyle-annotation}}
<p>It is common to put high-impact tags and badges to the right of titles. These tags should only ever appear on the right-hand side of the title, and they should be listed in descending weights. Tags with a background are heavier than tags that are hollow. Longer values are heavier than shorter values.</p>
{{/freestyle-annotation}}
{{#freestyle-usage "page-title-with-status-light" title="Page Title with Status Light"}}
<div class="mock-spacing">
<h1 class="title">
<span class="node-status-light initializing"></span>
This is the Page Title
<span class="bumper-left tag is-running">Running</span>
<span class="tag is-hollow is-small no-text-transform">237aedcb8982fe09bcee0877acedd</span>
</h1>
</div>
{{/freestyle-usage}}
{{#freestyle-annotation}}
<p>A simple color or pattern is faster to scan than a title and can often say more than words can. For pages that have an important status component to them (e.g., client detail page), a status light can be shown to the left of the title where typically eyes will begin to scan a page.</p>
{{/freestyle-annotation}}
{{#freestyle-usage "page-title-with-actions" title="Page Title with Actions"}}
<div class="mock-spacing">
<h1 class="title">
<span class="node-status-light initializing"></span>
This is the Page Title
<span class="bumper-left tag is-running">Running</span>
<span class="tag is-hollow is-small no-text-transform">237aedcb8982fe09bcee0877acedd</span>
<button class="button is-warning is-small is-inline">If you wish</button>
<button class="button is-danger is-outlined is-important is-small is-inline">No Regrets</button>
</h1>
</div>
{{/freestyle-usage}}
{{#freestyle-annotation}}
<p>When actions apply to the entire context of a page, (e.g., job actions on the job detail page), buttons for these actions go in the page title. Buttons are always placed on the far right end of a page title. No elements can go to the right of these buttons.</p>
{{/freestyle-annotation}}

View File

@@ -0,0 +1,77 @@
{{#freestyle-usage "progress-bar" title="Progress Bar"}}
<div class="inline-chart tooltip" aria-label="5 / 15">
<progress
class="progress is-primary is-small"
value="0.33"
max="1">
0.33
</progress>
</div>
{{/freestyle-usage}}
{{#freestyle-usage "progress-bar-colors" title="Progress Bar Colors"}}
<div class="columns">
<div class="column">
<div class="inline-chart tooltip" aria-label="5 / 15">
<progress
class="progress is-info is-small"
value="0.33"
max="1">
0.33
</progress>
</div>
</div>
<div class="column">
<div class="inline-chart tooltip" aria-label="5 / 15">
<progress
class="progress is-success is-small"
value="0.33"
max="1">
0.33
</progress>
</div>
</div>
<div class="column">
<div class="inline-chart tooltip" aria-label="5 / 15">
<progress
class="progress is-warning is-small"
value="0.33"
max="1">
0.33
</progress>
</div>
</div>
<div class="column">
<div class="inline-chart tooltip" aria-label="5 / 15">
<progress
class="progress is-danger is-small"
value="0.33"
max="1">
0.33
</progress>
</div>
</div>
</div>
{{/freestyle-usage}}
{{#freestyle-usage "progress-bar-live" title="Progress Bar Live Updates"}}
<div class="columns">
<div class="column is-one-third">
<div class="inline-chart tooltip" aria-label="{{numerator}} / {{denominator}}">
<progress
class="progress is-primary is-small"
value="{{percentage}}"
max="1">
{{percentage}}
</progress>
</div>
</div>
</div>
{{/freestyle-usage}}
{{#freestyle-annotation}}
<div class="boxed-section">
<div class="boxed-section-body is-dark">
{{json-viewer json=liveDetails}}
</div>
</div>
{{/freestyle-annotation}}

View File

@@ -0,0 +1,19 @@
{{#freestyle-usage "search-box" title="Search Box"}}
{{search-box
searchTerm=(mut searchTerm1)
placeholder="Search things..."}}
{{/freestyle-usage}}
{{#freestyle-annotation}}
<p>The search box component is a thin wrapper around a simple input. Although the searchTerm passed to it is a mutable reference, internally search term is debounced. This is to prevent potentially expensive code that depends on searchTerm from recomputing many times as a user types.</p>
<p>There is no form of the search box component that defers updating the searchTerm reference until the user manually clicks a "Search" button. This can be achieved by placing a button next to the search bar component and using it to perform search, but search should be automatic whenever possible.</p>
{{/freestyle-annotation}}
{{#freestyle-usage "search-box-compact" title="Search Box Compact"}}
{{search-box
searchTerm=(mut searchTerm2)
placeholder="Search things..."
inputClass="is-compact"}}
{{/freestyle-usage}}
{{#freestyle-annotation}}
<p>Search box provides an inputClass property to control the inner input. This is nice for fitting the search box into smaller spaces, such as boxed-section heads.</p>
{{/freestyle-annotation}}

View File

@@ -0,0 +1,3 @@
{{#freestyle-usage "table-configuration" title="Table, Configuration"}}
{{attributes-table attributes=attributes class="attributes-table"}}
{{/freestyle-usage}}

View File

@@ -0,0 +1,257 @@
{{#freestyle-usage "table-simple" title="Table"}}
{{#list-table source=shortList as |t|}}
{{#t.head}}
<th>Name</th>
<th>Language</th>
<th>Description</th>
{{/t.head}}
{{#t.body key="model.name" as |row|}}
<tr>
<td>{{row.model.name}}</td>
<td>{{row.model.lang}}</td>
<td>{{row.model.desc}}</td>
</tr>
{{/t.body}}
{{/list-table}}
{{/freestyle-usage}}
{{#freestyle-annotation}}
<p>Tables have airy designs with a minimal amount of borders. This maximizes their utility.</p>
{{/freestyle-annotation}}
{{#freestyle-usage "table-search" title="Table Search"}}
<div class="boxed-section">
<div class="boxed-section-head">
Table Name
{{search-box
searchTerm=(mut searchTerm)
placeholder="Search..."
class="is-inline pull-right"
inputClass="is-compact"}}
</div>
<div class="boxed-section-body {{if filteredShortList.length "is-full-bleed"}}">
{{#if filteredShortList.length}}
{{#list-table source=filteredShortList as |t|}}
{{#t.head}}
<th>Name</th>
<th>Language</th>
<th>Description</th>
{{/t.head}}
{{#t.body key="model.name" as |row|}}
<tr>
<td>{{row.model.name}}</td>
<td>{{row.model.lang}}</td>
<td>{{row.model.desc}}</td>
</tr>
{{/t.body}}
{{/list-table}}
{{else}}
<div class="empty-message">
<h3 class="empty-message-headline">No Matches</h3>
<p class="empty-message-body">No products match your query.</p>
</div>
{{/if}}
</div>
</div>
{{/freestyle-usage}}
{{#freestyle-annotation}}
<p>Tables compose with boxed-section and boxed-section composes with search box.</p>
{{/freestyle-annotation}}
{{#freestyle-usage "table-sortable-columns" title="Table Sortable Columns"}}
{{#list-table
source=sortedShortList
sortProperty=sortProperty
sortDescending=sortDescending as |t|}}
{{#t.head}}
{{#t.sort-by prop="name"}}Name{{/t.sort-by}}
{{#t.sort-by prop="lang" class="is-2"}}Language{{/t.sort-by}}
<th>Description</th>
{{/t.head}}
{{#t.body key="model.name" as |row|}}
<tr>
<td>{{row.model.name}}</td>
<td>{{row.model.lang}}</td>
<td>{{row.model.desc}}</td>
</tr>
{{/t.body}}
{{/list-table}}
{{/freestyle-usage}}
{{#freestyle-annotation}}
<p>The list-table component provides a <code>sort-by</code> contextual component for building <code>link-to</code> components with the appropriate query params.</p>
<p>This leaves the component stateless, relying on data to be passed down and sending actions back up via the router (via link-to).</p>
{{/freestyle-annotation}}
{{#freestyle-usage "table-multi-row" title="Table Multi-row"}}
{{#list-table
source=sortedShortList
sortProperty=sortProperty
sortDescending=sortDescending
class="is-striped" as |t|}}
{{#t.head}}
{{#t.sort-by prop="name"}}Name{{/t.sort-by}}
{{#t.sort-by prop="lang"}}Language{{/t.sort-by}}
{{/t.head}}
{{#t.body key="model.name" as |row|}}
<tr>
<td>{{row.model.name}}</td>
<td>{{row.model.lang}}</td>
</tr>
<tr>
<td colspan="2">{{row.model.desc}}</td>
</tr>
{{/t.body}}
{{/list-table}}
{{/freestyle-usage}}
{{#freestyle-annotation}}
<p>THe list-table component attempts to be as flexible as possible. For this reason, <code>t.body</code> does not provide the typical <code>tr</code> element. It's sometimes desired to have multiple elements per record.</p>
{{/freestyle-annotation}}
{{#freestyle-usage "table-pagination" title="Table Pagination"}}
{{#list-pagination source=longList size=5 page=currentPage as |p|}}
{{#list-table source=p.list class="with-foot" as |t|}}
{{#t.head}}
<th class="is-1">Rank</th>
<th>City</th>
<th>State</th>
<th>Population</th>
<th>Growth</th>
{{/t.head}}
{{#t.body key="model.rank" as |row|}}
<tr>
<td>{{row.model.rank}}</td>
<td>{{row.model.city}}</td>
<td>{{row.model.state}}</td>
<td>{{row.model.population}}</td>
<td>{{format-percentage row.model.growth total=1}}</td>
</tr>
{{/t.body}}
{{/list-table}}
<div class="table-foot">
<nav class="pagination">
<span class="bumper-left">U.S. City population and growth from 2000-2013</span>
<div class="pagination-numbers">
{{p.startsAt}}&ndash;{{p.endsAt}} of {{longList.length}}
</div>
{{#p.prev class="pagination-previous"}} &lt; {{/p.prev}}
{{#p.next class="pagination-next"}} &gt; {{/p.next}}
<ul class="pagination-list"></ul>
</nav>
</div>
{{/list-pagination}}
{{/freestyle-usage}}
{{#freestyle-annotation}}
<p>Pagination works like sorting: using <code>link-to</code>s to set a query param.</p>
<p>Pagination, like Table, is a minimal design. Only a next and previous button are available. The current place in the set of pages is tracked by showing which slice of items is currently shown.</p>
<p>The pagination component exposes first and last components (for jumping to the beginning and end of a list) as well as pageLinks for generating links around the current page.</p>
{{/freestyle-annotation}}
{{#freestyle-usage "table-row-links" title="Table Row Links"}}
{{#list-table source=shortList as |t|}}
{{#t.head}}
<th>Name</th>
<th>Language</th>
<th>Description</th>
{{/t.head}}
{{#t.body key="model.name" as |row|}}
<tr class="is-interactive">
<td><a href="#" class="is-primary">{{row.model.name}}</a></td>
<td>{{row.model.lang}}</td>
<td>{{row.model.desc}}</td>
</tr>
{{/t.body}}
{{/list-table}}
{{/freestyle-usage}}
{{#freestyle-annotation}}
<p>It is common for tables to act as lists of links, (e.g., clients list all allocations, each row links to the allocation detail). The helper class <code>is-interactive</code> on the <code>tr</code> makes table rows have a pointer cursor. The helper class <code>is-primary</code> on the <code>a</code> element in a table row makes the link bold and black instead of blue. This makes the link stand out less, since the entire row is a link.</p>
<p>A few rules for using table row links:</p>
<ol>
<li>The <code>is-primary</code> cell should always be the first cell</li>
<li>The <code>is-primary</code> cell should always contain a link to the destination in the form of an <code>a</code> element. This is to support opening a link in a new tab.</li>
<li>The full row should transition to the destination on click. This is to improve the usability of a table by creating a larger click area.</li>
</ol>
{{/freestyle-annotation}}
{{#freestyle-usage "table-cell-links" title="Table Cell Links"}}
{{#list-table source=shortList as |t|}}
{{#t.head}}
<th>Name</th>
<th>Language</th>
<th>Description</th>
{{/t.head}}
{{#t.body key="model.name" as |row|}}
<tr>
<td><a href={{row.model.link}}>{{row.model.name}}</a></td>
<td>{{row.model.lang}}</td>
<td>{{row.model.desc}}</td>
</tr>
{{/t.body}}
{{/list-table}}
{{/freestyle-usage}}
{{#freestyle-annotation}}
<p>Links in table cells are just links.</p>
{{/freestyle-annotation}}
{{#freestyle-usage "table-cell-decorations" title="Table Cell Decorations"}}
{{#list-table source=shortList as |t|}}
{{#t.head}}
<th>Name</th>
<th>Language</th>
<th>Description</th>
{{/t.head}}
{{#t.body key="model.name" as |row|}}
<tr>
<td><a href={{row.model.link}}>{{row.model.name}}</a></td>
<td class="nowrap">
<span class="color-swatch
{{if (eq row.model.lang "ruby") "swatch-6"}}
{{if (eq row.model.lang "golang") "swatch-5"}}" />
{{row.model.lang}}
</td>
<td>{{row.model.desc}}</td>
</tr>
{{/t.body}}
{{/list-table}}
{{/freestyle-usage}}
{{#freestyle-annotation}}
<p>Small icons and accents of color make tables easier to scan.</p>
{{/freestyle-annotation}}
{{#freestyle-usage "table-cell-icons" title="Table Cell Icons"}}
{{#list-pagination source=longList size=5 page=currentPage as |p|}}
{{#list-table source=p.list class="with-foot" as |t|}}
{{#t.head}}
<th class="is-narrow"></th>
<th class="is-1">Rank</th>
<th>City</th>
<th>State</th>
<th>Population</th>
<th>Growth</th>
{{/t.head}}
{{#t.body key="model.rank" as |row|}}
<tr>
<td class="is-narrow">
{{#if (lt row.model.growth 0)}}
{{x-icon "warning" class="is-warning"}}
{{/if}}
</td>
<td>{{row.model.rank}}</td>
<td>{{row.model.city}}</td>
<td>{{row.model.state}}</td>
<td>{{row.model.population}}</td>
<td>{{format-percentage row.model.growth total=1}}</td>
</tr>
{{/t.body}}
{{/list-table}}
<div class="table-foot">
<nav class="pagination">
<span class="bumper-left">U.S. City population and growth from 2000-2013. Cities with negative growth denoted.</span>
<div class="pagination-numbers">
{{p.startsAt}}&ndash;{{p.endsAt}} of {{longList.length}}
</div>
{{#p.prev class="pagination-previous"}} &lt; {{/p.prev}}
{{#p.next class="pagination-next"}} &gt; {{/p.next}}
<ul class="pagination-list"></ul>
</nav>
</div>
{{/list-pagination}}
{{/freestyle-usage}}

View File

@@ -0,0 +1,204 @@
{{#freestyle-usage 'timeline' title="Simple Timeline"}}
<ol class="timeline">
<li class="timeline-note">
{{moment-format yesterday "MMMM D, YYYY"}}
</li>
<li class="timeline-object">
<div class="boxed-section">
<div class="boxed-section-head is-light">
Object number one
</div>
</div>
</li>
<li class="timeline-object">
<div class="boxed-section">
<div class="boxed-section-head is-light">
Object number two
</div>
</div>
</li>
<li class="timeline-note">
{{moment-format today "MMMM D, YYYY"}}
</li>
<li class="timeline-object">
<div class="boxed-section">
<div class="boxed-section-head is-light">
Object number three
</div>
</div>
</li>
</ol>
{{/freestyle-usage}}
{{#freestyle-annotation}}
<p>Timelines are a combination of objects and notes. Objects compose with boxed sections to create structure.</p>
<p>Timeline notes should be used sparingly when possible. In this example there is a note per day rather than a note per object.</p>
{{/freestyle-annotation}}
{{#freestyle-usage 'timeline-intricate' title="Detailed Timeline"}}
<ol class="timeline">
<li class="timeline-note">
{{moment-format today "MMMM D, YYYY"}}
</li>
<li class="timeline-object">
<div class="boxed-section">
<div class="boxed-section-head is-light">
<span class="tag is-running">Running</span>
<span class="bumper-left pair is-faded">
<span class="term">Stable</span>
<span class="badge is-light is-faded"><code>a387e243</code></span>
</span>
<span class="bumper-left pair is-faded">
<span class="term">Submitted</span>
<span>{{moment-from-now (now)}}</span>
</span>
</div>
</div>
</li>
<li class="timeline-object">
<div class="boxed-section">
<div class="boxed-section-head is-light">
<span class="tag is-complete">Complete</span>
<span class="bumper-left pair is-faded">
<span class="term">Expired</span>
<span class="badge is-light is-faded"><code>b3220efb</code></span>
</span>
<span class="bumper-left pair is-faded">
<span class="term">Submitted</span>
<span>{{moment-format yesterday}}</span>
</span>
</div>
</div>
</li>
<li class="timeline-note">
{{moment-format yesterday "MMMM D, YYYY"}}
</li>
<li class="timeline-object">
<div class="boxed-section">
<div class="boxed-section-head is-light">
<span class="tag is-error">Failed</span>
<span class="bumper-left pair is-faded">
<span class="term">Reverted</span>
<span class="badge is-light is-faded"><code>fec9218e</code></span>
</span>
<span class="bumper-left pair is-faded">
<span class="term">Submitted</span>
<span>{{moment-format yesterday}}</span>
</span>
</div>
</div>
</li>
</ol>
{{/freestyle-usage}}
{{#freestyle-usage 'timeline-toggles' title='Toggling Timeline Objects'}}
<ol class="timeline">
<li class="timeline-note">
{{moment-format today "MMMM D, YYYY"}}
</li>
<li class="timeline-object">
<div class="boxed-section">
<div class="boxed-section-head is-light">
<span class="tag is-running">Running</span>
<span class="bumper-left pair is-faded">
<span class="term">Stable</span>
<span class="badge is-light is-faded"><code>a387e243</code></span>
</span>
<button
class="button is-light is-compact pull-right"
onclick={{action (mut toggle1) (not toggle1)}}>
{{if toggle1 "Close" "Open"}}
</button>
</div>
{{#if toggle1}}
<div class="boxed-section-body">
<p>Some details for the timeline object.</p>
</div>
{{/if}}
</div>
</li>
<li class="timeline-note">
{{moment-format yesterday "MMMM D, YYYY"}}
</li>
<li class="timeline-object">
<div class="boxed-section">
<div class="boxed-section-head is-light">
<span class="tag is-complete">Complete</span>
<span class="bumper-left pair is-faded">
<span class="term">Expired</span>
<span class="badge is-light is-faded"><code>b3220efb</code></span>
</span>
<button
class="button is-light is-compact pull-right"
onclick={{action (mut toggle2) (not toggle2)}}>
{{if toggle2 "Close" "Open"}}
</button>
</div>
{{#if toggle2}}
<div class="boxed-section-body">
<p>Some details for the timeline object.</p>
</div>
{{/if}}
</div>
</li>
</ol>
{{/freestyle-usage}}
{{#freestyle-usage 'timeline-emphasis' title='Emphasizing a Timeline Object'}}
<ol class="timeline">
<li class="timeline-note">
{{moment-format today "MMMM D, YYYY"}}
</li>
<li class="timeline-object">
<div class="boxed-section">
<div class="boxed-section-head is-light">
<span class="pair is-faded">
<span class="term">Stable</span>
<span class="badge is-light is-faded"><code>a387e243</code></span>
</span>
<span class="bumper-left pair is-faded">
<span class="term">Submitted</span>
<span>{{moment-from-now (now)}}</span>
</span>
</div>
</div>
</li>
<li class="timeline-object">
<div class="boxed-section">
<div class="boxed-section-head">
Pay attention here
</div>
<div class="boxed-section-body">
<span class="pair is-faded">
<span class="term">Expired</span>
<span class="badge is-light is-faded"><code>b3220efb</code></span>
</span>
<span class="bumper-left pair is-faded">
<span class="term">Submitted</span>
<span>{{moment-format yesterday}}</span>
</span>
</div>
</div>
</li>
<li class="timeline-note">
{{moment-format yesterday "MMMM D, YYYY"}}
</li>
<li class="timeline-object">
<div class="boxed-section">
<div class="boxed-section-head is-light">
<span class="pair is-faded">
<span class="term">Reverted</span>
<span class="badge is-light is-faded"><code>fec9218e</code></span>
</span>
<span class="bumper-left pair is-faded">
<span class="term">Submitted</span>
<span>{{moment-format yesterday}}</span>
</span>
</div>
</div>
</li>
</ol>
{{/freestyle-usage}}
{{#freestyle-annotation}}
By using a full boxed-section for an emphasized timeline object, the object takes up more space and gets more visual weight. It also adheres to existing patterns.
{{/freestyle-annotation}}

View File

@@ -0,0 +1,22 @@
{{#freestyle-usage "two-step-button" title="Two Step Button"}}
<div class="mock-spacing">
{{two-step-button
idleText="Scary Action"
cancelText="Nvm"
confirmText="Yep"
confirmationMessage="Wait, really? Like...seriously?"}}
</div>
{{/freestyle-usage}}
{{#freestyle-usage "two-step-button-title" title="Two Step Button in Title"}}
<div class="mock-spacing">
<h1 class="title">
This is a page title
{{two-step-button
idleText="Scary Action"
cancelText="Nvm"
confirmText="Yep"
confirmationMessage="Wait, really? Like...seriously?"}}
</h1>
</div>
{{/freestyle-usage}}

View File

@@ -1,16 +1,16 @@
{{#if isIdle}}
<button data-test-idle-button type="button" class="button is-danger is-outlined is-important is-small is-inline" onclick={{action "promptForConfirmation"}}>
<button data-test-idle-button type="button" class="button is-danger is-outlined is-important is-small" onclick={{action "promptForConfirmation"}}>
{{idleText}}
</button>
{{else if isPendingConfirmation}}
<span data-test-confirmation-message class="confirmation-text">{{confirmationMessage}}</span>
<button data-test-cancel-button type="button" class="button is-dark is-outlined is-small is-inline" onclick={{action (queue
<button data-test-cancel-button type="button" class="button is-dark is-outlined is-small" onclick={{action (queue
(action "setToIdle")
(action onCancel)
)}}>
{{cancelText}}
</button>
<button data-test-confirm-button class="button is-danger is-small is-inline" onclick={{action (queue
<button data-test-confirm-button class="button is-danger is-small" onclick={{action (queue
(action "setToIdle")
(action onConfirm)
)}}>

View File

@@ -15,6 +15,14 @@
{{/freestyle-section}}
{{#freestyle-section name="Components" as |section|}}
{{#section.subsection name="Accordion"}}
{{freestyle/sg-accordion}}
{{/section.subsection}}
{{#section.subsection name="Alerts"}}
{{freestyle/sg-alerts}}
{{/section.subsection}}
{{#section.subsection name="Boxed section"}}
{{freestyle/sg-boxed-section}}
{{/section.subsection}}
@@ -28,32 +36,66 @@
{{/section.subsection}}
{{#section.subsection name="Diff Viewer"}}
{{freestyle/sg-diff-viewer}}
{{/section.subsection}}
{{#section.subsection name="Dropdown"}}
{{freestyle/sg-dropdown}}
{{/section.subsection}}
{{#section.subsection name="Event stream"}}
{{/section.subsection}}
{{#section.subsection name="Gutter navigation"}}
{{#section.subsection name="Gutter menu"}}
{{freestyle/sg-gutter-menu}}
{{/section.subsection}}
{{#section.subsection name="Header"}}
{{freestyle/sg-header}}
{{/section.subsection}}
{{#section.subsection name="Inline definitions"}}
{{freestyle/sg-inline-definitions}}
{{/section.subsection}}
{{#section.subsection name="JSON Viewer"}}
{{freestyle/sg-json-viewer}}
{{/section.subsection}}
{{#section.subsection name="Log Stream"}}
{{freestyle/sg-log-stream}}
{{/section.subsection}}
{{#section.subsection name="Metric"}}
{{#section.subsection name="Metrics"}}
{{freestyle/sg-metrics}}
{{/section.subsection}}
{{#section.subsection name="Page tabs"}}
{{freestyle/sg-page-tabs}}
{{/section.subsection}}
{{#section.subsection name="Page title"}}
{{freestyle/sg-page-title}}
{{/section.subsection}}
{{#section.subsection name="Search box"}}
{{freestyle/sg-search-box}}
{{/section.subsection}}
{{#section.subsection name="Table"}}
{{freestyle/sg-table
sortProperty=sortProperty
sortDescending=sortDescending
currentPage=currentPage}}
{{/section.subsection}}
{{#section.subsection name="Table, Configuration"}}
{{freestyle/sg-table-configuration}}
{{/section.subsection}}
{{#section.subsection name="Timeline"}}
{{freestyle/sg-timeline}}
{{/section.subsection}}
{{#section.subsection name="Two-step Button"}}
{{freestyle/sg-two-step-button}}
{{/section.subsection}}
{{/freestyle-section}}
@@ -67,6 +109,7 @@
{{/section.subsection}}
{{#section.subsection name="Progress Bar"}}
{{freestyle/sg-progress-bar}}
{{/section.subsection}}
{{/freestyle-section}}
{{/freestyle-guide}}

View File

@@ -0,0 +1,43 @@
export default [
{
name: 'Nomad',
lang: 'golang',
desc:
'Nomad is a flexible, enterprise-grade cluster scheduler designed to easily integrate into existing workflows. Nomad can run a diverse workload of micro-service, batch, containerized and non-containerized applications.',
link: 'https://www.nomadproject.io/',
},
{
name: 'Terraform',
lang: 'golang',
desc:
'Terraform is a tool for building, changing, and combining infrastructure safely and efficiently.',
link: 'https://www.terraform.io/',
},
{
name: 'Vault',
lang: 'golang',
desc:
'A tool for secrets management, encryption as a service, and privileged access management',
link: 'https://www.vaultproject.io/',
},
{
name: 'Consul',
lang: 'golang',
desc:
'Consul is a distributed, highly available, and data center aware solution to connect and configure applications across dynamic, distributed infrastructure.',
link: 'https://www.consul.io/',
},
{
name: 'Vagrant',
lang: 'ruby',
desc: 'Vagrant is a tool for building and distributing development environments.',
link: 'https://www.vagrantup.com/',
},
{
name: 'Packer',
lang: 'golang',
desc:
'Packer is a tool for creating identical machine images for multiple platforms from a single source configuration.',
link: 'https://www.packer.io/',
},
];

View File

@@ -12,6 +12,13 @@ module.exports = function(defaults) {
svg: {
paths: ['public/images/icons'],
},
funnel: {
enabled: isProd,
exclude: [
`${defaults.project.pkg.name}/components/freestyle/**/*`,
`${defaults.project.pkg.name}/templates/components/freestyle/**/*`,
],
},
});
// Use `app.import` to add additional libraries to the generated

View File

@@ -36,6 +36,7 @@
"ember-cli-bourbon": "2.0.0-beta.1",
"ember-cli-dependency-checker": "^2.1.0",
"ember-cli-eslint": "^4.2.3",
"ember-cli-funnel": "^0.6.0",
"ember-cli-htmlbars": "^2.0.3",
"ember-cli-htmlbars-inline-precompile": "^1.0.2",
"ember-cli-inject-live-reload": "^1.4.1",

View File

@@ -2987,6 +2987,12 @@ ember-cli-eslint@^4.2.3:
rsvp "^4.6.1"
walk-sync "^0.3.0"
ember-cli-funnel@^0.6.0:
version "0.6.0"
resolved "https://registry.yarnpkg.com/ember-cli-funnel/-/ember-cli-funnel-0.6.0.tgz#ee0c14c216991f7f62f91de62a52268590216b2f"
dependencies:
broccoli-funnel "^2.0.0"
ember-cli-get-component-path-option@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/ember-cli-get-component-path-option/-/ember-cli-get-component-path-option-1.0.0.tgz#0d7b595559e2f9050abed804f1d8eff1b08bc771"