ui: Improved Discovery Chain resizing (#9360)

* install on-resize modifier

* Rerrange things to use on-resize modifier for positioning
This commit is contained in:
John Cowen 2020-12-11 09:38:33 +00:00 committed by GitHub
parent f827deb8a7
commit eb85b858d9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 141 additions and 62 deletions

View File

@ -16,6 +16,7 @@
}
{{/if}}
</style>
<div class="routes">
<header>
<h2>
@ -29,12 +30,14 @@
<div role="group">
{{#each routes as |item|}}
<Consul::DiscoveryChain::RouteCard
{{on-resize (dom-position (set item 'rect') from=this.edges)}}
@item={{item}}
@onclick={{action "click"}}
/>
{{/each}}
</div>
</div>
<div class="splitters">
<header>
<h2>
@ -48,102 +51,126 @@
<div role="group">
{{#each (sort-by 'Name' splitters) as |item|}}
<Consul::DiscoveryChain::SplitterCard
{{on-resize (dom-position (set item 'rect') from=this.edges)}}
@item={{item}}
@onclick={{action "click"}}
/>
{{/each}}
</div>
</div>
<div class="resolvers">
<header>
<h2>
Resolvers
<span {{tooltip "Resolvers are used to define which instances of a service should satisfy discovery requests."}}>
<span
{{tooltip "Resolvers are used to define which instances of a service should satisfy discovery requests."}}
>
</span>
</h2>
</header>
<div role="group">
{{#each (sort-by 'Name' resolvers) as |item|}}
<Consul::DiscoveryChain::ResolverCard
{{on-resize (dom-position (set item 'rect') from=this.edges)}}
@item={{item}}
@edges={{this.edges}}
@onclick={{action "click"}}
/>
{{/each}}
</div>
</div>
{{nodes}}
<svg class="edges"
{{did-insert (set this 'edges')}}
width="100%"
height="100%"
viewBox={{concat '0 0 ' width ' ' height}}
preserveAspectRatio="none"
>
{{#each routes as |item|}}
{{#let (dom-position (concat '#' item.ID) '.edges') as |src|}}
{{#let (dom-position (concat '#' item.NextNode) '.edges') as |destRect|}}
{{#if item.rect}}
{{#let item.rect item.NextItem.rect as |src destRect|}}
{{#let (tween-to (hash
x=destRect.x
y=(add destRect.y (div destRect.height 2))
) (concat item.ID)) as |dest|}}
<path
id={{concat item.ID '>' item.NextNode}}
d={{
svg-curve (hash
x=dest.x
y=dest.y
) src=(hash
x=(add src.x src.width)
y=(add src.y (div src.height 2))
)}} />
{{/let}}
{{/let}}
{{/let}}
{{/each}}
{{#each splitters as |splitter|}}
{{#let (dom-position (concat '#' splitter.ID) '.edges') as |src|}}
{{#each splitter.Splits as |item index|}}
{{#let (dom-position (concat '#' item.NextNode) '.edges') as |destRect|}}
{{#let (tween-to (hash
x=destRect.x
y=(add destRect.y (div destRect.height 2))
) (concat splitter.ID '-' index)) as |dest|}}
<path
{{tooltip (concat (round (or item.Weight 0) decimals=2) '%') options=(hash followCursor=true)}}
id={{concat 'splitter:' splitter.Name '>' item.NextNode}}
class="split"
id={{concat item.ID '>' item.NextNode}}
d={{
svg-curve (hash
x=dest.x
y=dest.y
y=(sub dest.y 0)
) src=(hash
x=(add src.x src.width)
x=src.right
y=(add src.y (div src.height 2))
)}} />
)}}
/>
{{/let}}
{{/let}}
{{/each}}
{{/let}}
{{/let}}
{{/if}}
{{/each}}
{{#each splitters as |splitter|}}
{{#if splitter.rect}}
{{#let splitter.rect as |src|}}
{{#each splitter.Splits as |item index|}}
{{#let item.NextItem.rect as |destRect|}}
{{#let (tween-to (hash
x=destRect.x
y=(add destRect.y (div destRect.height 2))
) (concat splitter.ID '-' index)) as |dest|}}
<path
{{tooltip
(concat (round (or item.Weight 0) decimals=2) '%')
options=(hash followCursor=true)
}}
id={{concat 'splitter:' splitter.Name '>' item.NextNode}}
class="split"
d={{
svg-curve (hash
x=dest.x
y=dest.y
) src=(hash
x=src.right
y=(add src.y (div src.height 2))
)}}
/>
{{/let}}
{{/let}}
{{/each}}
{{/let}}
{{/if}}
{{/each}}
</svg>
<svg class="resolver-inlets" viewBox={{concat '0 0 10 ' height}}>
<svg class="resolver-inlets" height="100%">
{{#each routes as |item|}}
{{#if (string-starts-with item.NextNode 'resolver:') }}
{{#let (dom-position (concat '#' item.NextNode) '.edges') as |dest|}}
{{#let (or item.NextItem.rect (hash y=0 height=0)) as |dest|}}
<circle r="2.5" cx="5" cy={{add dest.y (div dest.height 2)}} />
{{/let}}
{{/if}}
{{/each}}
{{#each splitters as |item|}}
{{#each item.Splits as |item|}}
{{#let (dom-position (concat '#' item.NextNode) '.edges') as |dest|}}
{{#let (or item.NextItem.rect (hash y=0 height=0)) as |dest|}}
<circle r="2.5" cx="5" cy={{add dest.y (div dest.height 2)}} />
{{/let}}
{{/each}}
{{/each}}
</svg>
<svg class="splitter-inlets" viewBox={{concat '0 0 10 ' height}}>
<svg class="splitter-inlets" height="100%">
{{#each routes as |item|}}
{{#if (string-starts-with item.NextNode 'splitter:') }}
{{#let (dom-position (concat '#' item.NextNode) '.edges') as |dest|}}
{{#let (or item.NextItem.rect (hash y=0 height=0)) as |dest|}}
<circle r="2.5" cx="5" cy={{add dest.y (div dest.height 2)}} />
{{/let}}
{{/if}}

View File

@ -73,6 +73,37 @@ export default Component.extend({
}
return routes;
}),
nodes: computed('routes', 'splitters', 'resolvers', function() {
let nodes = this.resolvers.reduce((prev, item) => {
prev[`resolver:${item.ID}`] = item;
item.Children.reduce((prev, item) => {
prev[`resolver:${item.ID}`] = item;
return prev;
}, prev);
return prev;
}, {});
nodes = this.splitters.reduce((prev, item) => {
prev[item.ID] = item;
return prev;
}, nodes);
nodes = this.routes.reduce((prev, item) => {
prev[item.ID] = item;
return prev;
}, nodes);
Object.entries(nodes).forEach(([key, value]) => {
if (typeof value.NextNode !== 'undefined') {
value.NextItem = nodes[value.NextNode];
}
if (typeof value.Splits !== 'undefined') {
value.Splits.forEach(item => {
if (typeof item.NextNode !== 'undefined') {
item.NextItem = nodes[item.NextNode];
}
});
}
});
return '';
}),
resolvers: computed('chain.{Nodes,Targets}', function() {
return getResolvers(
this.chain.Datacenter,
@ -117,12 +148,6 @@ export default Component.extend({
edges: edges.map(item => `#${CSS.escape(item)}`),
};
}),
width: computed('chain.{Nodes,Targets}', function() {
return this.element.offsetWidth;
}),
height: computed('chain.{Nodes,Targets}', function() {
return this.element.offsetHeight;
}),
actions: {
click: function(e) {
const id = e.currentTarget.getAttribute('id');

View File

@ -1,8 +1,9 @@
<div
class="resolver-card"
...attributes
>
<header onclick={{optional @onclick}} id={{concat 'resolver:' @item.ID}}>
<header
...attributes
onclick={{optional @onclick}} id={{concat 'resolver:' @item.ID}}>
<a name="">
<h3>{{@item.Name}}</h3>
{{#if item.Failover}}
@ -28,7 +29,11 @@
{{#if (gt @item.Children.length 0)}}
<ul>
{{#each @item.Children as |child|}}
<li onclick={{optional @onclick}} id={{concat 'resolver:' child.ID}}>
<li
onclick={{optional @onclick}}
id={{concat 'resolver:' child.ID}}
{{on-resize (dom-position (set child 'rect') from=@edges)}}
>
<a name="">
{{#if child.Redirect}}
<dl class="redirect">

View File

@ -2,6 +2,7 @@
class="route-card"
onclick={{@onclick}}
id={{@item.ID}}
...attributes
>
<header class={{if (eq this.path.value '/') 'short'}}>
{{#if (gt @item.Definition.Match.HTTP.Methods.length 0) }}

View File

@ -1,7 +1,7 @@
<div
...attributes
>
<a
...attributes
id={{@item.ID}}
class="splitter-card"
onclick={{optional @onclick}}

View File

@ -1,16 +1,18 @@
import Helper from '@ember/component/helper';
import { inject as service } from '@ember/service';
export default class DomPosition extends Helper {
@service('dom') dom;
compute([target, from], hash) {
const $target = this.dom.element(target);
const $from = this.dom.element(from);
const fromRect = $from.getBoundingClientRect();
const rect = $target.getBoundingClientRect();
rect.x = rect.x - fromRect.x;
rect.y = rect.y - fromRect.y;
return rect;
compute([target], { from }) {
if (typeof target === 'function') {
return entry => {
const $target = entry.target;
let rect = $target.getBoundingClientRect();
if (typeof from !== 'undefined') {
const fromRect = from.getBoundingClientRect();
rect.x = rect.x - fromRect.x;
rect.y = rect.y - fromRect.y;
}
return target(rect);
};
}
}
}

View File

@ -120,6 +120,7 @@
"ember-modifier": "^2.1.1",
"ember-named-blocks-polyfill": "^0.2.3",
"ember-on-helper": "^0.1.0",
"ember-on-resize-modifier": "^0.3.0",
"ember-page-title": "^5.2.3",
"ember-power-select": "^4.0.5",
"ember-power-select-with-create": "^0.8.0",

View File

@ -8135,7 +8135,7 @@ ember-modifier-manager-polyfill@^1.1.0, ember-modifier-manager-polyfill@^1.2.0:
ember-cli-version-checker "^2.1.2"
ember-compatibility-helpers "^1.2.0"
ember-modifier@^2.1.1:
ember-modifier@^2.1.0, ember-modifier@^2.1.1:
version "2.1.1"
resolved "https://registry.yarnpkg.com/ember-modifier/-/ember-modifier-2.1.1.tgz#aa3a12e2d6cf1622f774f3f1eab4880982a43fa9"
integrity sha512-g9mcpFWgw5lgNU40YNf0USNWqoGTJ+EqjDQKjm7556gaRNDeGnLylFKqx9O3opwLHEt6ZODnRDy9U0S5YEMREg==
@ -8171,6 +8171,16 @@ ember-on-helper@^0.1.0:
dependencies:
ember-cli-babel "^7.7.3"
ember-on-resize-modifier@^0.3.0:
version "0.3.0"
resolved "https://registry.yarnpkg.com/ember-on-resize-modifier/-/ember-on-resize-modifier-0.3.0.tgz#6c8b0fda3cd77c9f51c5e8394ed7af13b2c20fcb"
integrity sha512-LUZcO3dYJXTcUFI2/8X+wyZXEp0p/xDZS3UHxwI/j99MprL4ZNxdYELQ5Rhq0sR/eGMBaJMEMzgM7I62+irOrg==
dependencies:
ember-cli-babel "^7.20.5"
ember-cli-htmlbars "^5.1.2"
ember-modifier "^2.1.0"
ember-resize-observer-service "^0.3.0"
ember-page-title@^5.2.3:
version "5.2.3"
resolved "https://registry.yarnpkg.com/ember-page-title/-/ember-page-title-5.2.3.tgz#63b039d70d4a5d7db9c00de5b2108823fb90bb9d"
@ -8243,6 +8253,14 @@ ember-require-module@^0.3.0:
dependencies:
ember-cli-babel "^6.9.2"
ember-resize-observer-service@^0.3.0:
version "0.3.0"
resolved "https://registry.yarnpkg.com/ember-resize-observer-service/-/ember-resize-observer-service-0.3.0.tgz#69b6e29bd6d742001ebe0ec70249f20f46fedc4a"
integrity sha512-FrKPowJ9CwLBok+WZOhudoRXPg9xHArpotMdZ2FyztcBHIb8D1mVB6ELLw62KGa62Wf7RoRhfmkloZax/5WHwg==
dependencies:
ember-cli-babel "^7.20.5"
ember-cli-htmlbars "^5.1.2"
ember-resolver@^8.0.0:
version "8.0.2"
resolved "https://registry.yarnpkg.com/ember-resolver/-/ember-resolver-8.0.2.tgz#8a45a744aaf5391eb52b4cb393b3b06d2db1975c"