mirror of https://github.com/hashicorp/consul
Merge pull request #7125 from hashicorp/ui-staging
ui: UI Release Merge (ui-staging merge)pull/7127/head
commit
47ac0bcd01
|
@ -4,6 +4,9 @@ import { SLUG_KEY } from 'consul-ui/models/policy';
|
|||
import { FOREIGN_KEY as DATACENTER_KEY } from 'consul-ui/models/dc';
|
||||
import { NSPACE_KEY } from 'consul-ui/models/nspace';
|
||||
|
||||
import nonEmptySet from 'consul-ui/utils/non-empty-set';
|
||||
const Namespace = nonEmptySet('Namespace');
|
||||
|
||||
// TODO: Update to use this.formatDatacenter()
|
||||
export default Adapter.extend({
|
||||
requestForQuery: function(request, { dc, ns, index, id }) {
|
||||
|
@ -32,7 +35,6 @@ export default Adapter.extend({
|
|||
requestForCreateRecord: function(request, serialized, data) {
|
||||
const params = {
|
||||
...this.formatDatacenter(data[DATACENTER_KEY]),
|
||||
...this.formatNspace(data[NSPACE_KEY]),
|
||||
};
|
||||
return request`
|
||||
PUT /v1/acl/policy?${params}
|
||||
|
@ -42,13 +44,13 @@ export default Adapter.extend({
|
|||
Description: serialized.Description,
|
||||
Rules: serialized.Rules,
|
||||
Datacenters: serialized.Datacenters,
|
||||
...Namespace(serialized.Namespace),
|
||||
}}
|
||||
`;
|
||||
},
|
||||
requestForUpdateRecord: function(request, serialized, data) {
|
||||
const params = {
|
||||
...this.formatDatacenter(data[DATACENTER_KEY]),
|
||||
...this.formatNspace(data[NSPACE_KEY]),
|
||||
};
|
||||
return request`
|
||||
PUT /v1/acl/policy/${data[SLUG_KEY]}?${params}
|
||||
|
@ -58,6 +60,7 @@ export default Adapter.extend({
|
|||
Description: serialized.Description,
|
||||
Rules: serialized.Rules,
|
||||
Datacenters: serialized.Datacenters,
|
||||
Namespace: serialized.Namespace,
|
||||
}}
|
||||
`;
|
||||
},
|
||||
|
|
|
@ -3,7 +3,9 @@ import Adapter from './application';
|
|||
import { SLUG_KEY } from 'consul-ui/models/role';
|
||||
import { FOREIGN_KEY as DATACENTER_KEY } from 'consul-ui/models/dc';
|
||||
import { NSPACE_KEY } from 'consul-ui/models/nspace';
|
||||
import nonEmptySet from 'consul-ui/utils/non-empty-set';
|
||||
|
||||
const Namespace = nonEmptySet('Namespace');
|
||||
// TODO: Update to use this.formatDatacenter()
|
||||
export default Adapter.extend({
|
||||
requestForQuery: function(request, { dc, ns, index, id }) {
|
||||
|
@ -32,7 +34,6 @@ export default Adapter.extend({
|
|||
requestForCreateRecord: function(request, serialized, data) {
|
||||
const params = {
|
||||
...this.formatDatacenter(data[DATACENTER_KEY]),
|
||||
...this.formatNspace(data[NSPACE_KEY]),
|
||||
};
|
||||
return request`
|
||||
PUT /v1/acl/role?${params}
|
||||
|
@ -40,16 +41,15 @@ export default Adapter.extend({
|
|||
${{
|
||||
Name: serialized.Name,
|
||||
Description: serialized.Description,
|
||||
Namespace: serialized.Namespace,
|
||||
Policies: serialized.Policies,
|
||||
ServiceIdentities: serialized.ServiceIdentities,
|
||||
...Namespace(serialized.Namespace),
|
||||
}}
|
||||
`;
|
||||
},
|
||||
requestForUpdateRecord: function(request, serialized, data) {
|
||||
const params = {
|
||||
...this.formatDatacenter(data[DATACENTER_KEY]),
|
||||
...this.formatNspace(data[NSPACE_KEY]),
|
||||
};
|
||||
return request`
|
||||
PUT /v1/acl/role/${data[SLUG_KEY]}?${params}
|
||||
|
|
|
@ -4,7 +4,9 @@ import { inject as service } from '@ember/service';
|
|||
import { SLUG_KEY } from 'consul-ui/models/token';
|
||||
import { FOREIGN_KEY as DATACENTER_KEY } from 'consul-ui/models/dc';
|
||||
import { NSPACE_KEY } from 'consul-ui/models/nspace';
|
||||
import nonEmptySet from 'consul-ui/utils/non-empty-set';
|
||||
|
||||
const Namespace = nonEmptySet('Namespace');
|
||||
// TODO: Update to use this.formatDatacenter()
|
||||
export default Adapter.extend({
|
||||
store: service('store'),
|
||||
|
@ -35,7 +37,6 @@ export default Adapter.extend({
|
|||
requestForCreateRecord: function(request, serialized, data) {
|
||||
const params = {
|
||||
...this.formatDatacenter(data[DATACENTER_KEY]),
|
||||
...this.formatNspace(data[NSPACE_KEY]),
|
||||
};
|
||||
return request`
|
||||
PUT /v1/acl/token?${params}
|
||||
|
@ -46,6 +47,7 @@ export default Adapter.extend({
|
|||
Roles: serialized.Roles,
|
||||
ServiceIdentities: serialized.ServiceIdentities,
|
||||
Local: serialized.Local,
|
||||
...Namespace(serialized.Namespace),
|
||||
}}
|
||||
`;
|
||||
},
|
||||
|
@ -66,13 +68,13 @@ export default Adapter.extend({
|
|||
}
|
||||
const params = {
|
||||
...this.formatDatacenter(data[DATACENTER_KEY]),
|
||||
...this.formatNspace(data[NSPACE_KEY]),
|
||||
};
|
||||
return request`
|
||||
PUT /v1/acl/token/${data[SLUG_KEY]}?${params}
|
||||
|
||||
${{
|
||||
Description: serialized.Description,
|
||||
Namespace: serialized.Namespace,
|
||||
Policies: serialized.Policies,
|
||||
Roles: serialized.Roles,
|
||||
ServiceIdentities: serialized.ServiceIdentities,
|
||||
|
|
|
@ -42,6 +42,7 @@ export default Component.extend({
|
|||
guid: '',
|
||||
expanded: false,
|
||||
orientation: 'vertical',
|
||||
keyboardAccess: true,
|
||||
init: function() {
|
||||
this._super(...arguments);
|
||||
set(this, 'guid', this.dom.guid(this));
|
||||
|
@ -59,6 +60,9 @@ export default Component.extend({
|
|||
this._listeners.remove();
|
||||
},
|
||||
actions: {
|
||||
keypressClick: function(e) {
|
||||
e.target.dispatchEvent(new MouseEvent('click'));
|
||||
},
|
||||
keypress: function(e) {
|
||||
// If the event is from the trigger and its not an opening/closing
|
||||
// key then don't do anything
|
||||
|
@ -83,6 +87,8 @@ export default Component.extend({
|
|||
if (typeof keys[this.orientation][e.keyCode] === 'undefined') {
|
||||
return;
|
||||
}
|
||||
// prevent any scroll, or default actions
|
||||
e.preventDefault();
|
||||
const $focused = this.dom.element(`${MENU_ITEMS}:focus`, this.$menu);
|
||||
let i;
|
||||
if ($focused) {
|
||||
|
@ -95,11 +101,12 @@ export default Component.extend({
|
|||
},
|
||||
// TODO: The argument here needs to change to an event
|
||||
// see toggle-button.change
|
||||
change: function(open) {
|
||||
change: function(e) {
|
||||
const open = e.target.checked;
|
||||
if (open) {
|
||||
this.actions.open.apply(this, []);
|
||||
this.actions.open.apply(this, [e]);
|
||||
} else {
|
||||
this.actions.close.apply(this, []);
|
||||
this.actions.close.apply(this, [e]);
|
||||
}
|
||||
},
|
||||
close: function(e) {
|
||||
|
@ -127,6 +134,9 @@ export default Component.extend({
|
|||
this.$trigger.dispatchEvent(new MouseEvent('click'));
|
||||
return;
|
||||
}
|
||||
if (!this.keyboardAccess) {
|
||||
return;
|
||||
}
|
||||
this.actions.keypress.apply(this, [e]);
|
||||
},
|
||||
});
|
||||
|
|
|
@ -25,7 +25,7 @@ export default Component.extend(SlotsMixin, WithListeners, {
|
|||
this._super(...arguments);
|
||||
this.searchable = this.container.searchable(this.type);
|
||||
this.form = this.formContainer.form(this.type);
|
||||
this.form.clear({ Datacenter: this.dc });
|
||||
this.form.clear({ Datacenter: this.dc, Namespace: this.nspace });
|
||||
},
|
||||
options: computed('selectedOptions.[]', 'allOptions.[]', function() {
|
||||
// It's not massively important here that we are defaulting `items` and
|
||||
|
@ -52,7 +52,7 @@ export default Component.extend(SlotsMixin, WithListeners, {
|
|||
});
|
||||
},
|
||||
reset: function() {
|
||||
this.form.clear({ Datacenter: this.dc });
|
||||
this.form.clear({ Datacenter: this.dc, Namespace: this.nspace });
|
||||
},
|
||||
open: function() {
|
||||
if (!get(this, 'allOptions.closed')) {
|
||||
|
|
|
@ -3,93 +3,12 @@ import { inject as service } from '@ember/service';
|
|||
import { set, get, computed } from '@ember/object';
|
||||
import { next } from '@ember/runloop';
|
||||
|
||||
const getNodesByType = function(nodes = {}, type) {
|
||||
return Object.values(nodes).filter(item => item.Type === type);
|
||||
};
|
||||
|
||||
const targetsToFailover = function(targets, a) {
|
||||
let type;
|
||||
const Targets = targets.map(function(b) {
|
||||
// TODO: this isn't going to work past namespace for services
|
||||
// with dots in the name
|
||||
const [aRev, bRev] = [a, b].map(item => item.split('.').reverse());
|
||||
const types = ['Datacenter', 'Namespace', 'Service', 'Subset'];
|
||||
return bRev.find(function(item, i) {
|
||||
const res = item !== aRev[i];
|
||||
if (res) {
|
||||
type = types[i];
|
||||
}
|
||||
return res;
|
||||
});
|
||||
});
|
||||
return {
|
||||
Type: type,
|
||||
Targets: Targets,
|
||||
};
|
||||
};
|
||||
const getNodeResolvers = function(nodes = {}) {
|
||||
const failovers = getFailovers(nodes);
|
||||
const resolvers = {};
|
||||
Object.keys(nodes).forEach(function(key) {
|
||||
const node = nodes[key];
|
||||
if (node.Type === 'resolver' && !failovers.includes(key.split(':').pop())) {
|
||||
resolvers[node.Name] = node;
|
||||
}
|
||||
});
|
||||
return resolvers;
|
||||
};
|
||||
|
||||
const getTargetResolvers = function(dc, nspace = 'default', targets = [], nodes = {}) {
|
||||
const resolvers = {};
|
||||
Object.values(targets).forEach(item => {
|
||||
let node = nodes[item.ID];
|
||||
if (node) {
|
||||
if (typeof resolvers[item.Service] === 'undefined') {
|
||||
resolvers[item.Service] = {
|
||||
ID: item.ID,
|
||||
Name: item.Service,
|
||||
Children: [],
|
||||
Failover: null,
|
||||
Redirect: null,
|
||||
};
|
||||
}
|
||||
const resolver = resolvers[item.Service];
|
||||
let failoverable = resolver;
|
||||
if (item.ServiceSubset) {
|
||||
failoverable = item;
|
||||
// TODO: Sometimes we have set the resolvers ID to the ID of the
|
||||
// subset this just shifts the subset of the front of the URL for the moment
|
||||
const temp = item.ID.split('.');
|
||||
temp.shift();
|
||||
resolver.ID = temp.join('.');
|
||||
resolver.Children.push(item);
|
||||
}
|
||||
if (typeof node.Resolver.Failover !== 'undefined') {
|
||||
// TODO: Figure out if we can get rid of this
|
||||
/* eslint ember/no-side-effects: "warn" */
|
||||
set(failoverable, 'Failover', targetsToFailover(node.Resolver.Failover.Targets, item.ID));
|
||||
} else {
|
||||
const res = targetsToFailover([node.Resolver.Target], `service.${nspace}.${dc}`);
|
||||
if (res.Type === 'Datacenter' || res.Type === 'Namespace') {
|
||||
resolver.Children.push(item);
|
||||
set(failoverable, 'Redirect', true);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
return Object.values(resolvers);
|
||||
};
|
||||
const getFailovers = function(nodes = {}) {
|
||||
const failovers = [];
|
||||
Object.values(nodes)
|
||||
.filter(item => item.Type === 'resolver')
|
||||
.forEach(function(item) {
|
||||
(get(item, 'Resolver.Failover.Targets') || []).forEach(failover => {
|
||||
failovers.push(failover);
|
||||
});
|
||||
});
|
||||
return failovers;
|
||||
};
|
||||
import {
|
||||
createRoute,
|
||||
getSplitters,
|
||||
getRoutes,
|
||||
getResolvers,
|
||||
} from 'consul-ui/utils/components/discovery-chain/index';
|
||||
|
||||
export default Component.extend({
|
||||
dom: service('dom'),
|
||||
|
@ -134,74 +53,33 @@ export default Component.extend({
|
|||
this.ticker.destroy(this);
|
||||
},
|
||||
splitters: computed('chain.Nodes', function() {
|
||||
return getNodesByType(get(this, 'chain.Nodes'), 'splitter').map(function(item) {
|
||||
set(item, 'ID', `splitter:${item.Name}`);
|
||||
return item;
|
||||
});
|
||||
return getSplitters(get(this, 'chain.Nodes'));
|
||||
}),
|
||||
routers: computed('chain.Nodes', function() {
|
||||
// Right now there should only ever be one 'Router'.
|
||||
return getNodesByType(get(this, 'chain.Nodes'), 'router');
|
||||
routes: computed('chain.Nodes', function() {
|
||||
return getRoutes(get(this, 'chain.Nodes'), this.dom.guid);
|
||||
}),
|
||||
routes: computed('chain', 'routers', function() {
|
||||
const routes = get(this, 'routers').reduce(function(prev, item) {
|
||||
return prev.concat(
|
||||
item.Routes.map(function(route, i) {
|
||||
return {
|
||||
...route,
|
||||
ID: `route:${item.Name}-${JSON.stringify(route.Definition.Match.HTTP)}`,
|
||||
};
|
||||
})
|
||||
);
|
||||
}, []);
|
||||
if (routes.length === 0) {
|
||||
let nextNode = `resolver:${this.chain.ServiceName}.${this.chain.Namespace}.${this.chain.Datacenter}`;
|
||||
const splitterID = `splitter:${this.chain.ServiceName}`;
|
||||
if (typeof this.chain.Nodes[splitterID] !== 'undefined') {
|
||||
nextNode = splitterID;
|
||||
}
|
||||
routes.push({
|
||||
Default: true,
|
||||
ID: `route:${this.chain.ServiceName}`,
|
||||
Name: this.chain.ServiceName,
|
||||
Definition: {
|
||||
Match: {
|
||||
HTTP: {
|
||||
PathPrefix: '/',
|
||||
},
|
||||
},
|
||||
},
|
||||
NextNode: nextNode,
|
||||
});
|
||||
}
|
||||
return routes;
|
||||
}),
|
||||
nodeResolvers: computed('chain.Nodes', function() {
|
||||
return getNodeResolvers(get(this, 'chain.Nodes'));
|
||||
}),
|
||||
resolvers: computed('nodeResolvers.[]', 'chain.Targets', function() {
|
||||
return getTargetResolvers(
|
||||
resolvers: computed('chain.{Nodes,Targets}', function() {
|
||||
return getResolvers(
|
||||
this.chain.Datacenter,
|
||||
this.chain.Namespace,
|
||||
get(this, 'chain.Targets'),
|
||||
this.nodeResolvers
|
||||
get(this, 'chain.Nodes')
|
||||
);
|
||||
}),
|
||||
graph: computed('chain.Nodes', function() {
|
||||
const graph = this.dataStructs.graph();
|
||||
Object.entries(get(this, 'chain.Nodes')).forEach(function([key, item]) {
|
||||
const router = this.chain.ServiceName;
|
||||
Object.entries(get(this, 'chain.Nodes')).forEach(([key, item]) => {
|
||||
switch (item.Type) {
|
||||
case 'splitter':
|
||||
item.Splits.forEach(function(splitter) {
|
||||
item.Splits.forEach(splitter => {
|
||||
graph.addLink(`splitter:${item.Name}`, splitter.NextNode);
|
||||
});
|
||||
break;
|
||||
case 'router':
|
||||
item.Routes.forEach(function(route, i) {
|
||||
graph.addLink(
|
||||
`route:${item.Name}-${JSON.stringify(route.Definition.Match.HTTP)}`,
|
||||
route.NextNode
|
||||
);
|
||||
item.Routes.forEach((route, i) => {
|
||||
route = createRoute(route, router, this.dom.guid);
|
||||
graph.addLink(route.ID, route.NextNode);
|
||||
});
|
||||
break;
|
||||
}
|
||||
|
@ -212,18 +90,15 @@ export default Component.extend({
|
|||
if (this.selectedId === '' || !this.dom.element(`#${this.selectedId}`)) {
|
||||
return {};
|
||||
}
|
||||
const getTypeFromId = function(id) {
|
||||
return id.split(':').shift();
|
||||
};
|
||||
const id = this.selectedId;
|
||||
const type = getTypeFromId(id);
|
||||
const type = id.split(':').shift();
|
||||
const nodes = [id];
|
||||
const edges = [];
|
||||
this.graph.forEachLinkedNode(id, (linkedNode, link) => {
|
||||
nodes.push(linkedNode.id);
|
||||
edges.push(`${link.fromId}>${link.toId}`);
|
||||
this.graph.forEachLinkedNode(linkedNode.id, (linkedNode, link) => {
|
||||
const nodeType = getTypeFromId(linkedNode.id);
|
||||
const nodeType = linkedNode.id.split(':').shift();
|
||||
if (type !== nodeType && type !== 'splitter' && nodeType !== 'splitter') {
|
||||
nodes.push(linkedNode.id);
|
||||
edges.push(`${link.fromId}>${link.toId}`);
|
||||
|
@ -248,6 +123,16 @@ export default Component.extend({
|
|||
// TODO: Figure out if we can remove this next
|
||||
next(() => {
|
||||
this._listeners.remove();
|
||||
this._listeners.add(this.dom.document(), {
|
||||
click: e => {
|
||||
// all route/splitter/resolver components currently
|
||||
// have classes that end in '-card'
|
||||
if (!this.dom.closest('[class$="-card"]', e.target)) {
|
||||
set(this, 'active', false);
|
||||
set(this, 'selectedId', '');
|
||||
}
|
||||
},
|
||||
});
|
||||
[...this.dom.elements('path.split', this.element)].forEach(item => {
|
||||
this._listeners.add(item, {
|
||||
mouseover: e => this.actions.showSplit.apply(this, [e]),
|
||||
|
|
|
@ -1,6 +1,32 @@
|
|||
/*eslint ember/closure-actions: "warn"*/
|
||||
import Component from '@ember/component';
|
||||
import { inject as service } from '@ember/service';
|
||||
import Slotted from 'block-slots';
|
||||
|
||||
export default Component.extend(Slotted, {
|
||||
tagName: '',
|
||||
dom: service('dom'),
|
||||
expanded: false,
|
||||
keyboardAccess: true,
|
||||
onchange: function() {},
|
||||
init: function() {
|
||||
this._super(...arguments);
|
||||
this.guid = this.dom.guid(this);
|
||||
},
|
||||
actions: {
|
||||
change: function(e) {
|
||||
if (!e.target.checked) {
|
||||
[...this.dom.elements(`[id^=popover-menu-${this.guid}]`)].forEach(function($item) {
|
||||
$item.checked = false;
|
||||
});
|
||||
}
|
||||
this.onchange(e);
|
||||
},
|
||||
// Temporary send here so we can send route actions
|
||||
// easily. It kind of makes sense that you'll want to perform
|
||||
// route actions from a popup menu for the moment
|
||||
send: function() {
|
||||
this.sendAction(...arguments);
|
||||
},
|
||||
},
|
||||
});
|
||||
|
|
|
@ -1,5 +1,14 @@
|
|||
import Component from '@ember/component';
|
||||
|
||||
const ENTER = 13;
|
||||
export default Component.extend({
|
||||
tagName: 'fieldset',
|
||||
tagName: '',
|
||||
keyboardAccess: false,
|
||||
actions: {
|
||||
keydown: function(e) {
|
||||
if (e.keyCode === ENTER) {
|
||||
e.target.dispatchEvent(new MouseEvent('click'));
|
||||
}
|
||||
},
|
||||
},
|
||||
});
|
||||
|
|
|
@ -1,7 +1,14 @@
|
|||
import Component from '@ember/component';
|
||||
|
||||
const ENTER = 13;
|
||||
export default Component.extend({
|
||||
name: 'tab',
|
||||
tagName: 'nav',
|
||||
classNames: ['tab-nav'],
|
||||
tagName: '',
|
||||
actions: {
|
||||
keydown: function(e) {
|
||||
if (e.keyCode === ENTER) {
|
||||
e.target.dispatchEvent(new MouseEvent('click'));
|
||||
}
|
||||
},
|
||||
},
|
||||
});
|
||||
|
|
|
@ -1,134 +1,38 @@
|
|||
import CollectionComponent from 'ember-collection/components/ember-collection';
|
||||
import needsRevalidate from 'ember-collection/utils/needs-revalidate';
|
||||
import identity from 'ember-collection/utils/identity';
|
||||
import Grid from 'ember-collection/layouts/grid';
|
||||
import SlotsMixin from 'block-slots';
|
||||
import WithResizing from 'consul-ui/mixins/with-resizing';
|
||||
import style from 'ember-computed-style';
|
||||
|
||||
import { inject as service } from '@ember/service';
|
||||
import { computed, get, set } from '@ember/object';
|
||||
/**
|
||||
* Heavily extended `ember-collection` component
|
||||
* This adds support for z-index calculations to enable
|
||||
* Popup menus to pop over either rows above or below
|
||||
* the popup.
|
||||
* Additionally adds calculations for figuring out what the height
|
||||
* of the tabular component should be depending on the other elements
|
||||
* in the page.
|
||||
* Currently everything is here together for clarity, but to be split up
|
||||
* in the future
|
||||
*/
|
||||
import CollectionComponent from 'ember-collection/components/ember-collection';
|
||||
import needsRevalidate from 'ember-collection/utils/needs-revalidate';
|
||||
import Grid from 'ember-collection/layouts/grid';
|
||||
import style from 'ember-computed-style';
|
||||
import SlotsMixin from 'block-slots';
|
||||
import WithResizing from 'consul-ui/mixins/with-resizing';
|
||||
|
||||
// need to copy Cell in wholesale as there is no way to import it
|
||||
// there is no change made to `Cell` here, its only here as its
|
||||
// private in `ember-collection`
|
||||
// TODO: separate both Cell and ZIndexedGrid out
|
||||
class Cell {
|
||||
constructor(key, item, index, style) {
|
||||
this.key = key;
|
||||
this.hidden = false;
|
||||
this.item = item;
|
||||
this.index = index;
|
||||
this.style = style;
|
||||
}
|
||||
}
|
||||
// this is an amount of rows in the table NOT items
|
||||
// unlikely to have 10000 DOM rows ever :)
|
||||
const maxZIndex = 10000;
|
||||
// Adds z-index styling to the default Grid
|
||||
class ZIndexedGrid extends Grid {
|
||||
formatItemStyle(index, w, h, checked) {
|
||||
let style = super.formatItemStyle(index, w, h);
|
||||
// count backwards from maxZIndex
|
||||
let zIndex = maxZIndex - index;
|
||||
// apart from the row that contains an opened dropdown menu
|
||||
// this one should be highest z-index, so use max plus 1
|
||||
if (checked == index) {
|
||||
zIndex = maxZIndex + 1;
|
||||
}
|
||||
style += 'z-index: ' + zIndex;
|
||||
return style;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* The tabular-collection can contain 'actions' the UI for which
|
||||
* uses dropdown 'action groups', so a group of different actions.
|
||||
* State makes use of native HTML state using radiogroups
|
||||
* to ensure that only a single dropdown can be open at one time.
|
||||
* Therefore we listen to change events to do anything extra when
|
||||
* a dropdown is opened (the change function is bound to the instance of
|
||||
* the `tabular-component` on init, hoisted here for visibility)
|
||||
*
|
||||
* The extra functionality we have here is to detect whether the opened
|
||||
* dropdown menu would be cut off or not if it 'dropped down'.
|
||||
* If it would be cut off we use CSS to 'drop it up' instead.
|
||||
* We also set this row to have the max z-index here, and mark this
|
||||
* row as the 'checked row' for when a scroll/grid re-calculation is
|
||||
* performed
|
||||
*/
|
||||
const change = function(e) {
|
||||
if (e instanceof MouseEvent) {
|
||||
return;
|
||||
}
|
||||
// TODO: Why am I getting a jQuery event here?!
|
||||
if (e instanceof Event) {
|
||||
const value = e.currentTarget.value;
|
||||
if (value != get(this, 'checked')) {
|
||||
set(this, 'checked', value);
|
||||
// 'actions_close' would mean that all menus have been closed
|
||||
// therefore we don't need to calculate
|
||||
if (e.currentTarget.getAttribute('id') !== 'actions_close') {
|
||||
const dom = get(this, 'dom');
|
||||
const formatItemStyle = Grid.prototype.formatItemStyle;
|
||||
|
||||
const $tr = dom.closest('tr', e.currentTarget);
|
||||
const $group = dom.sibling(e.currentTarget, 'div');
|
||||
const groupRect = $group.getBoundingClientRect();
|
||||
const groupBottom = groupRect.top + $group.clientHeight;
|
||||
|
||||
const $footer = dom.element('footer[role="contentinfo"]');
|
||||
const footerRect = $footer.getBoundingClientRect();
|
||||
const footerTop = footerRect.top;
|
||||
|
||||
if (groupBottom > footerTop) {
|
||||
$group.classList.add('above');
|
||||
} else {
|
||||
$group.classList.remove('above');
|
||||
}
|
||||
$tr.style.zIndex = maxZIndex + 1;
|
||||
}
|
||||
} else {
|
||||
set(this, 'checked', null);
|
||||
}
|
||||
} else if (e.detail && e.detail.index) {
|
||||
if (e.detail.confirming) {
|
||||
this.confirming.push(e.detail.index);
|
||||
} else {
|
||||
const pos = this.confirming.indexOf(e.detail.index);
|
||||
if (pos !== -1) {
|
||||
this.confirming.splice(pos, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
export default CollectionComponent.extend(SlotsMixin, WithResizing, {
|
||||
tagName: 'table',
|
||||
classNames: ['dom-recycling'],
|
||||
classNameBindings: ['hasActions'],
|
||||
attributeBindings: ['style'],
|
||||
dom: service('dom'),
|
||||
style: style('getStyle'),
|
||||
width: 1150,
|
||||
rowHeight: 50,
|
||||
maxHeight: 500,
|
||||
style: style('getStyle'),
|
||||
checked: null,
|
||||
hasCaption: false,
|
||||
dom: service('dom'),
|
||||
init: function() {
|
||||
this._super(...arguments);
|
||||
this.change = change.bind(this);
|
||||
this.confirming = [];
|
||||
// TODO: The row height should auto calculate properly from the CSS
|
||||
this['cell-layout'] = new ZIndexedGrid(get(this, 'width'), get(this, 'rowHeight'));
|
||||
const o = this;
|
||||
this['cell-layout'] = new Grid(get(this, 'width'), get(this, 'rowHeight'));
|
||||
this['cell-layout'].formatItemStyle = function(itemIndex) {
|
||||
let style = formatItemStyle.apply(this, arguments);
|
||||
if (o.checked === itemIndex) {
|
||||
style = `${style};z-index: 1`;
|
||||
}
|
||||
return style;
|
||||
};
|
||||
},
|
||||
getStyle: computed('rowHeight', '_items', 'maxRows', 'maxHeight', function() {
|
||||
const maxRows = get(this, 'rows');
|
||||
|
@ -144,17 +48,24 @@ export default CollectionComponent.extend(SlotsMixin, WithResizing, {
|
|||
}),
|
||||
resize: function(e) {
|
||||
const $tbody = this.element;
|
||||
const dom = get(this, 'dom');
|
||||
const $appContent = dom.element('main > div');
|
||||
const $appContent = this.dom.element('main > div');
|
||||
if ($appContent) {
|
||||
const border = 1;
|
||||
const rect = $tbody.getBoundingClientRect();
|
||||
const $footer = dom.element('footer[role="contentinfo"]');
|
||||
const $footer = this.dom.element('footer[role="contentinfo"]');
|
||||
const space = rect.top + $footer.clientHeight + border;
|
||||
const height = e.detail.height - space;
|
||||
this.set('maxHeight', Math.max(0, height));
|
||||
// TODO: The row height should auto calculate properly from the CSS
|
||||
this['cell-layout'] = new ZIndexedGrid($appContent.clientWidth, get(this, 'rowHeight'));
|
||||
this['cell-layout'] = new Grid($appContent.clientWidth, get(this, 'rowHeight'));
|
||||
const o = this;
|
||||
this['cell-layout'].formatItemStyle = function(itemIndex) {
|
||||
let style = formatItemStyle.apply(this, arguments);
|
||||
if (o.checked === itemIndex) {
|
||||
style = `${style};z-index: 1`;
|
||||
}
|
||||
return style;
|
||||
};
|
||||
this.updateItems();
|
||||
this.updateScrollPosition();
|
||||
}
|
||||
|
@ -176,113 +87,39 @@ export default CollectionComponent.extend(SlotsMixin, WithResizing, {
|
|||
needsRevalidate(this);
|
||||
}
|
||||
},
|
||||
// need to overwrite this completely so I can pass through the checked index
|
||||
// unfortunately the nicest way I could think to do this is to copy this in wholesale
|
||||
// to add an extra argument for `formatItemStyle` in 3 places
|
||||
// tradeoff between changing as little code as possible in the original code
|
||||
updateCells: function() {
|
||||
if (!this._items) {
|
||||
return;
|
||||
}
|
||||
const numItems = get(this._items, 'length');
|
||||
if (this._cellLayout.length !== numItems) {
|
||||
this._cellLayout.length = numItems;
|
||||
}
|
||||
|
||||
var priorMap = this._cellMap;
|
||||
var cellMap = Object.create(null);
|
||||
|
||||
var index = this._cellLayout.indexAt(
|
||||
this._scrollLeft,
|
||||
this._scrollTop,
|
||||
this._clientWidth,
|
||||
this._clientHeight
|
||||
);
|
||||
var count = this._cellLayout.count(
|
||||
this._scrollLeft,
|
||||
this._scrollTop,
|
||||
this._clientWidth,
|
||||
this._clientHeight
|
||||
);
|
||||
var items = this._items;
|
||||
var bufferBefore = Math.min(index, this._buffer);
|
||||
index -= bufferBefore;
|
||||
count += bufferBefore;
|
||||
count = Math.min(count + this._buffer, get(items, 'length') - index);
|
||||
var i, style, itemIndex, itemKey, cell;
|
||||
|
||||
var newItems = [];
|
||||
|
||||
for (i = 0; i < count; i++) {
|
||||
itemIndex = index + i;
|
||||
itemKey = identity(items.objectAt(itemIndex));
|
||||
if (priorMap) {
|
||||
cell = priorMap[itemKey];
|
||||
}
|
||||
if (cell) {
|
||||
// additional `checked` argument
|
||||
style = this._cellLayout.formatItemStyle(
|
||||
itemIndex,
|
||||
this._clientWidth,
|
||||
this._clientHeight,
|
||||
this.checked
|
||||
);
|
||||
set(cell, 'style', style);
|
||||
set(cell, 'hidden', false);
|
||||
set(cell, 'key', itemKey);
|
||||
cellMap[itemKey] = cell;
|
||||
} else {
|
||||
newItems.push(itemIndex);
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < this._cells.length; i++) {
|
||||
cell = this._cells[i];
|
||||
if (!cellMap[cell.key]) {
|
||||
if (newItems.length) {
|
||||
itemIndex = newItems.pop();
|
||||
let item = items.objectAt(itemIndex);
|
||||
itemKey = identity(item);
|
||||
// additional `checked` argument
|
||||
style = this._cellLayout.formatItemStyle(
|
||||
itemIndex,
|
||||
this._clientWidth,
|
||||
this._clientHeight,
|
||||
this.checked
|
||||
);
|
||||
set(cell, 'style', style);
|
||||
set(cell, 'key', itemKey);
|
||||
set(cell, 'index', itemIndex);
|
||||
set(cell, 'item', item);
|
||||
set(cell, 'hidden', false);
|
||||
cellMap[itemKey] = cell;
|
||||
} else {
|
||||
set(cell, 'hidden', true);
|
||||
set(cell, 'style', 'height: 0; display: none;');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < newItems.length; i++) {
|
||||
itemIndex = newItems[i];
|
||||
let item = items.objectAt(itemIndex);
|
||||
itemKey = identity(item);
|
||||
// additional `checked` argument
|
||||
style = this._cellLayout.formatItemStyle(
|
||||
itemIndex,
|
||||
this._clientWidth,
|
||||
this._clientHeight,
|
||||
this.checked
|
||||
);
|
||||
cell = new Cell(itemKey, item, itemIndex, style);
|
||||
cellMap[itemKey] = cell;
|
||||
this._cells.pushObject(cell);
|
||||
}
|
||||
this._cellMap = cellMap;
|
||||
},
|
||||
actions: {
|
||||
click: function(e) {
|
||||
return get(this, 'dom').clickFirstAnchor(e);
|
||||
return this.dom.clickFirstAnchor(e);
|
||||
},
|
||||
change: function(index, e = {}) {
|
||||
if (typeof index !== 'string') {
|
||||
return;
|
||||
}
|
||||
if (this.$tr) {
|
||||
this.$tr.style.zIndex = null;
|
||||
}
|
||||
if (e.target.checked && index != get(this, 'checked')) {
|
||||
set(this, 'checked', parseInt(index));
|
||||
const target = e.target;
|
||||
const $tr = this.dom.closest('tr', target);
|
||||
const $group = this.dom.sibling(target, 'div');
|
||||
const groupRect = $group.getBoundingClientRect();
|
||||
const groupBottom = groupRect.top + $group.clientHeight;
|
||||
|
||||
const $footer = this.dom.element('footer[role="contentinfo"]');
|
||||
const footerRect = $footer.getBoundingClientRect();
|
||||
const footerTop = footerRect.top;
|
||||
|
||||
if (groupBottom > footerTop) {
|
||||
$group.classList.add('above');
|
||||
} else {
|
||||
$group.classList.remove('above');
|
||||
}
|
||||
$tr.style.zIndex = 1;
|
||||
this.$tr = $tr;
|
||||
} else {
|
||||
set(this, 'checked', null);
|
||||
}
|
||||
},
|
||||
},
|
||||
});
|
||||
|
|
|
@ -12,6 +12,7 @@ export default Component.extend({
|
|||
tagName: '',
|
||||
// TODO: reserved for the moment but we don't need it yet
|
||||
onblur: null,
|
||||
checked: false,
|
||||
onchange: function() {},
|
||||
init: function() {
|
||||
this._super(...arguments);
|
||||
|
@ -32,6 +33,11 @@ export default Component.extend({
|
|||
click: function(e) {
|
||||
e.preventDefault();
|
||||
this.input.checked = !this.input.checked;
|
||||
// manually dispatched mouse events have a detail = 0
|
||||
// real mouse events have the number of click counts
|
||||
if (e.detail !== 0) {
|
||||
e.target.blur();
|
||||
}
|
||||
this.actions.change.apply(this, [e]);
|
||||
},
|
||||
change: function(e) {
|
||||
|
@ -40,15 +46,19 @@ export default Component.extend({
|
|||
this._listeners.remove();
|
||||
this._listeners.add(this.dom.document(), 'click', e => {
|
||||
if (this.dom.isOutside(this.label, e.target)) {
|
||||
this.input.checked = false;
|
||||
// TODO: This should be an event
|
||||
this.onchange(this.input.checked);
|
||||
this._listeners.remove();
|
||||
if (this.dom.isOutside(this.label.nextElementSibling, e.target)) {
|
||||
if (this.input.checked) {
|
||||
this.input.checked = false;
|
||||
// TODO: This should be an event
|
||||
this.onchange({ target: this.input });
|
||||
}
|
||||
this._listeners.remove();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
// TODO: This should be an event
|
||||
this.onchange(this.input.checked);
|
||||
this.onchange({ target: this.input });
|
||||
},
|
||||
},
|
||||
});
|
||||
|
|
|
@ -1,61 +1,3 @@
|
|||
import _config from './config/environment';
|
||||
const doc = document;
|
||||
const getDevEnvVars = function() {
|
||||
return doc.cookie
|
||||
.split(';')
|
||||
.filter(item => item !== '')
|
||||
.map(item => item.trim().split('='));
|
||||
};
|
||||
const getUserEnvVar = function(str) {
|
||||
return window.localStorage.getItem(str);
|
||||
};
|
||||
// TODO: Look at `services/client` for pulling
|
||||
// HTTP headers in here so we can let things be controlled
|
||||
// via HTTP proxies, for example turning off blocking
|
||||
// queries if its a busy cluster
|
||||
// const getOperatorEnvVars = function() {}
|
||||
|
||||
// TODO: Not necessarily here but the entire app should
|
||||
// use the `env` export not the `default` one
|
||||
// but we might also change the name of this file, so wait for that first
|
||||
export const env = function(str) {
|
||||
let user = null;
|
||||
switch (str) {
|
||||
case 'CONSUL_UI_DISABLE_REALTIME':
|
||||
case 'CONSUL_UI_DISABLE_ANCHOR_SELECTION':
|
||||
case 'CONSUL_UI_REALTIME_RUNNER':
|
||||
user = getUserEnvVar(str);
|
||||
break;
|
||||
}
|
||||
// We use null here instead of an undefined check
|
||||
// as localStorage will return null if not set
|
||||
return user !== null ? user : _config[str];
|
||||
};
|
||||
export const config = function(key) {
|
||||
let $;
|
||||
switch (_config.environment) {
|
||||
case 'development':
|
||||
case 'staging':
|
||||
case 'test':
|
||||
$ = getDevEnvVars().reduce(function(prev, [key, value]) {
|
||||
const val = !!JSON.parse(String(value).toLowerCase());
|
||||
switch (key) {
|
||||
case 'CONSUL_ACLS_ENABLE':
|
||||
prev['CONSUL_ACLS_ENABLED'] = val;
|
||||
break;
|
||||
case 'CONSUL_NSPACES_ENABLE':
|
||||
prev['CONSUL_NSPACES_ENABLED'] = val;
|
||||
break;
|
||||
default:
|
||||
prev[key] = value;
|
||||
}
|
||||
return prev;
|
||||
}, {});
|
||||
if (typeof $[key] !== 'undefined') {
|
||||
return $[key];
|
||||
}
|
||||
break;
|
||||
}
|
||||
return _config[key];
|
||||
};
|
||||
export default env;
|
||||
import config from './config/environment';
|
||||
import getEnvironment from './utils/get-environment';
|
||||
export const env = getEnvironment(config, window, document);
|
||||
|
|
|
@ -1,9 +1,6 @@
|
|||
import { helper } from '@ember/component/helper';
|
||||
import { config } from 'consul-ui/env';
|
||||
// TODO: env actually uses config values not env values
|
||||
// see `app/env` for the renaming TODO's also
|
||||
export function env([name, def = ''], hash) {
|
||||
return config(name) != null ? config(name) : def;
|
||||
}
|
||||
|
||||
export default helper(env);
|
||||
import { env } from 'consul-ui/env';
|
||||
export default helper(function([name, def = ''], hash) {
|
||||
const val = env(name);
|
||||
return val != null ? val : def;
|
||||
});
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<!DOCTYPE html>
|
||||
<html class="{{content-for "root-class"}}">
|
||||
<html lang="en" class="{{content-for "root-class"}}">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
|
|
|
@ -1,15 +0,0 @@
|
|||
const scripts = document.getElementsByTagName('script');
|
||||
const current = scripts[scripts.length - 1];
|
||||
|
||||
export function initialize(application) {
|
||||
const Client = application.resolveRegistration('service:client/http');
|
||||
Client.reopen({
|
||||
isCurrent: function(src) {
|
||||
return current.src === src;
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export default {
|
||||
initialize,
|
||||
};
|
|
@ -1,5 +1,5 @@
|
|||
import Route from '@ember/routing/route';
|
||||
import { config } from 'consul-ui/env';
|
||||
import { env } from 'consul-ui/env';
|
||||
|
||||
import { routes } from 'consul-ui/router';
|
||||
import flat from 'flat';
|
||||
|
@ -26,7 +26,7 @@ Route.reopen(
|
|||
return prev;
|
||||
}, {})
|
||||
);
|
||||
if (config('CONSUL_NSPACES_ENABLED')) {
|
||||
if (env('CONSUL_NSPACES_ENABLED')) {
|
||||
const dotRe = /\./g;
|
||||
initialize = function(container) {
|
||||
const all = Object.keys(flat(routes))
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
import env, { config } from 'consul-ui/env';
|
||||
import { env } from 'consul-ui/env';
|
||||
|
||||
export function initialize(container) {
|
||||
if (env('CONSUL_UI_DISABLE_REALTIME')) {
|
||||
return;
|
||||
}
|
||||
['node', 'coordinate', 'session', 'service', 'proxy', 'discovery-chain']
|
||||
.concat(config('CONSUL_NSPACES_ENABLED') ? ['nspace/enabled'] : [])
|
||||
.concat(env('CONSUL_NSPACES_ENABLED') ? ['nspace/enabled'] : [])
|
||||
.map(function(item) {
|
||||
// create repositories that return a promise resolving to an EventSource
|
||||
return {
|
||||
|
@ -79,7 +79,7 @@ export function initialize(container) {
|
|||
},
|
||||
])
|
||||
.concat(
|
||||
config('CONSUL_NSPACES_ENABLED')
|
||||
env('CONSUL_NSPACES_ENABLED')
|
||||
? [
|
||||
{
|
||||
route: 'dc/nspaces/index',
|
||||
|
@ -104,7 +104,7 @@ export function initialize(container) {
|
|||
// but hardcode this for the moment
|
||||
if (typeof definition.route !== 'undefined') {
|
||||
container.inject(`route:${definition.route}`, name, `service:${servicePath}`);
|
||||
if (config('CONSUL_NSPACES_ENABLED') && definition.route.startsWith('dc/')) {
|
||||
if (env('CONSUL_NSPACES_ENABLED') && definition.route.startsWith('dc/')) {
|
||||
container.inject(`route:nspace/${definition.route}`, name, `service:${servicePath}`);
|
||||
}
|
||||
} else {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { config } from 'consul-ui/env';
|
||||
import { env } from 'consul-ui/env';
|
||||
export function initialize(container) {
|
||||
if (config('CONSUL_NSPACES_ENABLED')) {
|
||||
if (env('CONSUL_NSPACES_ENABLED')) {
|
||||
['dc', 'settings', 'dc.intentions.edit', 'dc.intentions.create'].forEach(function(item) {
|
||||
container.inject(`route:${item}`, 'nspacesRepo', 'service:repository/nspace/enabled');
|
||||
container.inject(`route:nspace.${item}`, 'nspacesRepo', 'service:repository/nspace/enabled');
|
||||
|
@ -15,7 +15,7 @@ export function initialize(container) {
|
|||
// 1. Make it be about adding classes to the root dom node
|
||||
// 2. Make it be about config and things to do on initialization re: config
|
||||
// If we go with 1 then we need to move both this and the above nspaces class
|
||||
if (config('CONSUL_ACLS_ENABLED')) {
|
||||
if (env('CONSUL_ACLS_ENABLED')) {
|
||||
container
|
||||
.lookup('service:dom')
|
||||
.root()
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import env from 'consul-ui/env';
|
||||
import { env } from 'consul-ui/env';
|
||||
|
||||
const SECONDARY_BUTTON = 2;
|
||||
const isSelecting = function(win = window) {
|
||||
|
|
|
@ -8,12 +8,14 @@ export default Mixin.create(WithBlockingActions, {
|
|||
actions: {
|
||||
use: function(item) {
|
||||
return this.feedback.execute(() => {
|
||||
// old style legacy ACLs don't have AccessorIDs
|
||||
// therefore set it to null, this way the frontend knows
|
||||
// old style legacy ACLs don't have AccessorIDs or Namespaces
|
||||
// therefore set AccessorID to null, this way the frontend knows
|
||||
// to use legacy ACLs
|
||||
// set the Namespace to just use default
|
||||
return this.settings
|
||||
.persist({
|
||||
token: {
|
||||
Namespace: 'default',
|
||||
AccessorID: null,
|
||||
SecretID: get(item, 'ID'),
|
||||
},
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
import EmberRouter from '@ember/routing/router';
|
||||
import { config } from 'consul-ui/env';
|
||||
import { env } from 'consul-ui/env';
|
||||
import walk from 'consul-ui/utils/routing/walk';
|
||||
|
||||
const Router = EmberRouter.extend({
|
||||
location: config('locationType'),
|
||||
rootURL: config('rootURL'),
|
||||
location: env('locationType'),
|
||||
rootURL: env('rootURL'),
|
||||
});
|
||||
export const routes = {
|
||||
// Our parent datacenter resource sets the namespace
|
||||
|
@ -107,7 +107,7 @@ export const routes = {
|
|||
_options: { path: '/*path' },
|
||||
},
|
||||
};
|
||||
if (config('CONSUL_NSPACES_ENABLED')) {
|
||||
if (env('CONSUL_NSPACES_ENABLED')) {
|
||||
routes.dc.nspaces = {
|
||||
_options: { path: '/namespaces' },
|
||||
edit: {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import Route from '@ember/routing/route';
|
||||
import { get } from '@ember/object';
|
||||
import { config } from 'consul-ui/env';
|
||||
import { env } from 'consul-ui/env';
|
||||
import { inject as service } from '@ember/service';
|
||||
import WithBlockingActions from 'consul-ui/mixins/with-blocking-actions';
|
||||
export default Route.extend(WithBlockingActions, {
|
||||
|
@ -16,9 +16,9 @@ export default Route.extend(WithBlockingActions, {
|
|||
return this.settings
|
||||
.persist({
|
||||
token: {
|
||||
Namespace: get(item, 'Namespace'),
|
||||
AccessorID: get(item, 'AccessorID'),
|
||||
SecretID: secret,
|
||||
Namespace: get(item, 'Namespace'),
|
||||
},
|
||||
})
|
||||
.then(item => {
|
||||
|
@ -32,9 +32,9 @@ export default Route.extend(WithBlockingActions, {
|
|||
return false;
|
||||
});
|
||||
} else {
|
||||
// TODO: Ideally we wouldn't need to use config() at a route level
|
||||
// TODO: Ideally we wouldn't need to use env() at a route level
|
||||
// transitionTo should probably remove it instead if NSPACES aren't enabled
|
||||
if (config('CONSUL_NSPACES_ENABLED') && get(item, 'token.Namespace') !== nspace) {
|
||||
if (env('CONSUL_NSPACES_ENABLED') && get(item, 'token.Namespace') !== nspace) {
|
||||
let routeName = this.router.currentRouteName;
|
||||
if (!routeName.startsWith('nspace')) {
|
||||
routeName = `nspace.${routeName}`;
|
||||
|
|
|
@ -15,7 +15,7 @@ export default Route.extend(WithIntentionActions, {
|
|||
},
|
||||
model: function(params) {
|
||||
const dc = this.modelFor('dc').dc.Name;
|
||||
const nspace = this.modelFor('nspace').nspace.substr(1);
|
||||
const nspace = '*';
|
||||
this.item = this.repo.create({
|
||||
Datacenter: dc,
|
||||
});
|
||||
|
|
|
@ -3,6 +3,7 @@ import Service, { inject as service } from '@ember/service';
|
|||
import { get, set } from '@ember/object';
|
||||
import { Promise } from 'rsvp';
|
||||
|
||||
import { env } from 'consul-ui/env';
|
||||
import getObjectPool from 'consul-ui/utils/get-object-pool';
|
||||
import Request from 'consul-ui/utils/http/request';
|
||||
import createURL from 'consul-ui/utils/createURL';
|
||||
|
@ -41,31 +42,7 @@ export default Service.extend({
|
|||
settings: service('settings'),
|
||||
init: function() {
|
||||
this._super(...arguments);
|
||||
let protocol = 'http/1.1';
|
||||
try {
|
||||
protocol = performance.getEntriesByType('resource').find(item => {
|
||||
// isCurrent is added in initializers/client and is used
|
||||
// to ensure we use the consul-ui.js src to sniff what the protocol
|
||||
// is. Based on the assumption that whereever this script is it's
|
||||
// likely to be the same as the xmlhttprequests
|
||||
return item.initiatorType === 'script' && this.isCurrent(item.name);
|
||||
}).nextHopProtocol;
|
||||
} catch (e) {
|
||||
// pass through
|
||||
}
|
||||
let maxConnections;
|
||||
// http/2, http2+QUIC/39 and SPDY don't have connection limits
|
||||
switch (true) {
|
||||
case protocol.indexOf('h2') === 0:
|
||||
case protocol.indexOf('hq') === 0:
|
||||
case protocol.indexOf('spdy') === 0:
|
||||
break;
|
||||
default:
|
||||
// generally 6 are available
|
||||
// reserve 1 for traffic that we can't manage
|
||||
maxConnections = 5;
|
||||
break;
|
||||
}
|
||||
const maxConnections = env('CONSUL_HTTP_MAX_CONNECTIONS');
|
||||
set(this, 'connections', getObjectPool(dispose, maxConnections));
|
||||
if (typeof maxConnections !== 'undefined') {
|
||||
set(this, 'maxConnections', maxConnections);
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { inject as service } from '@ember/service';
|
||||
import { get } from '@ember/object';
|
||||
import { config } from 'consul-ui/env';
|
||||
import { env } from 'consul-ui/env';
|
||||
import RepositoryService from 'consul-ui/services/repository';
|
||||
|
||||
const modelName = 'nspace';
|
||||
|
@ -18,7 +18,7 @@ export default RepositoryService.extend({
|
|||
return this.store.query(this.getModelName(), query);
|
||||
},
|
||||
authorize: function(dc, nspace) {
|
||||
if (!config('CONSUL_ACLS_ENABLED')) {
|
||||
if (!env('CONSUL_ACLS_ENABLED')) {
|
||||
return Promise.resolve([
|
||||
{
|
||||
Resource: 'operator',
|
||||
|
|
|
@ -34,6 +34,14 @@
|
|||
padding-top: calc(0.4em - 1px) !important;
|
||||
padding-bottom: calc(0.4em - 1px) !important;
|
||||
}
|
||||
%copy-button:empty {
|
||||
padding: 5px !important;
|
||||
margin-right: 0;
|
||||
margin-top: -5px;
|
||||
}
|
||||
%copy-button:not(:empty)::before {
|
||||
margin-right: 10px;
|
||||
}
|
||||
%internal-button {
|
||||
padding: 0.9em 1em;
|
||||
text-align: center;
|
||||
|
|
|
@ -15,15 +15,11 @@
|
|||
min-height: 17px;
|
||||
}
|
||||
%copy-button::before {
|
||||
@extend %with-copy-action-icon;
|
||||
@extend %as-pseudo;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
position: absolute;
|
||||
left: 12px;
|
||||
@extend %with-copy-action-mask, %as-pseudo;
|
||||
background-color: $gray-500;
|
||||
}
|
||||
%copy-button:not(:empty) {
|
||||
padding-left: 38px !important;
|
||||
%copy-button:not(:empty)::before {
|
||||
margin-right: 10px;
|
||||
}
|
||||
%primary-button,
|
||||
%secondary-button,
|
||||
|
@ -44,7 +40,8 @@
|
|||
color: $color-action;
|
||||
background-color: $gray-050;
|
||||
}
|
||||
%copy-button:disabled {
|
||||
%copy-button:hover::before {
|
||||
background-color: $blue-500;
|
||||
}
|
||||
%copy-button:active {
|
||||
background-color: $gray-200;
|
||||
|
|
|
@ -12,3 +12,9 @@
|
|||
%confirmation-alert > ul > li > * {
|
||||
width: 100%;
|
||||
}
|
||||
%confirmation-alert.warning header::before {
|
||||
margin-right: 5px;
|
||||
}
|
||||
%confirmation-alert.warning header {
|
||||
margin-bottom: 0.5em;
|
||||
}
|
||||
|
|
|
@ -24,3 +24,14 @@
|
|||
%confirmation-alert > ul > .dangerous > *:focus {
|
||||
@extend %frame-red-700;
|
||||
}
|
||||
%confirmation-alert.warning header {
|
||||
color: $orange-700;
|
||||
}
|
||||
%confirmation-alert.warning header::before {
|
||||
@extend %with-alert-triangle-mask, %as-pseudo;
|
||||
background-color: $yellow-500;
|
||||
margin-right: 5px;
|
||||
}
|
||||
%confirmation-alert p {
|
||||
color: $black;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
%display-toggle-siblings,
|
||||
%display-toggle-siblings ~ *:not(.animating):not(label) {
|
||||
display: none;
|
||||
}
|
||||
%display-toggle-siblings:checked ~ *:not(label) {
|
||||
display: block;
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
%display-toggle-siblings ~ label {
|
||||
cursor: pointer;
|
||||
}
|
|
@ -1,9 +1,16 @@
|
|||
@import './skin';
|
||||
@import './layout';
|
||||
|
||||
%menu-panel > ul > li > * {
|
||||
%menu-panel [role='separator'] {
|
||||
@extend %menu-panel-separator;
|
||||
}
|
||||
%menu-panel > div {
|
||||
@extend %menu-panel-header;
|
||||
}
|
||||
// %menu-panel > ul > li > *:not(div),
|
||||
%menu-panel [role='menuitem'] {
|
||||
@extend %internal-button;
|
||||
}
|
||||
%menu-panel > ul > li.dangerous > * {
|
||||
%menu-panel > ul > li.dangerous > *:not(div) {
|
||||
@extend %internal-button-dangerous;
|
||||
}
|
||||
|
|
|
@ -2,6 +2,30 @@
|
|||
position: absolute;
|
||||
overflow: hidden;
|
||||
}
|
||||
%menu-panel [type='checkbox'] {
|
||||
display: none;
|
||||
}
|
||||
%menu-panel [type='checkbox'] ~ * {
|
||||
transition: transform 150ms, min-height 150ms, max-height 150ms;
|
||||
min-height: 0;
|
||||
}
|
||||
%menu-panel [type='checkbox']:checked ~ * {
|
||||
transform: translateX(calc(-100% - 10px));
|
||||
/* this needs to autocalculate */
|
||||
/* or be hardcoded */
|
||||
/* min-height: 143px; */
|
||||
}
|
||||
%menu-panel-sub-panel {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: calc(100% + 10px);
|
||||
}
|
||||
%menu-panel > ul > li > div[role='menu'] {
|
||||
@extend %menu-panel-sub-panel;
|
||||
}
|
||||
%menu-panel > ul > li > *:not(div[role='menu']) {
|
||||
position: relative;
|
||||
}
|
||||
%menu-panel:not(.left) {
|
||||
right: 0px;
|
||||
}
|
||||
|
@ -12,7 +36,7 @@
|
|||
top: 28px;
|
||||
}
|
||||
%menu-panel.above {
|
||||
bottom: 28px;
|
||||
bottom: 42px;
|
||||
}
|
||||
%menu-panel > ul {
|
||||
margin: 0;
|
||||
|
@ -26,3 +50,37 @@
|
|||
%menu-panel > ul > li > * {
|
||||
text-align: left !important;
|
||||
}
|
||||
%menu-panel-separator {
|
||||
padding-top: 0.35em;
|
||||
}
|
||||
%menu-panel-separator:not(:first-child) {
|
||||
margin-top: 0.35em;
|
||||
}
|
||||
%menu-panel-separator:not(:empty) {
|
||||
padding-left: 1em;
|
||||
padding-right: 1em;
|
||||
padding-bottom: 0.1em;
|
||||
}
|
||||
%menu-panel-header {
|
||||
padding: 10px;
|
||||
padding-left: 36px;
|
||||
}
|
||||
%menu-panel .is-active > *::after {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
margin-top: -8px;
|
||||
right: 10px;
|
||||
}
|
||||
%menu-panel-header::before {
|
||||
position: absolute;
|
||||
left: 15px;
|
||||
top: calc(10px + 0.1em);
|
||||
}
|
||||
%menu-panel-header {
|
||||
max-width: fit-content;
|
||||
}
|
||||
@supports not (max-width: fit-content) {
|
||||
%menu-panel-header {
|
||||
max-width: 200px;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,3 +10,23 @@
|
|||
%menu-panel > ul > li {
|
||||
list-style-type: none;
|
||||
}
|
||||
%menu-panel-separator {
|
||||
text-transform: uppercase;
|
||||
color: $gray-400;
|
||||
}
|
||||
%menu-panel-header + ul,
|
||||
%menu-panel-separator:not(:first-child) {
|
||||
border-top: $decor-border-100;
|
||||
border-color: $gray-300;
|
||||
}
|
||||
%menu-panel-header {
|
||||
background-color: $gray-050;
|
||||
}
|
||||
%menu-panel-header::before {
|
||||
@extend %with-info-circle-fill-color-icon, %as-pseudo;
|
||||
font-size: 1.1em;
|
||||
}
|
||||
%menu-panel .is-active > *::after {
|
||||
@extend %with-check-plain-mask, %as-pseudo;
|
||||
background-color: $magenta-600;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
@import '../display-toggle/index';
|
||||
@import '../toggle-button/index';
|
||||
@import '../menu-panel/index';
|
||||
@import '../confirmation-alert/index';
|
||||
@import './skin';
|
||||
@import './layout';
|
|
@ -0,0 +1,25 @@
|
|||
%more-popover-menu {
|
||||
@extend %display-toggle-siblings;
|
||||
}
|
||||
%more-popover-menu + label > * {
|
||||
@extend %toggle-button;
|
||||
}
|
||||
%more-popover-menu-panel {
|
||||
@extend %menu-panel;
|
||||
width: 192px;
|
||||
}
|
||||
%more-popover-menu + label + div {
|
||||
@extend %more-popover-menu-panel;
|
||||
}
|
||||
%more-popover-menu-panel:not(.above) {
|
||||
top: 38px;
|
||||
}
|
||||
%more-popover-menu-panel:not(.left) {
|
||||
right: 10px;
|
||||
}
|
||||
%more-popover-menu-panel li [role='menu'] {
|
||||
display: none;
|
||||
}
|
||||
%more-popover-menu-panel [id$='-']:first-child:checked ~ ul label[for$='-'] + [role='menu'] {
|
||||
display: block;
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
%more-popover-menu + label > *::after {
|
||||
@extend %with-more-horizontal-icon, %as-pseudo;
|
||||
opacity: 0.7;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
%more-popover-menu + label > * {
|
||||
font-size: 0;
|
||||
}
|
|
@ -42,7 +42,6 @@ $edit-svg: url('data:image/svg+xml;charset=UTF-8,<svg viewBox="0 0 24 24" xmlns=
|
|||
$exit-svg: url('data:image/svg+xml;charset=UTF-8,<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M5 19V5h5.944V3H5a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14c1.1 0 2-.9 2-2v-5.98h-2V19H5zm9-16v2h3.59l-9.83 9.83 1.41 1.41L19 6.41V10h2V3h-7z" fill="%23000"/></svg>');
|
||||
$expand-less-svg: url('data:image/svg+xml;charset=UTF-8,<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M5 13l2.3 2.3-2.89 2.87 1.42 1.42L8.7 16.7 11 19v-6H5zm14-2l-2.3-2.3 2.89-2.87-1.42-1.42L15.3 7.3 13 5v6h6z" fill="%23000"/></svg>');
|
||||
$expand-more-svg: url('data:image/svg+xml;charset=UTF-8,<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M14 4l2.3 2.3-2.89 2.87 1.42 1.42L17.7 7.7 20 10V4h-6zm-4 16l-2.3-2.3 2.89-2.87-1.42-1.42L6.3 16.3 4 14v6h6z" fill="%23000"/></svg>');
|
||||
$eye-svg: url('data:image/svg+xml;charset=UTF-8,<svg width="16" height="8" viewBox="0 0 16 8" xmlns="http://www.w3.org/2000/svg"><path d="M10.229 1.301A3.493 3.493 0 0 1 11.5 4a3.493 3.493 0 0 1-1.271 2.699c1.547-.431 3.008-1.326 4.393-2.699-1.385-1.373-2.846-2.268-4.393-2.699zM5.771 6.7A3.493 3.493 0 0 1 4.5 4c0-1.086.495-2.057 1.271-2.699C4.224 1.732 2.763 2.627 1.378 4c1.385 1.373 2.846 2.268 4.393 2.699zM8 8C5.054 8 2.388 6.667 0 4c2.388-2.667 5.054-4 8-4 2.946 0 5.612 1.333 8 4-2.388 2.667-5.054 4-8 4zm.965-4.25a1 1 0 1 0 .07-2 1 1 0 0 0-.07 2z" fill="%237C8896"/></svg>');
|
||||
$file-fill-svg: url('data:image/svg+xml;charset=UTF-8,<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M18.714 7.714L14.43 3.43h-10v17.14h14.285V7.714zM20.143 7v13.571c0 .786-.643 1.429-1.429 1.429H4.43A1.433 1.433 0 0 1 3 20.571V3.43C3 2.643 3.643 2 4.429 2h10.714l5 5zM5.857 17.714h10v-1.428h-10v1.428zm0-2.857h10V13.43h-10v1.428zm0-2.857h10v-1.429h-10V12zm0-4.286h5.714V6.286H5.857v1.428z" fill="%23000"/></svg>');
|
||||
$file-outline-svg: url('data:image/svg+xml;charset=UTF-8,<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M20.143 7v13.571c0 .786-.643 1.429-1.429 1.429H4.43A1.433 1.433 0 0 1 3 20.571V3.43C3 2.643 3.643 2 4.429 2h10.714l5 5zm-1.429.714H14.43V3.43h-10v17.14h14.285V7.714z" fill="%23000"/></svg>');
|
||||
$filter-svg: url('data:image/svg+xml;charset=UTF-8,<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M10 18h4v-2h-4v2zM3 6v2h18V6H3zm3 7h12v-2H6v2z" fill="%23000"/></svg>');
|
||||
|
|
|
@ -17,6 +17,10 @@
|
|||
@extend %with-icon;
|
||||
background-image: $alert-triangle-svg;
|
||||
}
|
||||
%with-alert-triangle-mask {
|
||||
@extend %with-mask;
|
||||
mask-image: $alert-triangle-svg;
|
||||
}
|
||||
|
||||
%with-arrow-down-icon {
|
||||
@extend %with-icon;
|
||||
|
@ -98,6 +102,11 @@
|
|||
background-image: $check-circle-fill-svg;
|
||||
}
|
||||
|
||||
%with-check-circle-fill-mask {
|
||||
@extend %with-mask;
|
||||
mask-image: $check-circle-fill-svg;
|
||||
}
|
||||
|
||||
%with-check-circle-outline-icon {
|
||||
@extend %with-icon;
|
||||
background-image: $check-circle-outline-svg;
|
||||
|
@ -178,6 +187,10 @@
|
|||
@extend %with-icon;
|
||||
background-image: $copy-action-svg;
|
||||
}
|
||||
%with-copy-action-mask {
|
||||
@extend %with-mask;
|
||||
mask-image: $copy-action-svg;
|
||||
}
|
||||
|
||||
%with-copy-success-icon {
|
||||
@extend %with-icon;
|
||||
|
@ -229,11 +242,6 @@
|
|||
background-image: $expand-more-svg;
|
||||
}
|
||||
|
||||
%with-eye-icon {
|
||||
@extend %with-icon;
|
||||
background-image: $eye-svg;
|
||||
}
|
||||
|
||||
%with-file-fill-icon {
|
||||
@extend %with-icon;
|
||||
background-image: $file-fill-svg;
|
||||
|
@ -269,6 +277,11 @@
|
|||
background-image: $folder-outline-svg;
|
||||
}
|
||||
|
||||
%with-folder-outline-mask {
|
||||
@extend %with-mask;
|
||||
mask-image: $folder-outline-svg;
|
||||
}
|
||||
|
||||
%with-git-branch-icon {
|
||||
@extend %with-icon;
|
||||
background-image: $git-branch-svg;
|
||||
|
@ -289,6 +302,10 @@
|
|||
background-image: $hashicorp-logo-svg;
|
||||
}
|
||||
|
||||
%with-hashicorp-logo-mask {
|
||||
@extend %with-mask;
|
||||
mask-image: $hashicorp-logo-svg;
|
||||
}
|
||||
%with-help-circle-fill-icon {
|
||||
@extend %with-icon;
|
||||
background-image: $help-circle-fill-svg;
|
||||
|
@ -473,6 +490,11 @@
|
|||
background-image: $star-fill-svg;
|
||||
}
|
||||
|
||||
%with-star-fill-mask {
|
||||
@extend %with-mask;
|
||||
mask-image: $star-fill-svg;
|
||||
}
|
||||
|
||||
%with-star-outline-icon {
|
||||
@extend %with-icon;
|
||||
background-image: $star-outline-svg;
|
||||
|
@ -572,8 +594,16 @@
|
|||
@extend %with-icon;
|
||||
background-image: $visibility-hide-svg;
|
||||
}
|
||||
%with-visibility-hide-mask {
|
||||
@extend %with-mask;
|
||||
mask-image: $visibility-hide-svg;
|
||||
}
|
||||
|
||||
%with-visibility-show-icon {
|
||||
@extend %with-icon;
|
||||
background-image: $visibility-show-svg;
|
||||
}
|
||||
%with-visibility-show-mask {
|
||||
@extend %with-mask;
|
||||
mask-image: $visibility-show-svg;
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
@import '../base/components/anchors/index';
|
||||
a[rel*='external'] {
|
||||
@extend %with-exit;
|
||||
a[rel*='external']::after {
|
||||
@extend %with-exit-icon, %as-pseudo;
|
||||
margin-left: 8px;
|
||||
}
|
||||
%main-content label a[rel*='help'] {
|
||||
color: $gray-400;
|
||||
|
|
|
@ -1,6 +1,32 @@
|
|||
@import './app-view/index';
|
||||
@import './filter-bar/index';
|
||||
@import '../base/components/buttons/index';
|
||||
|
||||
@import '../base/components/popover-menu/index';
|
||||
|
||||
%app-view-header .actions > [type='checkbox'] {
|
||||
@extend %more-popover-menu;
|
||||
}
|
||||
%more-popover-menu-panel [type='checkbox']:checked ~ * {
|
||||
/* this needs to autocalculate */
|
||||
min-height: 143px;
|
||||
max-height: 143px;
|
||||
}
|
||||
%more-popover-menu-panel [id$='logout']:checked ~ * {
|
||||
/* this needs to autocalculate */
|
||||
min-height: 163px;
|
||||
max-height: 163px;
|
||||
}
|
||||
%more-popover-menu-panel [id$='delete']:checked ~ ul label[for$='delete'] + [role='menu'],
|
||||
%more-popover-menu-panel [id$='logout']:checked ~ ul label[for$='logout'] + [role='menu'],
|
||||
%more-popover-menu-panel [id$='use']:checked ~ ul label[for$='use'] + [role='menu'] {
|
||||
display: block;
|
||||
}
|
||||
%app-view-header .actions label + div {
|
||||
// We need this extra to allow tooltips to show
|
||||
overflow: visible !important;
|
||||
}
|
||||
|
||||
main {
|
||||
@extend %app-view;
|
||||
}
|
||||
|
|
|
@ -1,3 +1,10 @@
|
|||
/*TODO: Rename this to %app-view-brand-icon or similar */
|
||||
%with-external-source-icon {
|
||||
background-repeat: no-repeat;
|
||||
background-size: contain;
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
}
|
||||
%app-view h2,
|
||||
%app-view fieldset {
|
||||
border-bottom: $decor-border-200;
|
||||
|
|
|
@ -1,7 +1,4 @@
|
|||
@import '../base/components/buttons/index';
|
||||
%copy-button {
|
||||
@extend %with-clipboard;
|
||||
}
|
||||
button[type='submit'],
|
||||
a.type-create {
|
||||
@extend %primary-button;
|
||||
|
|
|
@ -1,9 +0,0 @@
|
|||
%checkbox-group span {
|
||||
display: inline-block;
|
||||
margin-left: 10px;
|
||||
min-width: 50px;
|
||||
}
|
||||
%checkbox-group label {
|
||||
margin-right: 10px;
|
||||
white-space: nowrap;
|
||||
}
|
|
@ -1,3 +0,0 @@
|
|||
%checkbox-group label {
|
||||
cursor: pointer;
|
||||
}
|
|
@ -1,2 +0,0 @@
|
|||
@import './skin';
|
||||
@import './layout';
|
|
@ -1,62 +0,0 @@
|
|||
%form-row {
|
||||
margin-bottom: 1.4em;
|
||||
}
|
||||
%form-element {
|
||||
@extend %form-row;
|
||||
}
|
||||
%form-element,
|
||||
%form-element > em,
|
||||
%form-element > span,
|
||||
%form-element textarea {
|
||||
display: block;
|
||||
}
|
||||
%form-element a {
|
||||
display: inline;
|
||||
}
|
||||
%form-element > em > code {
|
||||
display: inline-block;
|
||||
}
|
||||
%form-element [type='text'],
|
||||
%form-element [type='password'] {
|
||||
display: inline-flex;
|
||||
justify-content: flex-start;
|
||||
}
|
||||
%form-element textarea {
|
||||
resize: vertical;
|
||||
}
|
||||
%form-element > span {
|
||||
margin-bottom: 0.5em;
|
||||
}
|
||||
%form-element > span + em {
|
||||
margin-top: -0.5em;
|
||||
margin-bottom: 0.5em;
|
||||
}
|
||||
%form-element textarea {
|
||||
max-width: 100%;
|
||||
min-width: 100%;
|
||||
min-height: 70px;
|
||||
padding: 0.625em 15px;
|
||||
}
|
||||
%form-element [type='text'],
|
||||
%form-element [type='password'] {
|
||||
max-width: 100%;
|
||||
width: 100%;
|
||||
height: 2.25em;
|
||||
}
|
||||
%form-element > em {
|
||||
margin-top: 2px;
|
||||
}
|
||||
%form-element > em > code {
|
||||
padding: 0 4px;
|
||||
}
|
||||
%form-element [type='text'],
|
||||
%form-element [type='password'],
|
||||
%form-element textarea {
|
||||
padding: 17px 13px;
|
||||
}
|
||||
%form-element textarea {
|
||||
padding: 6px 13px;
|
||||
}
|
||||
%form-element > span {
|
||||
margin-bottom: 0.4em !important;
|
||||
}
|
|
@ -1,61 +0,0 @@
|
|||
%form-element > strong {
|
||||
@extend %with-error;
|
||||
}
|
||||
%form-element-error > input,
|
||||
%form-element-error > textarea {
|
||||
border: $decor-border-100;
|
||||
}
|
||||
%form-element [type='text'],
|
||||
%form-element [type='password'],
|
||||
%form-element textarea {
|
||||
-moz-appearance: none;
|
||||
-webkit-appearance: none;
|
||||
box-shadow: inset 0 4px 1px rgba(0, 0, 0, 0.06);
|
||||
border-radius: $decor-radius-100;
|
||||
border: $decor-border-100;
|
||||
}
|
||||
%form-element [type='text']:focus,
|
||||
%form-element [type='password']:focus,
|
||||
%form-element textarea:focus {
|
||||
outline: none;
|
||||
}
|
||||
%form-element > em > code {
|
||||
border-radius: $decor-radius-100;
|
||||
}
|
||||
%form-element-error > input {
|
||||
border-color: $color-failure !important;
|
||||
}
|
||||
%form-element > strong {
|
||||
color: $color-failure;
|
||||
}
|
||||
%form-element > em {
|
||||
color: $gray-400;
|
||||
}
|
||||
%form-element > em > code {
|
||||
background-color: $gray-200;
|
||||
color: $magenta-600;
|
||||
border-radius: $decor-radius-100;
|
||||
}
|
||||
%form-element > span {
|
||||
color: $black;
|
||||
}
|
||||
%form-element [type='text'],
|
||||
%form-element [type='password'],
|
||||
%form-element textarea {
|
||||
color: $gray-500;
|
||||
}
|
||||
%form-element [type='text'],
|
||||
%form-element [type='password'],
|
||||
%form-element textarea {
|
||||
border-color: $gray-300;
|
||||
}
|
||||
%form-element [type='text']:hover,
|
||||
%form-element [type='password']:hover,
|
||||
%form-element textarea:hover {
|
||||
border-color: $gray-500;
|
||||
}
|
||||
%form-element [type='text']:focus,
|
||||
%form-element [type='password']:focus,
|
||||
%form-element textarea:focus {
|
||||
border-color: $blue-500;
|
||||
}
|
|
@ -1,5 +1,4 @@
|
|||
@import './freetext-filter/index';
|
||||
@import './icons/index';
|
||||
.freetext-filter {
|
||||
@extend %freetext-filter;
|
||||
}
|
||||
|
|
|
@ -1,10 +1,9 @@
|
|||
@import './healthcheck-info/index';
|
||||
@import './icons/index';
|
||||
tr .healthcheck-info {
|
||||
%table tr .healthcheck-info {
|
||||
@extend %healthcheck-info;
|
||||
}
|
||||
// TODO: Look at why we can't have the zeros in the healthcheck-info
|
||||
td span.zero {
|
||||
%table td span.zero {
|
||||
@extend %with-minus-square-fill-color-icon;
|
||||
background-position: left center;
|
||||
display: block;
|
||||
|
|
|
@ -13,6 +13,11 @@
|
|||
}
|
||||
|
||||
%stats-card-icon:last-child {
|
||||
/* TODO: In order to get rid of our colored star */
|
||||
/* this needs to use a %mask, and we are already using */
|
||||
/* our before/after psuedo elements for the tooltip */
|
||||
/* so this will need reworking slighly before we can */
|
||||
/* get rid of our hardcoded magenta star icon */
|
||||
@extend %with-star-icon;
|
||||
}
|
||||
%stats-card header > .zero {
|
||||
|
|
|
@ -1,140 +0,0 @@
|
|||
/*TODO: The old pseudo-icon was to specific */
|
||||
/* make a temporary one with the -- prefix */
|
||||
/* to make it more reusable temporarily */
|
||||
%bg-icon {
|
||||
background-repeat: no-repeat;
|
||||
background-position: center;
|
||||
}
|
||||
%--pseudo-icon {
|
||||
display: inline-block;
|
||||
content: '';
|
||||
visibility: visible;
|
||||
background-repeat: no-repeat;
|
||||
background-position: center;
|
||||
}
|
||||
%pseudo-icon-bg-img {
|
||||
@extend %--pseudo-icon;
|
||||
position: relative;
|
||||
background-size: contain;
|
||||
background-color: transparent;
|
||||
}
|
||||
%pseudo-icon-css {
|
||||
@extend %--pseudo-icon;
|
||||
display: block;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
width: 1em;
|
||||
height: 1em;
|
||||
margin-top: -0.6em;
|
||||
background-color: currentColor;
|
||||
}
|
||||
/* %pseudo-icon-mask, %pseudo-icon-overlay ?*/
|
||||
%pseudo-icon {
|
||||
@extend %pseudo-icon-css;
|
||||
}
|
||||
%with-external-source-icon {
|
||||
background-repeat: no-repeat;
|
||||
background-size: contain;
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
}
|
||||
%with-folder {
|
||||
text-indent: 30px;
|
||||
}
|
||||
%with-hashicorp,
|
||||
%with-folder,
|
||||
%with-chevron,
|
||||
%with-clipboard {
|
||||
position: relative;
|
||||
}
|
||||
%with-chevron {
|
||||
padding-left: 10px;
|
||||
display: inline-block;
|
||||
}
|
||||
%with-hashicorp {
|
||||
background-color: $white;
|
||||
}
|
||||
%with-hashicorp::before {
|
||||
@extend %pseudo-icon;
|
||||
opacity: 0.45;
|
||||
background-image: $hashicorp-logo-svg;
|
||||
background-size: cover;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
left: -25px;
|
||||
margin-top: -10px;
|
||||
background-color: $color-transparent;
|
||||
}
|
||||
%with-clipboard {
|
||||
padding-left: 38px !important;
|
||||
}
|
||||
%with-clipboard::before {
|
||||
@extend %pseudo-icon;
|
||||
background-image: url('data:image/svg+xml;charset=UTF-8,<svg width="16" height="17" xmlns="http://www.w3.org/2000/svg"><g fill="%231563FF"><path d="M1.5 10.6v.156c0-.117-.043-.156-.033-.156H1.5zm0 0V1.5h8v2.97H11V1.344C11 .602 10.343 0 9.533 0H1.467C.657 0 0 .602 0 1.344v9.412c0 .742.657 1.344 1.467 1.344h2.995v-1.5H1.5zm-.033-9.1c-.01 0 .033-.04.033-.156V1.5h-.033zm8.033 0v-.156c0 .117.043.156.033.156H9.5zm0 0v-.156c0 .117.043.156.033.156H9.5zm0 0v2.97H11V1.344C11 .602 10.343 0 9.533 0H1.467C.657 0 0 .602 0 1.344v9.412c0 .742.657 1.344 1.467 1.344h2.995v-1.5H1.5V1.5h8zm-8 0h-.033c-.01 0 .033-.04.033-.156V1.5zm0 9.256c0-.117-.043-.156-.033-.156H1.5v.156z" fill-rule="nonzero"/><path d="M14.4 4.5H5.6c-.083 0-.1.016-.1-.033v10.266c0-.049.017-.033.1-.033h8.8c.083 0 .1-.016.1.033V4.467c0 .049-.017.033-.1.033zm0-1.5c.884 0 1.6.657 1.6 1.467v10.266c0 .81-.716 1.467-1.6 1.467H5.6c-.884 0-1.6-.657-1.6-1.467V4.467C4 3.657 4.716 3 5.6 3h8.8z" fill-rule="nonzero"/><path d="M2.5 8.25H10v1.154H2.5z"/><path d="M10.942 8.705l.001.001-2.827 2.828-.807-.808 2.02-2.02-2.02-2.021.807-.808 2.827 2.827v.001z"/></g></svg>');
|
||||
width: 16px;
|
||||
height: 17px;
|
||||
left: 12px;
|
||||
margin-top: -8px;
|
||||
background-color: $color-transparent;
|
||||
}
|
||||
%with-chevron::before {
|
||||
@extend %pseudo-icon;
|
||||
content: '❮';
|
||||
width: 6px;
|
||||
background-color: transparent;
|
||||
left: 0;
|
||||
font-size: 0.7rem;
|
||||
}
|
||||
%with-folder::before {
|
||||
@extend %pseudo-icon;
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
top: 50%;
|
||||
margin-top: -6px;
|
||||
left: 2px;
|
||||
background-image: url('data:image/svg+xml;charset=UTF-8,<svg width="14" height="13" xmlns="http://www.w3.org/2000/svg"><path d="M4.779 1H1.8c-.439 0-.8.37-.8.833v9.334c0 .463.361.833.8.833h10.4c.439 0 .8-.37.8-.833V3.833C13 3.37 12.639 3 12.2 3H6.35a.5.5 0 0 1-.42-.228L4.78 1z" stroke="%23BBC4D2" fill="none"/></svg>');
|
||||
background-color: $color-transparent;
|
||||
}
|
||||
%with-exit::after {
|
||||
@extend %pseudo-icon-bg-img;
|
||||
top: 3px;
|
||||
right: -8px;
|
||||
background-image: $exit-svg;
|
||||
background-color: $color-transparent;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
/*TODO: All chevrons need merging */
|
||||
%with-chevron-down::before {
|
||||
@extend %pseudo-icon-bg-img;
|
||||
background-image: $chevron-svg;
|
||||
width: 10px;
|
||||
height: 6px;
|
||||
}
|
||||
%with-star-before::before,
|
||||
%with-star-after::after {
|
||||
@extend %pseudo-icon-bg-img;
|
||||
background-image: $star-svg;
|
||||
width: 10px;
|
||||
height: 9px;
|
||||
}
|
||||
%with-star-before::before {
|
||||
padding-right: 12px;
|
||||
}
|
||||
%with-star-after::after {
|
||||
padding-left: 22px;
|
||||
}
|
||||
%with-star {
|
||||
@extend %with-star-before;
|
||||
}
|
||||
%with-eye::before {
|
||||
@extend %pseudo-icon-bg-img;
|
||||
background-image: $eye-svg;
|
||||
width: 16px;
|
||||
height: 8px;
|
||||
padding-right: 12px;
|
||||
}
|
||||
%with-tick {
|
||||
@extend %pseudo-icon;
|
||||
background-image: url('data:image/svg+xml;charset=UTF-8,<svg width="10" height="8" xmlns="http://www.w3.org/2000/svg"><path d="M8.95 0L10 .985 3.734 8 0 4.737l.924-1.11 2.688 2.349z" fill="%23FFF"/></svg>');
|
||||
}
|
|
@ -1,3 +1,4 @@
|
|||
@import './form-elements';
|
||||
@import './breadcrumbs';
|
||||
@import './anchors';
|
||||
@import './progress';
|
||||
|
@ -24,7 +25,6 @@
|
|||
@import './phrase-editor';
|
||||
@import './filter-bar';
|
||||
@import './tomography-graph';
|
||||
@import './action-group';
|
||||
@import './flash-message';
|
||||
@import './code-editor';
|
||||
@import './confirmation-dialog';
|
||||
|
|
|
@ -21,10 +21,3 @@
|
|||
%main-nav-horizontal-drop-nav {
|
||||
@extend %menu-panel;
|
||||
}
|
||||
/*TODO: %menu-panel? */
|
||||
%main-nav-horizontal-drop-nav [role='separator'] {
|
||||
@extend %main-nav-horizontal-drop-nav-separator;
|
||||
}
|
||||
%main-nav-horizontal-drop-nav > div {
|
||||
@extend %main-nav-horizontal-drop-nav-header;
|
||||
}
|
||||
|
|
|
@ -1,64 +1,27 @@
|
|||
%main-nav-horizontal > ul > li,
|
||||
%main-nav-horizontal-drop-nav a {
|
||||
position: relative;
|
||||
}
|
||||
%main-nav-horizontal [type='checkbox'] ~ div {
|
||||
display: none;
|
||||
}
|
||||
%main-nav-horizontal [type='checkbox']:checked ~ div {
|
||||
display: block;
|
||||
}
|
||||
%main-nav-horizontal [type='checkbox'] + label > * {
|
||||
/* less space as the chevron adds space */
|
||||
padding-right: 4px !important;
|
||||
}
|
||||
%main-nav-horizontal > ul > li {
|
||||
position: relative;
|
||||
}
|
||||
%main-nav-horizontal-action {
|
||||
display: block;
|
||||
padding: 5px 12px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
%main-nav-horizontal input + label > * {
|
||||
/* less space as the chevron adds space */
|
||||
padding-right: 4px !important;
|
||||
}
|
||||
%main-nav-horizontal-drop-nav {
|
||||
z-index: 400;
|
||||
/* TODO: We should probably make menu-panel default to left hand side*/
|
||||
left: 0;
|
||||
right: auto;
|
||||
}
|
||||
/* TODO: Revisit this once its part of menu-panel as its probably */
|
||||
/* a property of that */
|
||||
%main-nav-horizontal-drop-nav-separator:not(:empty) {
|
||||
padding-left: 1em;
|
||||
padding-right: 1em;
|
||||
padding-bottom: 0.1em;
|
||||
}
|
||||
%main-nav-horizontal-drop-nav-separator {
|
||||
padding-top: 0.35em;
|
||||
}
|
||||
%main-nav-horizontal-drop-nav-separator:not(:first-child) {
|
||||
margin-top: 0.35em;
|
||||
}
|
||||
%main-nav-horizontal-drop-nav-header {
|
||||
padding: 10px;
|
||||
padding-left: 36px;
|
||||
}
|
||||
%main-nav-horizontal-drop-nav-header::before {
|
||||
position: absolute;
|
||||
left: 15px;
|
||||
top: calc(10px + 0.1em);
|
||||
}
|
||||
%main-nav-horizontal-drop-nav-header {
|
||||
max-width: fit-content;
|
||||
}
|
||||
@supports not (max-width: fit-content) {
|
||||
%main-nav-horizontal-drop-nav-header {
|
||||
max-width: 200px;
|
||||
}
|
||||
}
|
||||
%main-nav-horizontal-drop-nav .is-active > *::after {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
margin-top: -8px;
|
||||
right: 10px;
|
||||
}
|
||||
@media #{$--lt-horizontal-nav} {
|
||||
%main-nav-horizontal-panel label span {
|
||||
visibility: visible !important;
|
||||
|
@ -120,9 +83,7 @@
|
|||
}
|
||||
%main-nav-horizontal-drop-nav {
|
||||
width: 180px;
|
||||
}
|
||||
%main-nav-horizontal-drop-nav-header {
|
||||
text-align: left;
|
||||
top: 38px !important;
|
||||
}
|
||||
%main-nav-horizontal input + label > * {
|
||||
right: -15px;
|
||||
|
@ -132,9 +93,6 @@
|
|||
padding-top: 15px;
|
||||
padding-bottom: 15px;
|
||||
}
|
||||
%main-nav-horizontal-drop-nav {
|
||||
top: 38px !important;
|
||||
}
|
||||
}
|
||||
@media #{$--horizontal-nav} {
|
||||
%main-nav-horizontal-panel {
|
||||
|
|
|
@ -35,14 +35,14 @@
|
|||
%main-nav-horizontal input {
|
||||
display: none;
|
||||
}
|
||||
%main-nav-horizontal input + label > *::after {
|
||||
%main-nav-horizontal [type='checkbox'] + label > *::after {
|
||||
@extend %as-pseudo;
|
||||
@extend %with-chevron-down-mask;
|
||||
position: relative;
|
||||
top: 2px;
|
||||
background-color: $white;
|
||||
}
|
||||
%main-nav-horizontal input:checked + label > *::after {
|
||||
%main-nav-horizontal [type='checkbox']:checked + label > *::after {
|
||||
@extend %with-chevron-up-mask;
|
||||
}
|
||||
%main-nav-horizontal-toggle-button span {
|
||||
|
@ -57,23 +57,3 @@
|
|||
}
|
||||
}
|
||||
/**/
|
||||
%main-nav-horizontal-drop-nav-header {
|
||||
background-color: $gray-050;
|
||||
}
|
||||
%main-nav-horizontal-drop-nav-separator {
|
||||
text-transform: uppercase;
|
||||
color: $gray-400;
|
||||
}
|
||||
%main-nav-horizontal-drop-nav .is-active > *::after {
|
||||
@extend %with-check-plain-mask, %as-pseudo;
|
||||
background-color: $magenta-600;
|
||||
}
|
||||
%main-nav-horizontal-drop-nav-header + ul,
|
||||
%main-nav-horizontal-drop-nav-separator:not(:first-child) {
|
||||
border-top: $decor-border-100;
|
||||
border-color: $gray-300;
|
||||
}
|
||||
%main-nav-horizontal-drop-nav-header::before {
|
||||
@extend %with-info-circle-fill-color-icon, %as-pseudo;
|
||||
font-size: 1.1em;
|
||||
}
|
||||
|
|
|
@ -1,2 +0,0 @@
|
|||
@import './skin';
|
||||
@import './layout';
|
|
@ -1,72 +0,0 @@
|
|||
%modal-dialog > div > div {
|
||||
@extend %modal-window;
|
||||
}
|
||||
%with-modal {
|
||||
overflow: hidden;
|
||||
}
|
||||
%modal-dialog {
|
||||
z-index: 500;
|
||||
position: fixed;
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
%modal-control,
|
||||
%modal-control + * {
|
||||
display: none;
|
||||
}
|
||||
%modal-control:checked + * {
|
||||
display: block;
|
||||
}
|
||||
%modal-dialog > label {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
%modal-dialog > div {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 100%;
|
||||
}
|
||||
%modal-window.overflowing {
|
||||
overflow: auto;
|
||||
height: 100%;
|
||||
}
|
||||
%modal-window {
|
||||
max-width: 855px;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
%modal-window > * {
|
||||
padding-left: 15px;
|
||||
padding-right: 15px;
|
||||
}
|
||||
%modal-window > div {
|
||||
overflow-y: auto;
|
||||
max-height: 80vh;
|
||||
padding: 20px 23px;
|
||||
}
|
||||
%modal-window > footer,
|
||||
%modal-window > header {
|
||||
padding-top: 12px;
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
%modal-window table {
|
||||
height: 150px !important;
|
||||
}
|
||||
%modal-window tbody {
|
||||
max-height: 100px;
|
||||
}
|
||||
%modal-window > header {
|
||||
position: relative;
|
||||
}
|
||||
%modal-window > header [for='modal_close'] {
|
||||
float: right;
|
||||
text-indent: -9000px;
|
||||
width: 23px;
|
||||
height: 23px;
|
||||
}
|
|
@ -1,35 +0,0 @@
|
|||
%modal-dialog > label {
|
||||
background-color: rgba($white, 0.9);
|
||||
}
|
||||
%modal-window {
|
||||
box-shadow: 2px 8px 8px 0 rgba($black, 0.1);
|
||||
}
|
||||
%modal-window {
|
||||
/*%frame-gray-000*/
|
||||
background-color: $white;
|
||||
border: $decor-border-100;
|
||||
border-color: $gray-300;
|
||||
}
|
||||
%modal-window > footer,
|
||||
%modal-window > header {
|
||||
@extend %frame-gray-800;
|
||||
}
|
||||
%modal-window > footer {
|
||||
border-top-width: 1px;
|
||||
}
|
||||
%modal-window > header {
|
||||
border-bottom-width: 1px;
|
||||
}
|
||||
|
||||
%modal-window > header [for='modal_close'] {
|
||||
@extend %bg-icon;
|
||||
background-image: $cancel-plain-svg;
|
||||
background-size: 80%;
|
||||
|
||||
cursor: pointer;
|
||||
/*%frame-gray-050??*/
|
||||
background-color: $gray-050;
|
||||
border: $decor-border-100;
|
||||
border-color: $gray-300;
|
||||
border-radius: $decor-radius-100;
|
||||
}
|
|
@ -1,5 +1,14 @@
|
|||
%footer > a:first-child {
|
||||
@extend %with-hashicorp;
|
||||
position: relative;
|
||||
}
|
||||
%footer > a:first-child::before {
|
||||
@extend %with-hashicorp-logo-mask, %as-pseudo;
|
||||
background-color: $gray-400;
|
||||
font-size: 1.4em;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
margin-top: -0.7em;
|
||||
left: -25px;
|
||||
}
|
||||
%footer {
|
||||
border-top: $decor-border-100;
|
||||
|
|
|
@ -1,7 +1,3 @@
|
|||
/* TODO: All of these folders should be self contained */
|
||||
/* figure out what the best way to deal with 'icons' is */
|
||||
/* most probably a centralized module */
|
||||
@import '../icons/index';
|
||||
@import './loader';
|
||||
@import './main-header-horizontal';
|
||||
@import '../main-nav-horizontal/index';
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
@import './layout';
|
|
@ -1,23 +0,0 @@
|
|||
%radio-group {
|
||||
overflow: hidden;
|
||||
}
|
||||
%radio-group label {
|
||||
float: left;
|
||||
}
|
||||
%radio-group label > span {
|
||||
float: right;
|
||||
}
|
||||
%radio-group {
|
||||
padding-left: 1px;
|
||||
}
|
||||
%radio-group label:not(:last-child) {
|
||||
margin-right: 25px;
|
||||
}
|
||||
%radio-group label > span {
|
||||
margin-left: 1em;
|
||||
margin-top: 0.2em;
|
||||
}
|
||||
%radio-group label,
|
||||
%radio-group label > span {
|
||||
margin-bottom: 0 !important;
|
||||
}
|
|
@ -3,9 +3,19 @@
|
|||
@extend %secret-button;
|
||||
}
|
||||
%secret-button {
|
||||
visibility: hidden;
|
||||
@extend %with-eye;
|
||||
position: relative;
|
||||
}
|
||||
%secret-button span {
|
||||
visibility: hidden;
|
||||
position: absolute;
|
||||
}
|
||||
%secret-button em {
|
||||
margin-left: 22px;
|
||||
}
|
||||
%secret-button span::before {
|
||||
@extend %with-visibility-show-mask, %as-pseudo;
|
||||
background-color: $gray-500;
|
||||
}
|
||||
%secret-button input:checked + span::before {
|
||||
@extend %with-visibility-hide-mask;
|
||||
}
|
||||
|
|
|
@ -4,20 +4,20 @@
|
|||
%secret-button input {
|
||||
display: none;
|
||||
}
|
||||
%secret-button input + em {
|
||||
%secret-button input ~ em {
|
||||
visibility: hidden;
|
||||
font-style: normal;
|
||||
}
|
||||
%secret-button input:checked + em {
|
||||
%secret-button input:checked ~ em {
|
||||
@extend %user-select-text;
|
||||
visibility: visible;
|
||||
cursor: auto;
|
||||
}
|
||||
%secret-button input + em::before {
|
||||
%secret-button input ~ em::before {
|
||||
display: inline;
|
||||
visibility: visible;
|
||||
content: '■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■';
|
||||
}
|
||||
%secret-button input:checked + em::before {
|
||||
%secret-button input:checked ~ em::before {
|
||||
display: none;
|
||||
}
|
||||
|
|
|
@ -1,9 +1,21 @@
|
|||
@import './icons/index';
|
||||
@import '../base/components/table/index';
|
||||
@import '../base/components/popover-menu/index';
|
||||
table {
|
||||
@extend %table, %table-flex;
|
||||
}
|
||||
|
||||
%table-actions > [type='checkbox'] {
|
||||
@extend %more-popover-menu;
|
||||
}
|
||||
%table-actions .confirmation-alert {
|
||||
@extend %confirmation-alert;
|
||||
}
|
||||
%table-actions > [type='checkbox'] + label {
|
||||
position: absolute;
|
||||
top: 8px;
|
||||
right: 15px;
|
||||
}
|
||||
|
||||
html.template-service.template-list td:first-child a span,
|
||||
html.template-node.template-show #services td:first-child a span,
|
||||
html.template-service.template-show #instances td:first-child a span {
|
||||
|
@ -19,8 +31,11 @@ html.template-service.template-list main th:first-child {
|
|||
text-indent: 28px;
|
||||
}
|
||||
|
||||
td.folder {
|
||||
@extend %with-folder;
|
||||
td.folder::before {
|
||||
@extend %with-folder-outline-mask, %as-pseudo;
|
||||
background-color: $gray-300;
|
||||
margin-top: 1px;
|
||||
margin-right: 5px;
|
||||
}
|
||||
table:not(.sessions) tbody tr {
|
||||
cursor: pointer;
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
%tabular-details-toggle-button {
|
||||
@extend %with-chevron-down;
|
||||
%tabular-details-toggle-button::before {
|
||||
@extend %with-chevron-down-icon, %as-pseudo;
|
||||
}
|
||||
%tabular-details td:only-child {
|
||||
cursor: default;
|
||||
|
|
|
@ -1,2 +0,0 @@
|
|||
@import './skin';
|
||||
@import './layout';
|
|
@ -1,36 +0,0 @@
|
|||
%toggle label {
|
||||
position: relative;
|
||||
}
|
||||
%toggle input {
|
||||
display: none;
|
||||
}
|
||||
%toggle label span {
|
||||
display: inline-block;
|
||||
padding-left: 34px;
|
||||
}
|
||||
%toggle label span::before,
|
||||
%toggle label span::after {
|
||||
position: absolute;
|
||||
display: block;
|
||||
content: '';
|
||||
top: 50%;
|
||||
}
|
||||
%toggle label span::before {
|
||||
left: 0px;
|
||||
width: 24px;
|
||||
height: 12px;
|
||||
margin-top: -5px;
|
||||
}
|
||||
%toggle label span::after {
|
||||
margin-top: -3px;
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
}
|
||||
%toggle label input:checked + span::after,
|
||||
%toggle-negative label input + span::after {
|
||||
left: 14px;
|
||||
}
|
||||
%toggle label span::after,
|
||||
%toggle-negative label input:checked + span::after {
|
||||
left: 2px;
|
||||
}
|
|
@ -1,30 +0,0 @@
|
|||
/* TODO: Maybe move this to reset? */
|
||||
%toggle label span {
|
||||
cursor: pointer;
|
||||
}
|
||||
%toggle label span::after {
|
||||
border-radius: $decor-radius-full;
|
||||
}
|
||||
%toggle label span::before {
|
||||
border-radius: 7px;
|
||||
}
|
||||
%toggle-negative {
|
||||
border: 0;
|
||||
}
|
||||
%toggle.type-negative {
|
||||
@extend %toggle-negative;
|
||||
}
|
||||
%toggle label span {
|
||||
color: $gray-900;
|
||||
}
|
||||
%toggle label span::after {
|
||||
background-color: $white;
|
||||
}
|
||||
%toggle label input:checked + span::before,
|
||||
%toggle-negative label input + span::before {
|
||||
background-color: $blue-500;
|
||||
}
|
||||
%toggle label span::before,
|
||||
%toggle-negative label input:checked + span::before {
|
||||
background-color: $gray-300;
|
||||
}
|
|
@ -19,6 +19,7 @@ fieldset > header,
|
|||
%app-view-content div > dl > dt,
|
||||
%table caption,
|
||||
%tbody-th,
|
||||
%confirmation-alert header,
|
||||
%form-element > span {
|
||||
@extend %h4;
|
||||
}
|
||||
|
@ -50,7 +51,7 @@ pre code,
|
|||
%form-element-label,
|
||||
%stats-card header a span,
|
||||
%footer,
|
||||
%main-nav-horizontal-drop-nav > div,
|
||||
%menu-panel-header,
|
||||
%app-view h1 span.kind-proxy {
|
||||
@extend %p2;
|
||||
}
|
||||
|
@ -59,7 +60,7 @@ pre code,
|
|||
%main-content p,
|
||||
%app-view > div.disabled > div,
|
||||
%form-element-note,
|
||||
%main-nav-horizontal-drop-nav-separator,
|
||||
%menu-panel-separator,
|
||||
%form-element-error > strong {
|
||||
@extend %p3;
|
||||
}
|
||||
|
@ -81,7 +82,7 @@ pre code,
|
|||
%splitter-card > header {
|
||||
font-weight: $typo-weight-bold;
|
||||
}
|
||||
%main-nav-horizontal-drop-nav-separator {
|
||||
%menu-panel-separator {
|
||||
font-weight: $typo-weight-medium;
|
||||
}
|
||||
/**/
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
td a.is-management {
|
||||
@extend %with-star-after;
|
||||
}
|
||||
td a.is-management::after {
|
||||
@extend %with-star-fill-mask, %as-pseudo;
|
||||
background-color: $magenta-600;
|
||||
height: 16px;
|
||||
top: 2px;
|
||||
padding-left: 32px;
|
||||
|
|
|
@ -5,14 +5,12 @@
|
|||
float: right;
|
||||
}
|
||||
%token-yours {
|
||||
text-indent: 20px;
|
||||
color: $blue-500;
|
||||
padding-left: 15px;
|
||||
}
|
||||
%token-yours::after {
|
||||
@extend %with-tick;
|
||||
border-radius: 100%;
|
||||
%token-yours::before {
|
||||
@extend %with-check-circle-fill-mask, %as-pseudo;
|
||||
background-color: $blue-500;
|
||||
margin-right: 5px;
|
||||
}
|
||||
.me ~ :nth-last-child(2) {
|
||||
@extend %token-yours;
|
||||
|
|
|
@ -2,10 +2,9 @@
|
|||
position: relative;
|
||||
}
|
||||
#urls_service span::after {
|
||||
@extend %with-tick;
|
||||
background-color: $green-500;
|
||||
border-radius: 100%;
|
||||
top: 13px;
|
||||
@extend %with-check-circle-fill-color-icon, %as-pseudo;
|
||||
position: absolute;
|
||||
top: 3px;
|
||||
right: 0;
|
||||
}
|
||||
#urls_service span::after {
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
{{else}}
|
||||
{{#hashicorp-consul id="wrapper" permissions=permissions dcs=dcs dc=dc nspaces=nspaces nspace=nspace}}
|
||||
{{#app-view class="loading show"}}
|
||||
{{#block-slot 'content'}}
|
||||
{{#block-slot name='content'}}
|
||||
{{partial 'consul-loading'}}
|
||||
{{/block-slot}}
|
||||
{{/app-view}}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
{{!<form>}}
|
||||
{{freetext-filter searchable=searchable value=search placeholder="Search by name/token"}}
|
||||
{{radio-group name="type" value=type items=filters onchange=(action onchange)}}
|
||||
{{radio-group keyboardAccess=true name="type" value=type items=filters onchange=(action onchange)}}
|
||||
{{!</form>}}
|
||||
|
|
|
@ -9,28 +9,28 @@
|
|||
<strong>
|
||||
{{component.flashType}}!
|
||||
</strong>
|
||||
{{#yield-slot 'notification' (block-params (lowercase component.flashType) (lowercase flash.action) flash.item )}}{{yield}}{{/yield-slot}}
|
||||
{{#yield-slot name='notification' params=(block-params (lowercase component.flashType) (lowercase flash.action) flash.item )}}{{yield}}{{/yield-slot}}
|
||||
</p>
|
||||
{{/flash-message}}
|
||||
{{/each}}
|
||||
<div>
|
||||
<div class="actions">
|
||||
{{#if authorized}}
|
||||
{{#yield-slot 'actions'}}{{yield}}{{/yield-slot}}
|
||||
{{#yield-slot name='actions'}}{{yield}}{{/yield-slot}}
|
||||
{{/if}}
|
||||
</div>
|
||||
|
||||
<div>
|
||||
{{#if authorized}}
|
||||
<nav aria-label="Breadcrumb">
|
||||
{{#yield-slot 'breadcrumbs'}}{{yield}}{{/yield-slot}}
|
||||
{{#yield-slot name='breadcrumbs'}}{{yield}}{{/yield-slot}}
|
||||
</nav>
|
||||
{{/if}}
|
||||
{{#yield-slot 'header'}}{{yield}}{{/yield-slot}}
|
||||
{{#yield-slot name='header'}}{{yield}}{{/yield-slot}}
|
||||
</div>
|
||||
</div>
|
||||
{{#if authorized}}
|
||||
{{#yield-slot 'toolbar'}}
|
||||
{{#yield-slot name='toolbar'}}
|
||||
<input type="checkbox" id="toolbar-toggle" />
|
||||
{{yield}}
|
||||
{{/yield-slot}}
|
||||
|
@ -42,11 +42,11 @@
|
|||
{{partial 'consul-loading'}}
|
||||
{{else}}
|
||||
{{#if (not enabled) }}
|
||||
{{#yield-slot 'disabled'}}{{yield}}{{/yield-slot}}
|
||||
{{#yield-slot name='disabled'}}{{yield}}{{/yield-slot}}
|
||||
{{else if (not authorized)}}
|
||||
{{#yield-slot 'authorization'}}{{yield}}{{/yield-slot}}
|
||||
{{#yield-slot name='authorization'}}{{yield}}{{/yield-slot}}
|
||||
{{else}}
|
||||
{{#yield-slot 'content'}}{{yield}}{{/yield-slot}}
|
||||
{{#yield-slot name='content'}}{{yield}}{{/yield-slot}}
|
||||
{{/if}}
|
||||
{{/if}}
|
||||
</div>
|
||||
|
|
|
@ -1 +1,8 @@
|
|||
{{yield (action 'change') (action 'keypress') (concat 'component-aria-menu-trigger-' guid) (concat 'component-aria-menu-menu-' guid) (if expanded 'true' undefined)}}
|
||||
{{yield
|
||||
(action 'change')
|
||||
(action 'keypress')
|
||||
(concat 'component-aria-menu-trigger-' guid)
|
||||
(concat 'component-aria-menu-menu-' guid)
|
||||
(if expanded 'true' undefined)
|
||||
(action 'keypressClick')
|
||||
}}
|
|
@ -1,6 +1,6 @@
|
|||
{{!<form>}}
|
||||
{{freetext-filter searchable=searchable value=search placeholder="Search by name"}}
|
||||
{{radio-group name="status" value=status items=(array
|
||||
{{radio-group keyboardAccess=true name="status" value=status items=(array
|
||||
(hash label='All (Any Status)' value='' )
|
||||
(hash label='Critical Checks' value='critical')
|
||||
(hash label='Warning Checks' value='warning')
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{{yield}}
|
||||
{{#if (gt items.length 0)}}
|
||||
{{#yield-slot 'set' (block-params items)}}{{yield}}{{/yield-slot}}
|
||||
{{#yield-slot name='set' params=(block-params items)}}{{yield}}{{/yield-slot}}
|
||||
{{else}}
|
||||
{{#yield-slot 'empty'}}{{yield}}{{/yield-slot}}
|
||||
{{#yield-slot name='empty'}}{{yield}}{{/yield-slot}}
|
||||
{{/if}}
|
|
@ -1,7 +1,7 @@
|
|||
{{yield}}
|
||||
{{#yield-slot 'create'}}{{yield}}{{/yield-slot}}
|
||||
{{#yield-slot name='create'}}{{yield}}{{/yield-slot}}
|
||||
<label class="type-text">
|
||||
<span>{{#yield-slot 'label'}}{{yield}}{{/yield-slot}}</span>
|
||||
<span>{{#yield-slot name='label'}}{{yield}}{{/yield-slot}}</span>
|
||||
{{#power-select
|
||||
onopen=(action 'open')
|
||||
search=(action 'search')
|
||||
|
@ -11,11 +11,11 @@
|
|||
searchPlaceholder=placeholder
|
||||
onchange=(action 'change' 'items[]' items) as |item|
|
||||
}}
|
||||
{{#yield-slot 'option' (block-params item)}}{{yield}}{{/yield-slot}}
|
||||
{{#yield-slot name='option' params=(block-params item)}}{{yield}}{{/yield-slot}}
|
||||
{{/power-select}}
|
||||
</label>
|
||||
{{#if (gt items.length 0)}}
|
||||
{{#yield-slot 'set'}}{{yield}}{{/yield-slot}}
|
||||
{{#yield-slot name='set'}}{{yield}}{{/yield-slot}}
|
||||
{{else}}
|
||||
|
||||
{{/if}}
|
|
@ -1,10 +1,10 @@
|
|||
{{yield}}
|
||||
{{#yield-slot 'action' (block-params confirm cancel)}}
|
||||
{{#yield-slot name='action' params=(block-params confirm cancel)}}
|
||||
{{#if (or permanent (not confirming))}}
|
||||
{{yield}}
|
||||
{{/if}}
|
||||
{{/yield-slot}}
|
||||
{{#yield-slot 'dialog' (block-params execute cancel message actionName)}}
|
||||
{{#yield-slot name='dialog' params=(block-params execute cancel message actionName)}}
|
||||
{{#if confirming }}
|
||||
{{yield}}
|
||||
{{/if}}
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
{{#feedback-dialog type='inline'}}
|
||||
{{#block-slot 'action' as |success error|}}
|
||||
{{#block-slot name='action' as |success error|}}
|
||||
{{#copy-button success=(action success) error=(action error) clipboardText=copy title=(concat 'Copy ' name ' to the clipboard')}}{{#if hasBlock }}{{yield}}{{else}}{{value}}{{/if}}{{/copy-button}}
|
||||
{{/block-slot}}
|
||||
{{#block-slot 'success' as |transition|}}
|
||||
{{#block-slot name='success' as |transition|}}
|
||||
<p class={{transition}}>
|
||||
Copied {{name}}!
|
||||
</p>
|
||||
{{/block-slot}}
|
||||
{{#block-slot 'error' as |transition|}}
|
||||
{{#block-slot name='error' as |transition|}}
|
||||
<p class={{transition}}>
|
||||
Sorry, something went wrong!
|
||||
</p>
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
{{yield}}
|
||||
{{#if (eq state 'success') }}
|
||||
{{#yield-slot 'success' (block-params transition)}}{{yield}}{{/yield-slot}}
|
||||
{{#yield-slot name='success' params=(block-params transition)}}{{yield}}{{/yield-slot}}
|
||||
{{else if (eq state 'error') }}
|
||||
{{#yield-slot 'error' (block-params transition)}}{{yield}}{{/yield-slot}}
|
||||
{{#yield-slot name='error' params=(block-params transition)}}{{yield}}{{/yield-slot}}
|
||||
{{/if}}
|
||||
{{#if (or permanent (eq state 'ready')) }}
|
||||
{{#yield-slot 'action' (block-params success error)}}{{yield}}{{message}}{{/yield-slot}}
|
||||
{{#yield-slot name='action' params=(block-params success error)}}{{yield}}{{message}}{{/yield-slot}}
|
||||
{{/if}}
|
|
@ -1,6 +1,6 @@
|
|||
<header role="banner" data-test-navigation>
|
||||
<a data-test-main-nav-logo href={{href-to 'index'}}><svg width="28" height="27" xmlns="http://www.w3.org/2000/svg"><path d="M13.284 16.178a2.876 2.876 0 1 1-.008-5.751 2.876 2.876 0 0 1 .008 5.75zm5.596-1.547a1.333 1.333 0 1 1 0-2.667 1.333 1.333 0 0 1 0 2.667zm4.853 1.249a1.271 1.271 0 1 1 .027-.107c0 .031 0 .067-.027.107zm-.937-3.436a1.333 1.333 0 1 1 .986-1.595c.033.172.033.348 0 .52-.07.53-.465.96-.986 1.075zm4.72 3.29a1.333 1.333 0 1 1-1.076-1.538 1.333 1.333 0 0 1 1.116 1.417.342.342 0 0 0-.027.12h-.013zm-1.08-3.33a1.333 1.333 0 1 1 1.088-1.524c.014.114.014.229 0 .342a1.333 1.333 0 0 1-1.102 1.182h.014zm-.925 7.925a1.333 1.333 0 1 1 .165-.547c-.01.193-.067.38-.165.547zm-.48-12.191a1.333 1.333 0 1 1 .507-1.814c.14.237.198.514.164.787-.038.438-.289.828-.67 1.045v-.018zM13.333 26.667C5.97 26.667 0 20.697 0 13.333 0 5.97 5.97 0 13.333 0c2.929-.01 5.778.955 8.098 2.742L19.8 4.89a10.667 10.667 0 0 0-17.133 8.444 10.667 10.667 0 0 0 17.137 8.471l1.627 2.13a13.218 13.218 0 0 1-8.098 2.733z" fill="#FFF"/></svg></a>
|
||||
<input type="checkbox" name="menu" id="main-nav-toggle" onchange={{ action 'change'}} />
|
||||
<a data-test-main-nav-logo href={{href-to 'index'}}><svg width="28" height="27" xmlns="http://www.w3.org/2000/svg"><title>Consul</title><path d="M13.284 16.178a2.876 2.876 0 1 1-.008-5.751 2.876 2.876 0 0 1 .008 5.75zm5.596-1.547a1.333 1.333 0 1 1 0-2.667 1.333 1.333 0 0 1 0 2.667zm4.853 1.249a1.271 1.271 0 1 1 .027-.107c0 .031 0 .067-.027.107zm-.937-3.436a1.333 1.333 0 1 1 .986-1.595c.033.172.033.348 0 .52-.07.53-.465.96-.986 1.075zm4.72 3.29a1.333 1.333 0 1 1-1.076-1.538 1.333 1.333 0 0 1 1.116 1.417.342.342 0 0 0-.027.12h-.013zm-1.08-3.33a1.333 1.333 0 1 1 1.088-1.524c.014.114.014.229 0 .342a1.333 1.333 0 0 1-1.102 1.182h.014zm-.925 7.925a1.333 1.333 0 1 1 .165-.547c-.01.193-.067.38-.165.547zm-.48-12.191a1.333 1.333 0 1 1 .507-1.814c.14.237.198.514.164.787-.038.438-.289.828-.67 1.045v-.018zM13.333 26.667C5.97 26.667 0 20.697 0 13.333 0 5.97 5.97 0 13.333 0c2.929-.01 5.778.955 8.098 2.742L19.8 4.89a10.667 10.667 0 0 0-17.133 8.444 10.667 10.667 0 0 0 17.137 8.471l1.627 2.13a13.218 13.218 0 0 1-8.098 2.733z" fill="#FFF"/></svg></a>
|
||||
<input type="checkbox" name="menu" id="main-nav-toggle" onchange={{action 'change'}} />
|
||||
<label for="main-nav-toggle">
|
||||
Toggle Menu
|
||||
</label>
|
||||
|
@ -8,7 +8,7 @@
|
|||
<label for="main-nav-toggle">
|
||||
<span>Close</span>
|
||||
</label>
|
||||
<nav>
|
||||
<nav aria-label="Main">
|
||||
{{#if dc}}
|
||||
<ul>
|
||||
{{#if (and (env 'CONSUL_NSPACES_ENABLED') (gt nspaces.length 0))}}
|
||||
|
|
|
@ -6,10 +6,10 @@
|
|||
class=item.Status
|
||||
tagName='li'
|
||||
}}
|
||||
{{#block-slot 'header'}}
|
||||
{{#block-slot name='header'}}
|
||||
<h3>{{item.Name}}</h3>
|
||||
{{/block-slot}}
|
||||
{{#block-slot 'content'}}
|
||||
{{#block-slot name='content'}}
|
||||
<dl>
|
||||
<dt>ServiceName</dt>
|
||||
<dd>{{or item.ServiceName '-'}}</dd>
|
||||
|
@ -37,15 +37,15 @@
|
|||
<dd>
|
||||
<pre><code>{{item.Output}}</code></pre>
|
||||
{{#feedback-dialog type='inline'}}
|
||||
{{#block-slot 'action' as |success error|}}
|
||||
{{#block-slot name='action' as |success error|}}
|
||||
{{copy-button success=(action success) error=(action error) clipboardText=item.Output title='copy output to clipboard'}}
|
||||
{{/block-slot}}
|
||||
{{#block-slot 'success' as |transition|}}
|
||||
{{#block-slot name='success' as |transition|}}
|
||||
<p class={{transition}}>
|
||||
Copied output!
|
||||
</p>
|
||||
{{/block-slot}}
|
||||
{{#block-slot 'error' as |transition|}}
|
||||
{{#block-slot name='error' as |transition|}}
|
||||
<p class={{transition}}>
|
||||
Sorry, something went wrong!
|
||||
</p>
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
{{yield}}
|
||||
<div>
|
||||
<header>
|
||||
{{#yield-slot 'header'}}{{yield}}{{/yield-slot}}
|
||||
{{#yield-slot name='header'}}{{yield}}{{/yield-slot}}
|
||||
</header>
|
||||
{{#yield-slot 'content'}}{{yield}}{{/yield-slot}}
|
||||
{{#yield-slot name='content'}}{{yield}}{{/yield-slot}}
|
||||
</div>
|
|
@ -1,19 +1,19 @@
|
|||
{{#stats-card}}
|
||||
{{#block-slot 'icon'}}{{yield}}{{/block-slot}}
|
||||
{{#block-slot 'mini-stat'}}
|
||||
{{#block-slot name='icon'}}{{yield}}{{/block-slot}}
|
||||
{{#block-slot name='mini-stat'}}
|
||||
{{#if (eq checks.length 0)}}
|
||||
<span class="zero" data-tooltip="This node has no registered healthchecks">{{checks.length}}</span>
|
||||
{{else if (eq checks.length healthy.length)}}
|
||||
<span class="non-zero" data-tooltip={{concat 'All ' healthy.length ' ' (pluralize healthy.length 'check' without-count=true) ' passing'}}>{{healthy.length}}</span>
|
||||
{{/if}}
|
||||
{{/block-slot}}
|
||||
{{#block-slot 'header'}}
|
||||
{{#block-slot name='header'}}
|
||||
<a href={{href}}>
|
||||
<strong>{{name}}</strong>
|
||||
<span>{{address}}</span>
|
||||
</a>
|
||||
{{/block-slot}}
|
||||
{{#block-slot 'body'}}
|
||||
{{#block-slot name='body'}}
|
||||
{{#if (not-eq checks.length healthy.length)}}
|
||||
<ul>
|
||||
{{#each unhealthy as |item|}}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
{{!<form>}}
|
||||
{{freetext-filter searchable=searchable value=search placeholder="Search by Source or Destination"}}
|
||||
{{radio-group name="action" value=action items=filters onchange=(action onchange)}}
|
||||
{{radio-group keyboardAccess=true name="action" value=action items=filters onchange=(action onchange)}}
|
||||
{{!</form>}}
|
|
@ -6,13 +6,13 @@
|
|||
<div>
|
||||
<header>
|
||||
<label for="modal_close">Close</label>
|
||||
{{#yield-slot 'header'}}{{yield}}{{/yield-slot}}
|
||||
{{#yield-slot name='header'}}{{yield}}{{/yield-slot}}
|
||||
</header>
|
||||
<div>
|
||||
{{#yield-slot 'body'}}{{yield}}{{/yield-slot}}
|
||||
{{#yield-slot name='body'}}{{yield}}{{/yield-slot}}
|
||||
</div>
|
||||
<footer>
|
||||
{{#yield-slot 'actions' (block-params (action 'close'))}}{{yield}}{{/yield-slot}}
|
||||
{{#yield-slot name='actions' params=(block-params (action 'close'))}}{{yield}}{{/yield-slot}}
|
||||
</footer>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{{yield}}
|
||||
<fieldset>
|
||||
{{#yield-slot 'template'}}
|
||||
{{#yield-slot name='template'}}
|
||||
{{else}}
|
||||
<header>
|
||||
Policy or service identity?
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
{{#child-selector repo=repo dc=dc nspace=nspace type="policy" placeholder="Search for policy" items=items}}
|
||||
{{yield}}
|
||||
{{#block-slot 'label'}}
|
||||
{{#block-slot name='label'}}
|
||||
Apply an existing policy
|
||||
{{/block-slot}}
|
||||
{{#block-slot 'create'}}
|
||||
{{#yield-slot 'trigger'}}
|
||||
{{#block-slot name='create'}}
|
||||
{{#yield-slot name='trigger'}}
|
||||
{{yield}}
|
||||
{{else}}
|
||||
<label class="type-dialog" for="new-policy-toggle">
|
||||
|
@ -13,13 +13,13 @@
|
|||
{{!TODO: potentially call trigger something else}}
|
||||
{{!the modal has to go here so that if you provide a slot to trigger it doesn't get rendered}}
|
||||
{{#modal-dialog data-test-policy-form onopen=(action 'open') name="new-policy-toggle"}}
|
||||
{{#block-slot 'header'}}
|
||||
{{#block-slot name='header'}}
|
||||
<h2>New Policy</h2>
|
||||
{{/block-slot}}
|
||||
{{#block-slot 'body'}}
|
||||
{{#block-slot name='body'}}
|
||||
{{policy-form form=form dc=dc}}
|
||||
{{/block-slot}}
|
||||
{{#block-slot 'actions' as |close|}}
|
||||
{{#block-slot name='actions' as |close|}}
|
||||
<button type="submit" {{action 'save' item items (queue (action close) (action 'reset'))}} disabled={{if (or item.isSaving item.isPristine item.isInvalid) 'disabled'}}>
|
||||
{{#if item.isSaving }}
|
||||
<div class="progress indeterminate"></div>
|
||||
|
@ -31,20 +31,20 @@
|
|||
{{/modal-dialog}}
|
||||
{{/yield-slot}}
|
||||
{{/block-slot}}
|
||||
{{#block-slot 'option' as |option|}}
|
||||
{{#block-slot name='option' as |option|}}
|
||||
{{option.Name}}
|
||||
{{/block-slot}}
|
||||
{{#block-slot 'set'}}
|
||||
{{#block-slot name='set'}}
|
||||
{{#tabular-details
|
||||
data-test-policies
|
||||
onchange=(action 'loadItem')
|
||||
items=(sort-by 'CreateTime:desc' 'Name:asc' items) as |item index|
|
||||
}}
|
||||
{{#block-slot 'header'}}
|
||||
{{#block-slot name='header'}}
|
||||
<th>Name</th>
|
||||
<th>Datacenters</th>
|
||||
{{/block-slot}}
|
||||
{{#block-slot 'row'}}
|
||||
{{#block-slot name='row'}}
|
||||
<td class={{policy/typeof item}}>
|
||||
{{#if item.ID }}
|
||||
<a href={{href-to 'dc.acls.policies.edit' item.ID}}>{{item.Name}}</a>
|
||||
|
@ -56,7 +56,7 @@
|
|||
{{if (not item.isSaving) (join ', ' (policy/datacenters item)) 'Saving...'}}
|
||||
</td>
|
||||
{{/block-slot}}
|
||||
{{#block-slot 'details'}}
|
||||
{{#block-slot name='details'}}
|
||||
<label class="type-text">
|
||||
<span>Rules <a href="{{env 'CONSUL_DOCS_URL'}}/guides/acl.html#rule-specification" rel="help noopener noreferrer" target="_blank">(HCL Format)</a></span>
|
||||
{{#if (eq item.template '')}}
|
||||
|
@ -69,10 +69,10 @@
|
|||
</label>
|
||||
<div>
|
||||
{{#confirmation-dialog message='Are you sure you want to remove this policy from this token?'}}
|
||||
{{#block-slot 'action' as |confirm|}}
|
||||
{{#block-slot name='action' as |confirm|}}
|
||||
<button data-test-delete type="button" class="type-delete" {{action confirm 'remove' item items}}>Remove</button>
|
||||
{{/block-slot}}
|
||||
{{#block-slot 'dialog' as |execute cancel message|}}
|
||||
{{#block-slot name='dialog' as |execute cancel message|}}
|
||||
<p>
|
||||
{{message}}
|
||||
</p>
|
||||
|
|
|
@ -1,21 +1,25 @@
|
|||
{{yield}}
|
||||
{{#aria-menu as |change keydown ariaLabelledBy ariaControls expanded|}}
|
||||
{{#toggle-button onchange=change as |click|}}
|
||||
<button type="button" aria-haspopup="menu" onkeydown={{keydown}} onclick={{click}} id={{ariaLabelledBy}} aria-controls={{ariaControls}}>
|
||||
{{yield (concat 'popover-menu-' guid)}}
|
||||
{{#aria-menu keyboardAccess=keyboardAccess as |change keypress ariaLabelledBy ariaControls ariaExpanded keypressClick|}}
|
||||
{{#toggle-button checked=expanded onchange=(queue change (action 'change')) as |click|}}
|
||||
<button type="button" aria-haspopup="menu" onkeydown={{keypress}} onclick={{click}} id={{ariaLabelledBy}} aria-controls={{ariaControls}}>
|
||||
{{#yield-slot name='trigger'}}
|
||||
{{yield}}
|
||||
{{/yield-slot}}
|
||||
</button>
|
||||
{{/toggle-button}}
|
||||
<div>
|
||||
<input type="checkbox" id={{concat 'popover-menu-' guid '-'}} />
|
||||
{{#each submenus as |sub|}}
|
||||
<input type="checkbox" id={{concat 'popover-menu-' guid '-' sub}} />
|
||||
{{/each}}
|
||||
{{#yield-slot name='header'}}
|
||||
<div>
|
||||
{{yield}}
|
||||
</div>
|
||||
{{else}}
|
||||
{{/yield-slot}}
|
||||
<ul role="menu" id={{ariaControls}} aria-labelledby={{ariaLabelledBy}} aria-expanded={{expanded}}>
|
||||
{{#yield-slot name='menu'}}
|
||||
<ul role="menu" id={{ariaControls}} aria-labelledby={{ariaLabelledBy}} aria-expanded={{ariaExpanded}}>
|
||||
{{#yield-slot name='menu' params=(block-params (concat 'popover-menu-' guid '-') send keypressClick) }}
|
||||
{{yield}}
|
||||
{{/yield-slot}}
|
||||
</ul>
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
{{!<fieldset>}}
|
||||
<div role="radiogroup" id="radiogroup_{{name}}" data-test-radiogroup="{{name}}">{{! menu?}}
|
||||
<fieldset>
|
||||
<div role="radiogroup" id="radiogroup_{{name}}" data-test-radiogroup={{name}}>{{! menu?}}
|
||||
{{#each items as |item|}}
|
||||
<label class="type-radio value-{{item.value}}" data-test-radiobutton="{{name}}_{{item.value}}"> {{! slugify }}
|
||||
<input type="radio" name={{name}} value={{item.value}} checked={{if (eq (concat value) item.value) 'checked'}} onchange={{action onchange}} />
|
||||
<span>{{item.label}}</span>
|
||||
</label>
|
||||
<label tabindex={{if keyboardAccess '0'}} onkeydown={{if keyboardAccess (action 'keydown')}} class="type-radio value-{{item.value}}" data-test-radiobutton="{{name}}_{{item.value}}"> {{! slugify }}
|
||||
<input type="radio" name={{name}} value={{item.value}} checked={{if (eq (concat value) item.value) 'checked'}} onchange={{action onchange}} />
|
||||
<span>{{item.label}}</span>
|
||||
</label>
|
||||
{{/each}}
|
||||
</div>
|
||||
{{!</fieldset>}}
|
||||
</div>
|
||||
</fieldset>
|
||||
|
|
|
@ -15,17 +15,6 @@
|
|||
</ol>
|
||||
</dd>
|
||||
</dl>
|
||||
{{else if item.Redirect}}
|
||||
<dl class="redirect">
|
||||
<dt data-tooltip="Redirect">Redirect</dt>
|
||||
<dd>
|
||||
<ol>
|
||||
<li>
|
||||
<span>{{item.ID}}</span>
|
||||
</li>
|
||||
</ol>
|
||||
</dd>
|
||||
</dl>
|
||||
{{/if}}
|
||||
</a>
|
||||
</header>
|
||||
|
@ -47,15 +36,15 @@
|
|||
</ol>
|
||||
</dd>
|
||||
</dl>
|
||||
{{else if item.Redirect}}
|
||||
{{else if child.Redirect}}
|
||||
<dl class="redirect">
|
||||
<dt data-tooltip="Redirect">Redirect</dt>
|
||||
<dd>
|
||||
{{child.ID}}
|
||||
{{child.Name}}
|
||||
</dd>
|
||||
</dl>
|
||||
{{else}}
|
||||
{{child.ServiceSubset}}
|
||||
{{child.Name}}
|
||||
{{/if}}
|
||||
</a>
|
||||
</li>
|
||||
|
|
|
@ -18,9 +18,9 @@
|
|||
{{!TODO: temporary policies id, look at the inception token modals and get rid of id="policies" and use something else}}
|
||||
<fieldset id="policies" class="policies">
|
||||
<h2>Policies</h2>
|
||||
{{#yield-slot 'policy' (block-params item)}}
|
||||
{{#yield-slot name='policy' params=(block-params item)}}
|
||||
{{yield}}
|
||||
{{else}}
|
||||
{{policy-selector dc=dc items=item.Policies}}
|
||||
{{policy-selector dc=dc nspace=nspace items=item.Policies}}
|
||||
{{/yield-slot}}
|
||||
</fieldset>
|
||||
|
|
|
@ -1,19 +1,19 @@
|
|||
{{#modal-dialog data-test-role-form onclose=(action (mut state) 'role') name="new-role-toggle"}}
|
||||
{{#block-slot 'header'}}
|
||||
{{#block-slot name='header'}}
|
||||
{{#if (eq state 'role')}}
|
||||
<h2>New Role</h2>
|
||||
{{else}}
|
||||
<h2>New Policy</h2>
|
||||
{{/if}}
|
||||
{{/block-slot}}
|
||||
{{#block-slot 'body'}}
|
||||
{{#block-slot name='body'}}
|
||||
|
||||
<input id="{{name}}_state_role" type="radio" name="{{name}}[state]" value="role" checked={{if (eq state 'role') 'checked'}} onchange={{action 'change'}} />
|
||||
{{#role-form form=form dc=dc}}
|
||||
{{#block-slot 'policy'}}
|
||||
{{#role-form form=form dc=dc nspace=nspace}}
|
||||
{{#block-slot name='policy'}}
|
||||
|
||||
{{#policy-selector source=source dc=dc items=item.Policies}}
|
||||
{{#block-slot 'trigger'}}
|
||||
{{#policy-selector source=source dc=dc nspace=nspace items=item.Policies}}
|
||||
{{#block-slot name='trigger'}}
|
||||
<label for="{{name}}_state_policy" data-test-create-policy class="type-dialog">
|
||||
<span>Create new policy</span>
|
||||
</label>
|
||||
|
@ -27,7 +27,7 @@
|
|||
{{policy-form data-test-policy-form name="role[policy]" form=policyForm dc=dc}}
|
||||
|
||||
{{/block-slot}}
|
||||
{{#block-slot 'actions' as |close|}}
|
||||
{{#block-slot name='actions' as |close|}}
|
||||
|
||||
{{#if (eq state 'role')}}
|
||||
<button type="submit" {{action 'save' item items (queue (action close) (action 'reset'))}} disabled={{if (or item.isSaving item.isPristine item.isInvalid) 'disabled'}}>
|
||||
|
@ -51,29 +51,29 @@
|
|||
{{/modal-dialog}}
|
||||
|
||||
{{#child-selector repo=repo dc=dc nspace=nspace type="role" placeholder="Search for role" items=items}}
|
||||
{{#block-slot 'label'}}
|
||||
{{#block-slot name='label'}}
|
||||
Apply an existing role
|
||||
{{/block-slot}}
|
||||
{{#block-slot 'create'}}
|
||||
{{#block-slot name='create'}}
|
||||
<label class="type-dialog" for="new-role-toggle">
|
||||
<span>Create new role</span>
|
||||
</label>
|
||||
|
||||
{{/block-slot}}
|
||||
{{#block-slot 'option' as |option|}}
|
||||
{{#block-slot name='option' as |option|}}
|
||||
{{option.Name}}
|
||||
{{/block-slot}}
|
||||
{{#block-slot 'set'}}
|
||||
{{#block-slot name='set'}}
|
||||
{{#tabular-collection
|
||||
data-test-roles
|
||||
rows=5
|
||||
items=(sort-by 'CreateTime:desc' 'Name:asc' items) as |item index|
|
||||
}}
|
||||
{{#block-slot 'header'}}
|
||||
{{#block-slot name='header'}}
|
||||
<th>Name</th>
|
||||
<th>Description</th>
|
||||
{{/block-slot}}
|
||||
{{#block-slot 'row'}}
|
||||
{{#block-slot name='row'}}
|
||||
<td>
|
||||
<a href={{href-to 'dc.acls.roles.edit' item.ID}}>{{item.Name}}</a>
|
||||
</td>
|
||||
|
@ -81,26 +81,41 @@
|
|||
{{item.Description}}
|
||||
</td>
|
||||
{{/block-slot}}
|
||||
{{#block-slot 'actions' as |index change checked|}}
|
||||
{{#confirmation-dialog confirming=false index=index message="Are you sure you want to remove this Role?"}}
|
||||
{{#block-slot 'action' as |confirm|}}
|
||||
{{#action-group index=index onchange=(action change) checked=(if (eq checked index) 'checked')}}
|
||||
<ul>
|
||||
<li>
|
||||
<a data-test-edit href={{href-to 'dc.acls.roles.edit' item.ID}}>Edit</a>
|
||||
{{#block-slot name='actions' as |index change checked|}}
|
||||
{{#popover-menu expanded=(if (eq checked index) true false) onchange=(action change index) keyboardAccess=false}}
|
||||
{{#block-slot name='trigger'}}
|
||||
More
|
||||
{{/block-slot}}
|
||||
{{#block-slot name='menu' as |confirm send keypressClick|}}
|
||||
<li role="none">
|
||||
<a role="menuitem" tabindex="-1" href={{href-to 'dc.acls.roles.edit' item.ID}}>Edit</a>
|
||||
</li>
|
||||
<li role="none" class="dangerous">
|
||||
<label for={{confirm}} role="menuitem" tabindex="-1" onkeypress={{keypressClick}} data-test-delete>Remove</label>
|
||||
<div role="menu">
|
||||
<div class="confirmation-alert warning">
|
||||
<div>
|
||||
<header>
|
||||
Confirm Remove
|
||||
</header>
|
||||
<p>
|
||||
Are you sure you want to remove this role?
|
||||
</p>
|
||||
</div>
|
||||
<ul>
|
||||
<li class="dangerous">
|
||||
<button tabindex="-1" type="button" class="type-delete" onclick={{action send 'remove' item items}}>Remove</button>
|
||||
</li>
|
||||
<li>
|
||||
<button type="button" class="type-delete" data-test-delete {{action confirm 'remove' item items}}>Remove</button>
|
||||
<label for={{confirm}}>Cancel</label>
|
||||
</li>
|
||||
</ul>
|
||||
{{/action-group}}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
{{/block-slot}}
|
||||
{{#block-slot 'dialog' as |execute cancel message name|}}
|
||||
{{delete-confirmation message=message execute=execute cancel=cancel}}
|
||||
{{/block-slot}}
|
||||
{{/confirmation-dialog}}
|
||||
{{/popover-menu}}
|
||||
{{/block-slot}}
|
||||
{{/tabular-collection}}
|
||||
|
||||
{{/block-slot}}
|
||||
{{/child-selector}}
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
{{/if}}
|
||||
<dl>
|
||||
<dt>
|
||||
{{path.type}}
|
||||
{{path.type}}
|
||||
</dt>
|
||||
<dd>
|
||||
{{#if (not-eq path.type 'Default')}}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<label class="type-reveal">
|
||||
<span>Reveal</span>
|
||||
<input type="checkbox" />
|
||||
<span>Reveal</span>
|
||||
<em>{{yield}}</em>
|
||||
</label>
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue