mirror of
https://github.com/ansible/awx.git
synced 2026-03-21 19:07:39 -02:30
Merge pull request #6903 from marshmalien/copyProjectRevisionIcon
Add copy icon and update Tooltip content to project revision sha
This commit is contained in:
@@ -5,3 +5,4 @@
|
|||||||
@import 'popover/_index';
|
@import 'popover/_index';
|
||||||
@import 'tabs/_index';
|
@import 'tabs/_index';
|
||||||
@import 'utility/_index';
|
@import 'utility/_index';
|
||||||
|
@import 'truncate/_index';
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
function ComponentsStrings (BaseString) {
|
function ComponentsStrings (BaseString) {
|
||||||
BaseString.call(this, 'components');
|
BaseString.call(this, 'components');
|
||||||
|
|
||||||
let t = this.t;
|
let t = this.t;
|
||||||
let ns = this.components;
|
let ns = this.components;
|
||||||
|
|
||||||
@@ -42,6 +42,11 @@ function ComponentsStrings (BaseString) {
|
|||||||
ns.lookup = {
|
ns.lookup = {
|
||||||
NOT_FOUND: t('That value was not found. Please enter or select a valid value.')
|
NOT_FOUND: t('That value was not found. Please enter or select a valid value.')
|
||||||
};
|
};
|
||||||
|
|
||||||
|
ns.truncate = {
|
||||||
|
DEFAULT: t('Copy full revision to clipboard.'),
|
||||||
|
COPIED: t('Copied to clipboard.')
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ComponentsStrings.$inject = ['BaseStringService'];
|
ComponentsStrings.$inject = ['BaseStringService'];
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ import panelBody from './panel/body.directive';
|
|||||||
import popover from './popover/popover.directive';
|
import popover from './popover/popover.directive';
|
||||||
import tab from './tabs/tab.directive';
|
import tab from './tabs/tab.directive';
|
||||||
import tabGroup from './tabs/group.directive';
|
import tabGroup from './tabs/group.directive';
|
||||||
|
import truncate from './truncate/truncate.directive';
|
||||||
|
|
||||||
import BaseInputController from './input/base.controller';
|
import BaseInputController from './input/base.controller';
|
||||||
import ComponentsStrings from './components.strings';
|
import ComponentsStrings from './components.strings';
|
||||||
@@ -46,7 +47,8 @@ angular
|
|||||||
.directive('atPopover', popover)
|
.directive('atPopover', popover)
|
||||||
.directive('atTab', tab)
|
.directive('atTab', tab)
|
||||||
.directive('atTabGroup', tabGroup)
|
.directive('atTabGroup', tabGroup)
|
||||||
.service('BaseInputController', BaseInputController)
|
.directive('atTruncate', truncate)
|
||||||
.service('ComponentsStrings', ComponentsStrings);
|
.service('ComponentsStrings', ComponentsStrings)
|
||||||
|
.service('BaseInputController', BaseInputController);
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -7,13 +7,19 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.at-Popover-icon {
|
.at-Popover-icon {
|
||||||
.at-mixin-ButtonIcon();
|
.at-mixin-ButtonIcon();
|
||||||
color: @at-color-icon-popover;
|
color: @at-color-icon-popover;
|
||||||
font-size: @at-font-size-icon;
|
font-size: @at-font-size-icon;
|
||||||
padding: 0;
|
padding: 1px;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.at-Popover-icon--defaultCursor {
|
||||||
|
i > {
|
||||||
|
cursor: default;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.at-Popover-container {
|
.at-Popover-container {
|
||||||
visibility: hidden;
|
visibility: hidden;
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
@@ -24,7 +30,7 @@
|
|||||||
height: auto;
|
height: auto;
|
||||||
position: fixed;
|
position: fixed;
|
||||||
z-index: 2000;
|
z-index: 2000;
|
||||||
margin: 0 0 0 18px;
|
margin: 0;
|
||||||
border-radius: @at-border-radius;
|
border-radius: @at-border-radius;
|
||||||
box-shadow: 0 5px 10px rgba(0,0,0, 0.2);
|
box-shadow: 0 5px 10px rgba(0,0,0, 0.2);
|
||||||
transition: opacity .15s linear;
|
transition: opacity .15s linear;
|
||||||
@@ -36,7 +42,7 @@
|
|||||||
position: fixed;
|
position: fixed;
|
||||||
z-index: 1999;
|
z-index: 1999;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
margin: 8px 0 0 3px;
|
margin: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.at-Popover-title {
|
.at-Popover-title {
|
||||||
@@ -48,5 +54,5 @@
|
|||||||
.at-Popover-text {
|
.at-Popover-text {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
font-size: @at-font-size-body;
|
font-size: @at-font-size;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,22 @@
|
|||||||
|
const DEFAULT_POSITION = 'right';
|
||||||
|
const DEFAULT_ACTION = 'click';
|
||||||
|
const DEFAULT_ICON = 'fa fa-question-circle';
|
||||||
|
const DEFAULT_ALIGNMENT = 'inline';
|
||||||
|
const DEFAULT_ARROW_HEIGHT = 14;
|
||||||
|
const DEFAULT_PADDING = 10;
|
||||||
|
const DEFAULT_REFRESH_DELAY = 50;
|
||||||
|
const DEFAULT_RESET_ON_EXIT = false;
|
||||||
|
|
||||||
function atPopoverLink (scope, el, attr, controllers) {
|
function atPopoverLink (scope, el, attr, controllers) {
|
||||||
let popoverController = controllers[0];
|
let popoverController = controllers[0];
|
||||||
let container = el[0];
|
let container = el[0];
|
||||||
let popover = container.getElementsByClassName('at-Popover-container')[0];
|
let popover = container.getElementsByClassName('at-Popover-container')[0];
|
||||||
let icon = container.getElementsByTagName('i')[0];
|
let icon = container.getElementsByTagName('i')[0];
|
||||||
|
|
||||||
popoverController.init(scope, container, icon, popover);
|
let done = scope.$watch('state', () => {
|
||||||
|
popoverController.init(scope, container, icon, popover);
|
||||||
|
done();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function AtPopoverController () {
|
function AtPopoverController () {
|
||||||
@@ -13,13 +25,35 @@ function AtPopoverController () {
|
|||||||
let container;
|
let container;
|
||||||
let icon;
|
let icon;
|
||||||
let popover;
|
let popover;
|
||||||
|
let scope;
|
||||||
|
|
||||||
vm.init = (scope, _container_, _icon_, _popover_) => {
|
vm.init = (_scope_, _container_, _icon_, _popover_) => {
|
||||||
|
scope = _scope_;
|
||||||
icon = _icon_;
|
icon = _icon_;
|
||||||
popover = _popover_;
|
popover = _popover_;
|
||||||
scope.inline = true;
|
|
||||||
|
|
||||||
icon.addEventListener('click', vm.createDisplayListener());
|
scope.popover = scope.state.popover || {};
|
||||||
|
|
||||||
|
scope.popover.text = scope.state.help_text || scope.popover.text;
|
||||||
|
scope.popover.title = scope.state.label || scope.popover.title;
|
||||||
|
scope.popover.inline = scope.popover.inline || DEFAULT_ALIGNMENT;
|
||||||
|
scope.popover.position = scope.popover.position || DEFAULT_POSITION;
|
||||||
|
scope.popover.icon = scope.popover.icon || DEFAULT_ICON;
|
||||||
|
scope.popover.on = scope.popover.on || DEFAULT_ACTION;
|
||||||
|
scope.popover.resetOnExit = scope.popover.resetOnExit || DEFAULT_RESET_ON_EXIT;
|
||||||
|
|
||||||
|
if (scope.popover.resetOnExit) {
|
||||||
|
scope.originalText = scope.popover.text;
|
||||||
|
scope.originalTitle = scope.popover.title;
|
||||||
|
}
|
||||||
|
|
||||||
|
icon.addEventListener(scope.popover.on, vm.createDisplayListener());
|
||||||
|
|
||||||
|
scope.$watch('popover.text', vm.refresh);
|
||||||
|
|
||||||
|
if (scope.popover.click) {
|
||||||
|
icon.addEventListener('click', scope.popover.click);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
vm.createDismissListener = (createEvent) => {
|
vm.createDismissListener = (createEvent) => {
|
||||||
@@ -30,16 +64,30 @@ function AtPopoverController () {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
vm.open = false;
|
vm.dismiss();
|
||||||
|
|
||||||
popover.style.visibility = 'hidden';
|
if (scope.popover.on === 'mouseenter') {
|
||||||
popover.style.opacity = 0;
|
icon.removeEventListener('mouseleave', vm.dismissListener);
|
||||||
|
} else {
|
||||||
|
window.addEventListener(scope.popover.on, vm.dismissListener);
|
||||||
|
}
|
||||||
|
|
||||||
window.removeEventListener('click', vm.dismissListener);
|
|
||||||
window.removeEventListener('resize', vm.dismissListener);
|
window.removeEventListener('resize', vm.dismissListener);
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
vm.dismiss = (refresh) => {
|
||||||
|
if (!refresh && scope.popover.resetOnExit) {
|
||||||
|
scope.popover.text = scope.originalText;
|
||||||
|
scope.popover.title = scope.originalTitle;
|
||||||
|
}
|
||||||
|
|
||||||
|
vm.open = false;
|
||||||
|
|
||||||
|
popover.style.visibility = 'hidden';
|
||||||
|
popover.style.opacity = 0;
|
||||||
|
};
|
||||||
|
|
||||||
vm.isClickWithinPopover = (event, popover) => {
|
vm.isClickWithinPopover = (event, popover) => {
|
||||||
let box = popover.getBoundingClientRect();
|
let box = popover.getBoundingClientRect();
|
||||||
|
|
||||||
@@ -61,38 +109,96 @@ function AtPopoverController () {
|
|||||||
|
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
|
|
||||||
vm.open = true;
|
vm.display();
|
||||||
|
|
||||||
let arrow = popover.getElementsByClassName('at-Popover-arrow')[0];
|
|
||||||
|
|
||||||
let iPos = icon.getBoundingClientRect();
|
|
||||||
let pPos = popover.getBoundingClientRect();
|
|
||||||
|
|
||||||
let wHeight = window.clientHeight;
|
|
||||||
let pHeight = pPos.height;
|
|
||||||
|
|
||||||
let cx = Math.floor(iPos.left + (iPos.width / 2));
|
|
||||||
let cy = Math.floor(iPos.top + (iPos.height / 2));
|
|
||||||
|
|
||||||
arrow.style.top = (iPos.top - iPos.height) + 'px';
|
|
||||||
arrow.style.left = iPos.right + 'px';
|
|
||||||
|
|
||||||
if (cy < (pHeight / 2)) {
|
|
||||||
popover.style.top = '10px';
|
|
||||||
} else {
|
|
||||||
popover.style.top = (cy - pHeight / 2) + 'px';
|
|
||||||
}
|
|
||||||
|
|
||||||
popover.style.left = cx + 'px';
|
|
||||||
popover.style.visibility = 'visible';
|
|
||||||
popover.style.opacity = 1;
|
|
||||||
|
|
||||||
vm.dismissListener = vm.createDismissListener(event);
|
vm.dismissListener = vm.createDismissListener(event);
|
||||||
|
|
||||||
window.addEventListener('click', vm.dismissListener);
|
if (scope.popover.on === 'mouseenter') {
|
||||||
|
icon.addEventListener('mouseleave', vm.dismissListener);
|
||||||
|
} else {
|
||||||
|
window.addEventListener(scope.popover.on, vm.dismissListener);
|
||||||
|
}
|
||||||
|
|
||||||
window.addEventListener('resize', vm.dismissListener);
|
window.addEventListener('resize', vm.dismissListener);
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
vm.refresh = () => {
|
||||||
|
if (!vm.open) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
vm.dismiss(true);
|
||||||
|
window.setTimeout(vm.display, DEFAULT_REFRESH_DELAY);
|
||||||
|
};
|
||||||
|
|
||||||
|
vm.getPositions = () => {
|
||||||
|
let arrow = popover.getElementsByClassName('at-Popover-arrow')[0];
|
||||||
|
|
||||||
|
arrow.style.lineHeight = `${DEFAULT_ARROW_HEIGHT}px`;
|
||||||
|
arrow.children[0].style.lineHeight = `${DEFAULT_ARROW_HEIGHT}px`;
|
||||||
|
|
||||||
|
let data = {
|
||||||
|
arrow,
|
||||||
|
icon: icon.getBoundingClientRect(),
|
||||||
|
popover: popover.getBoundingClientRect(),
|
||||||
|
windowHeight: window.innerHeight
|
||||||
|
};
|
||||||
|
|
||||||
|
data.cx = Math.floor(data.icon.left + (data.icon.width / 2));
|
||||||
|
data.cy = Math.floor(data.icon.top + (data.icon.height / 2));
|
||||||
|
|
||||||
|
return data;
|
||||||
|
};
|
||||||
|
|
||||||
|
vm.display = () => {
|
||||||
|
vm.open = true;
|
||||||
|
|
||||||
|
let positions = vm.getPositions();
|
||||||
|
|
||||||
|
popover.style.visibility = 'visible';
|
||||||
|
popover.style.opacity = 1;
|
||||||
|
|
||||||
|
if (scope.popover.position === 'right') {
|
||||||
|
vm.displayRight(positions);
|
||||||
|
} else if (scope.popover.position === 'top') {
|
||||||
|
vm.displayTop(positions);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
vm.displayRight = (pos) => {
|
||||||
|
let arrowTop = Math.floor((pos.cy - (pos.icon.height / 2)));
|
||||||
|
let arrowLeft = pos.cx + DEFAULT_PADDING;
|
||||||
|
|
||||||
|
let popoverTop;
|
||||||
|
let popoverLeft = arrowLeft + DEFAULT_PADDING - 1;
|
||||||
|
|
||||||
|
if (pos.cy < (pos.popover.height / 2)) {
|
||||||
|
popoverTop = DEFAULT_PADDING;
|
||||||
|
} else {
|
||||||
|
popoverTop = Math.floor((pos.cy - pos.popover.height / 2));
|
||||||
|
}
|
||||||
|
|
||||||
|
pos.arrow.style.top = `${arrowTop}px`;
|
||||||
|
pos.arrow.style.left = `${arrowLeft}px`;
|
||||||
|
|
||||||
|
popover.style.top = `${popoverTop}px`;
|
||||||
|
popover.style.left = `${popoverLeft}px`;
|
||||||
|
};
|
||||||
|
|
||||||
|
vm.displayTop = (pos) => {
|
||||||
|
let arrowTop = pos.icon.top - pos.icon.height - DEFAULT_PADDING;
|
||||||
|
let arrowLeft = Math.floor(pos.icon.right - pos.icon.width - (pos.arrow.style.width / 2));
|
||||||
|
|
||||||
|
let popoverTop = pos.icon.top - pos.popover.height - pos.icon.height - 5;
|
||||||
|
let popoverLeft = Math.floor(pos.cx - (pos.popover.width / 2));
|
||||||
|
|
||||||
|
pos.arrow.style.top = `${arrowTop}px`;
|
||||||
|
pos.arrow.style.left = `${arrowLeft}px`;
|
||||||
|
|
||||||
|
popover.style.top = `${popoverTop}px`;
|
||||||
|
popover.style.left = `${popoverLeft}px`;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function atPopover (pathService) {
|
function atPopover (pathService) {
|
||||||
@@ -115,4 +221,4 @@ atPopover.$inject = [
|
|||||||
'PathService'
|
'PathService'
|
||||||
];
|
];
|
||||||
|
|
||||||
export default atPopover;
|
export default atPopover;
|
||||||
@@ -1,16 +1,16 @@
|
|||||||
<div ng-show="state.help_text"
|
<div ng-show="popover.text" class="at-Popover" ng-class="{ 'at-Popover--inline': popover.inline }">
|
||||||
class="at-Popover"
|
<span class="at-Popover-icon"
|
||||||
ng-class="{ 'at-Popover--inline': inline }">
|
ng-class="{ 'at-Popover-icon--defaultCursor': popover.on === 'mouseenter' && !popover.click }">
|
||||||
<span class="at-Popover-icon">
|
<i class="fa {{ popover.icon }}"></i>
|
||||||
<i class="fa fa-question-circle"></i>
|
|
||||||
</span>
|
</span>
|
||||||
<div class="at-Popover-container">
|
<div class="at-Popover-container">
|
||||||
<div class="at-Popover-arrow">
|
<div class="at-Popover-arrow">
|
||||||
<i class="fa fa-caret-left fa-2x"></i>
|
<i ng-if="popover.position === 'right'" class="fa fa-caret-left fa-2x"></i>
|
||||||
|
<i ng-if="popover.position === 'top'" class="fa fa-caret-down fa-2x"></i>
|
||||||
</div>
|
</div>
|
||||||
<div class="at-Popover-content">
|
<div class="at-Popover-content">
|
||||||
<h4 class="at-Popover-title">{{::state.label}}</h4>
|
<h4 ng-if="popover.title" class="at-Popover-title">{{ popover.title }}</h4>
|
||||||
<p class="at-Popover-text">{{::state.help_text}}</p>
|
<p ng-if="popover.text" class="at-Popover-text">{{ popover.text }}</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
34
awx/ui/client/lib/components/truncate/_index.less
Normal file
34
awx/ui/client/lib/components/truncate/_index.less
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
.at-Truncate {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
.at-Truncate-text {
|
||||||
|
font-family: monospace;
|
||||||
|
}
|
||||||
|
|
||||||
|
.at-Truncate-copy {
|
||||||
|
color: @at-gray-dark-2x;
|
||||||
|
cursor: pointer;
|
||||||
|
margin-left: 10px;
|
||||||
|
|
||||||
|
i:hover {
|
||||||
|
color: @at-blue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.at-Truncate-textarea {
|
||||||
|
background: transparent;
|
||||||
|
border: none;
|
||||||
|
box-shadow: none;
|
||||||
|
height: 2em;
|
||||||
|
left: 0px;
|
||||||
|
outline: none;
|
||||||
|
padding: 0px;
|
||||||
|
position: fixed;
|
||||||
|
top: 0px;
|
||||||
|
width: 2em;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
68
awx/ui/client/lib/components/truncate/truncate.directive.js
Normal file
68
awx/ui/client/lib/components/truncate/truncate.directive.js
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
function atTruncateLink (scope, el, attr, ctrl) {
|
||||||
|
let truncateController = ctrl;
|
||||||
|
let string = attr.string;
|
||||||
|
let maxlength = attr.maxlength;
|
||||||
|
|
||||||
|
truncateController.init(el, string, maxlength);
|
||||||
|
}
|
||||||
|
|
||||||
|
function AtTruncateController (strings) {
|
||||||
|
let vm = this;
|
||||||
|
let el;
|
||||||
|
let string;
|
||||||
|
let maxlength;
|
||||||
|
vm.strings = strings;
|
||||||
|
|
||||||
|
vm.init = (_el_, _string_, _maxlength_) => {
|
||||||
|
el = _el_;
|
||||||
|
string = _string_;
|
||||||
|
maxlength = _maxlength_;
|
||||||
|
vm.truncatedString = string.substring(0, maxlength);
|
||||||
|
};
|
||||||
|
|
||||||
|
vm.tooltip = {
|
||||||
|
popover: {
|
||||||
|
text: vm.strings.components.truncate.DEFAULT,
|
||||||
|
on: 'mouseover',
|
||||||
|
position: 'top',
|
||||||
|
icon: 'fa fa-clone',
|
||||||
|
resetOnExit: true,
|
||||||
|
click: copyToClipboard
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
function copyToClipboard() {
|
||||||
|
vm.tooltip.popover.text = vm.strings.components.truncate.COPIED;
|
||||||
|
|
||||||
|
let textarea = el[0].getElementsByClassName('at-Truncate-textarea')[0];
|
||||||
|
textarea.value = string;
|
||||||
|
textarea.select();
|
||||||
|
|
||||||
|
document.execCommand('copy');
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
AtTruncateController.$inject = ['ComponentsStrings'];
|
||||||
|
|
||||||
|
function atTruncate(pathService) {
|
||||||
|
return {
|
||||||
|
restrict: 'E',
|
||||||
|
replace: true,
|
||||||
|
transclude: true,
|
||||||
|
templateUrl: pathService.getPartialPath('components/truncate/truncate'),
|
||||||
|
controller: AtTruncateController,
|
||||||
|
controllerAs: 'vm',
|
||||||
|
link: atTruncateLink,
|
||||||
|
scope: {
|
||||||
|
state: '=',
|
||||||
|
maxLength: '@',
|
||||||
|
string: '@'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
atTruncate.$inject = [
|
||||||
|
'PathService'
|
||||||
|
];
|
||||||
|
|
||||||
|
export default atTruncate;
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
<div class="at-Truncate">
|
||||||
|
<div class="at-Truncate-tag">
|
||||||
|
<span class="at-Truncate-text">{{vm.truncatedString}}</span>
|
||||||
|
</div>
|
||||||
|
<div class="at-Truncate-copy" data-placement="top">
|
||||||
|
<at-popover position="top" on="mouseover" state="vm.tooltip"></at-popover>
|
||||||
|
</div>
|
||||||
|
<textarea class="at-Truncate-textarea"></textarea>
|
||||||
|
</div>
|
||||||
@@ -108,10 +108,6 @@
|
|||||||
margin-left: 10px;
|
margin-left: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.JobResults-resultRowText--revision{
|
|
||||||
font-family: monospace;
|
|
||||||
}
|
|
||||||
|
|
||||||
.JobResults-resultRowText--instanceGroup {
|
.JobResults-resultRowText--instanceGroup {
|
||||||
display: flex;
|
display: flex;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -247,9 +247,8 @@
|
|||||||
<label class="JobResults-resultRowLabel">
|
<label class="JobResults-resultRowLabel">
|
||||||
Revision
|
Revision
|
||||||
</label>
|
</label>
|
||||||
<div class="JobResults-resultRowText JobResults-resultRowText--revision">
|
<at-truncate string="{{job.scm_revision}}" maxLength="7" class="JobResults-resultRowText">
|
||||||
{{ job.scm_revision }}
|
</at-truncate>
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- PLAYBOOK DETAIL -->
|
<!-- PLAYBOOK DETAIL -->
|
||||||
|
|||||||
@@ -13,10 +13,9 @@ import { N_ } from '../i18n';
|
|||||||
import GetProjectPath from './factories/get-project-path.factory';
|
import GetProjectPath from './factories/get-project-path.factory';
|
||||||
import GetProjectIcon from './factories/get-project-icon.factory';
|
import GetProjectIcon from './factories/get-project-icon.factory';
|
||||||
import GetProjectToolTip from './factories/get-project-tool-tip.factory';
|
import GetProjectToolTip from './factories/get-project-tool-tip.factory';
|
||||||
import revisions from './revisions/main';
|
|
||||||
|
|
||||||
export default
|
export default
|
||||||
angular.module('Projects', [revisions.name])
|
angular.module('Projects', [])
|
||||||
.controller('ProjectsList', ProjectsList)
|
.controller('ProjectsList', ProjectsList)
|
||||||
.controller('ProjectsAdd', ProjectsAdd)
|
.controller('ProjectsAdd', ProjectsAdd)
|
||||||
.controller('ProjectsEdit', ProjectsEdit)
|
.controller('ProjectsEdit', ProjectsEdit)
|
||||||
|
|||||||
@@ -1,11 +0,0 @@
|
|||||||
/*************************************************
|
|
||||||
* Copyright (c) 2015 Ansible, Inc.
|
|
||||||
*
|
|
||||||
* All Rights Reserved
|
|
||||||
*************************************************/
|
|
||||||
|
|
||||||
import revisions from './revisions.directive';
|
|
||||||
|
|
||||||
export default
|
|
||||||
angular.module('revisions', [])
|
|
||||||
.directive('revisions', revisions);
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
@import "./client/src/shared/branding/colors.default.less";
|
|
||||||
|
|
||||||
.RevisionHash {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.RevisionHash-name {
|
|
||||||
font-family: monospace;
|
|
||||||
}
|
|
||||||
|
|
||||||
.RevisionHash-copy {
|
|
||||||
color: @default-link;
|
|
||||||
text-transform: uppercase;
|
|
||||||
cursor: pointer;
|
|
||||||
font-size: 11px;
|
|
||||||
margin-left: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.RevisionHash-copy:hover {
|
|
||||||
color: @default-link-hov;
|
|
||||||
}
|
|
||||||
@@ -1,51 +0,0 @@
|
|||||||
export default
|
|
||||||
[ 'templateUrl',
|
|
||||||
'Rest',
|
|
||||||
'$q',
|
|
||||||
'$filter',
|
|
||||||
function(templateUrl, Rest, $q, $filter) {
|
|
||||||
return {
|
|
||||||
restrict: 'E',
|
|
||||||
scope: false,
|
|
||||||
templateUrl: templateUrl('projects/revisions/revisions'),
|
|
||||||
link: function(scope) {
|
|
||||||
let full_revision = scope.project.scm_revision;
|
|
||||||
scope.revisionHash = $filter('limitTo')(full_revision, 7, 0);
|
|
||||||
scope.count = scope.project.scm_revision.length;
|
|
||||||
|
|
||||||
scope.copyRevisionHash = function() {
|
|
||||||
let textArea = document.createElement("textarea");
|
|
||||||
|
|
||||||
// Place in top-left corner of screen regardless of scroll position.
|
|
||||||
textArea.style.position = 'fixed';
|
|
||||||
textArea.style.top = 0;
|
|
||||||
textArea.style.left = 0;
|
|
||||||
|
|
||||||
// Ensure it has a small width and height. Setting to 1px / 1em
|
|
||||||
// doesn't work as this gives a negative w/h on some browsers.
|
|
||||||
textArea.style.width = '2em';
|
|
||||||
textArea.style.height = '2em';
|
|
||||||
|
|
||||||
// We don't need padding, reducing the size if it does flash render.
|
|
||||||
textArea.style.padding = 0;
|
|
||||||
|
|
||||||
// Clean up any borders.
|
|
||||||
textArea.style.border = 'none';
|
|
||||||
textArea.style.outline = 'none';
|
|
||||||
textArea.style.boxShadow = 'none';
|
|
||||||
|
|
||||||
// Avoid flash of white box if rendered for any reason.
|
|
||||||
textArea.style.background = 'transparent';
|
|
||||||
|
|
||||||
textArea.value = full_revision;
|
|
||||||
document.body.appendChild(textArea);
|
|
||||||
textArea.select();
|
|
||||||
|
|
||||||
document.execCommand('copy');
|
|
||||||
|
|
||||||
document.body.removeChild(textArea);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
];
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
<div class="RevisionHash-tag">
|
|
||||||
<span class="RevisionHash-name">{{revisionHash}}</span>
|
|
||||||
</div>
|
|
||||||
<div class="RevisionHash-copy" ng-if="count > 7" aw-tool-tip="Copy full revision to clipboard" data-placement="top" ng-click="copyRevisionHash()">
|
|
||||||
Copy
|
|
||||||
</div>
|
|
||||||
@@ -525,7 +525,7 @@ angular.module('GeneratorHelpers', [systemStatus.name])
|
|||||||
Attr(field, 'columnClass') : "";
|
Attr(field, 'columnClass') : "";
|
||||||
html += `
|
html += `
|
||||||
<td ${classList}>
|
<td ${classList}>
|
||||||
<revisions class=\"RevisionHash\"></revisions>
|
<at-truncate string="{{project.scm_revision}}" maxLength="7"></at-truncate>
|
||||||
</td>`;
|
</td>`;
|
||||||
} else if (field.type === 'badgeCount') {
|
} else if (field.type === 'badgeCount') {
|
||||||
html = BadgeCount(params);
|
html = BadgeCount(params);
|
||||||
|
|||||||
Reference in New Issue
Block a user