mirror of
https://github.com/ansible/awx.git
synced 2024-11-02 09:51:09 +03:00
Add partial implementation of drag and drop ssh key support
This commit is contained in:
parent
0ca53024f0
commit
952958a83c
@ -85,7 +85,7 @@ function AtFormController (eventService) {
|
|||||||
let handled;
|
let handled;
|
||||||
|
|
||||||
if (err.status === 400) {
|
if (err.status === 400) {
|
||||||
handled = vm.setValidationErrors(err.data);
|
handled = vm.handleValidationErrors(err.data);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!handled) {
|
if (!handled) {
|
||||||
@ -93,22 +93,8 @@ function AtFormController (eventService) {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
vm.setValidationErrors = errors => {
|
vm.handleValidationErrors = errors => {
|
||||||
let errorMessageSet = false;
|
let errorMessageSet = vm.setValidationMessages(errors);
|
||||||
|
|
||||||
for (let id in errors) {
|
|
||||||
vm.components
|
|
||||||
.filter(component => component.category === 'input')
|
|
||||||
.forEach(component => {
|
|
||||||
if (component.state._id === id) {
|
|
||||||
errorMessageSet = true;
|
|
||||||
|
|
||||||
component.state._rejected = true;
|
|
||||||
component.state._isValid = false;
|
|
||||||
component.state._message = errors[id].join(' ');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (errorMessageSet) {
|
if (errorMessageSet) {
|
||||||
vm.check();
|
vm.check();
|
||||||
@ -117,6 +103,30 @@ function AtFormController (eventService) {
|
|||||||
return errorMessageSet;
|
return errorMessageSet;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
vm.setValidationMessages = (errors, errorSet) => {
|
||||||
|
let errorMessageSet = errorSet || false;
|
||||||
|
|
||||||
|
for (let id in errors) {
|
||||||
|
if (!Array.isArray(errors[id]) && typeof errors[id] === 'object') {
|
||||||
|
errorMessageSet = vm.setValidationMessages(errors[id], errorMessageSet);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
vm.components
|
||||||
|
.filter(component => component.category === 'input')
|
||||||
|
.filter(component => errors[component.state.id])
|
||||||
|
.forEach(component => {
|
||||||
|
errorMessageSet = true;
|
||||||
|
|
||||||
|
component.state._rejected = true;
|
||||||
|
component.state._isValid = false;
|
||||||
|
component.state._message = errors[component.state.id].join(' ');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return errorMessageSet;
|
||||||
|
};
|
||||||
|
|
||||||
vm.validate = () => {
|
vm.validate = () => {
|
||||||
let isValid = true;
|
let isValid = true;
|
||||||
|
|
||||||
|
@ -15,8 +15,14 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.at-InputGroup-button {
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
.at-Input-button {
|
.at-Input-button {
|
||||||
width: 72px;
|
width: @at-input-button-width;
|
||||||
|
display: block;
|
||||||
|
height: 100%;
|
||||||
|
|
||||||
&, &:active, &:hover, &:focus {
|
&, &:active, &:hover, &:focus {
|
||||||
color: @at-gray-dark-3x;
|
color: @at-gray-dark-3x;
|
||||||
@ -36,6 +42,21 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.at-InputFile--hidden {
|
||||||
|
position: absolute;
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
left: 0;
|
||||||
|
right: @at-input-button-width;
|
||||||
|
z-index: -2;
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.at-InputFile--drag {
|
||||||
|
z-index: 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
.at-InputGroup {
|
.at-InputGroup {
|
||||||
padding: 0;
|
padding: 0;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
@ -64,6 +85,10 @@
|
|||||||
height: @at-space-6x;
|
height: @at-space-6x;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.at-InputLabel {
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
.at-InputLabel-name {
|
.at-InputLabel-name {
|
||||||
color: @at-gray-dark-4x;
|
color: @at-gray-dark-4x;
|
||||||
font-size: @at-font-size-2x;
|
font-size: @at-font-size-2x;
|
||||||
@ -71,6 +96,14 @@
|
|||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.at-InputLabel-hint {
|
||||||
|
margin-left: @at-space-4x;
|
||||||
|
color: @at-gray-dark-3x;
|
||||||
|
font-size: @at-font-size;
|
||||||
|
font-weight: @at-font-weight;
|
||||||
|
line-height: @at-line-height-short;
|
||||||
|
}
|
||||||
|
|
||||||
.at-InputMessage--rejected {
|
.at-InputMessage--rejected {
|
||||||
font-size: @at-font-size;
|
font-size: @at-font-size;
|
||||||
color: @at-red;
|
color: @at-red;
|
||||||
@ -82,8 +115,7 @@
|
|||||||
color: @at-red;
|
color: @at-red;
|
||||||
font-weight: @at-font-weight-2x;
|
font-weight: @at-font-weight-2x;
|
||||||
font-size: @at-font-size-2x;
|
font-size: @at-font-size-2x;
|
||||||
line-height: @at-line-height-short;
|
margin: 0;
|
||||||
margin: @at-space-3x @at-space 0 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.at-InputSelect {
|
.at-InputSelect {
|
||||||
@ -123,3 +155,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.at-Textarea {
|
||||||
|
.at-mixin-FontFixedWidth();
|
||||||
|
}
|
||||||
|
@ -75,9 +75,9 @@ function AtInputGroupController ($scope, $compile) {
|
|||||||
if (input.type === 'string') {
|
if (input.type === 'string') {
|
||||||
if (!input.multiline) {
|
if (!input.multiline) {
|
||||||
if (input.secret) {
|
if (input.secret) {
|
||||||
config._component = 'at-input-text';
|
|
||||||
} else {
|
|
||||||
config._component = 'at-input-secret';
|
config._component = 'at-input-secret';
|
||||||
|
} else {
|
||||||
|
config._component = 'at-input-text';
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
config._expand = true;
|
config._expand = true;
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
<label class="at-InputLabel at-u-flat">
|
<label class="at-InputLabel">
|
||||||
<span ng-if="state.required" class="pull-left at-InputLabel-required">*</span>
|
<span ng-if="state.required" class="at-InputLabel-required">*</span>
|
||||||
<span class="pull-left at-InputLabel-name">{{::state.label}}</span>
|
<span class="at-InputLabel-name">{{::state.label}}</span>
|
||||||
<at-popover class="pull-left" state="state"></at-popover>
|
<at-popover state="state"></at-popover>
|
||||||
|
<span ng-if="state._hint" class="at-InputLabel-hint">{{::state._hint}}</span>
|
||||||
</label>
|
</label>
|
||||||
|
@ -1,25 +1,89 @@
|
|||||||
|
const DEFAULT_HINT = 'HINT: Drag and drop an SSH private key file on the field below.';
|
||||||
|
|
||||||
function atInputTextareaSecretLink (scope, element, attrs, controllers) {
|
function atInputTextareaSecretLink (scope, element, attrs, controllers) {
|
||||||
let formController = controllers[0];
|
let formController = controllers[0];
|
||||||
let inputController = controllers[1];
|
let inputController = controllers[1];
|
||||||
|
|
||||||
if (scope.tab === '1') {
|
if (scope.tab === '1') {
|
||||||
element.find('input')[0].focus();
|
element.find('textarea')[0].focus();
|
||||||
}
|
}
|
||||||
|
|
||||||
inputController.init(scope, element, formController);
|
inputController.init(scope, element, formController);
|
||||||
}
|
}
|
||||||
|
|
||||||
function AtInputTextareaSecretController (baseInputController) {
|
function AtInputTextareaSecretController (baseInputController, eventService) {
|
||||||
let vm = this || {};
|
let vm = this || {};
|
||||||
|
|
||||||
vm.init = (scope, element, form) => {
|
let scope;
|
||||||
baseInputController.call(vm, 'input', scope, element, form);
|
let textarea;
|
||||||
|
let input;
|
||||||
|
|
||||||
|
vm.init = (_scope_, element, form) => {
|
||||||
|
baseInputController.call(vm, 'input', _scope_, element, form);
|
||||||
|
|
||||||
|
scope = _scope_;
|
||||||
|
textarea = element.find('textarea')[0];
|
||||||
|
|
||||||
|
if (scope.state.format === 'ssh_private_key') {
|
||||||
|
scope.ssh = true;
|
||||||
|
scope.state._hint = scope.state._hint || DEFAULT_HINT;
|
||||||
|
input = element.find('input')[0];
|
||||||
|
vm.setFileListeners(textarea, input);
|
||||||
|
} else {
|
||||||
|
scope.isShown = true;
|
||||||
|
scope.buttonText = 'HIDE';
|
||||||
|
}
|
||||||
|
|
||||||
vm.check();
|
vm.check();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
vm.setFileListeners = (textarea, input) => {
|
||||||
|
textarea
|
||||||
|
let eventNames = [
|
||||||
|
'drag',
|
||||||
|
'dragstart',
|
||||||
|
'dragend',
|
||||||
|
'dragover',
|
||||||
|
'dragenter',
|
||||||
|
'dragleave',
|
||||||
|
'drop'
|
||||||
|
];
|
||||||
|
|
||||||
|
eventService.addListener(textarea, 'dragenter', event => {
|
||||||
|
console.log('enter');
|
||||||
|
scope.drag = true;
|
||||||
|
});
|
||||||
|
|
||||||
|
eventService.addListener(input, ['dragleave', 'dragover'], event => {
|
||||||
|
console.log('exit');
|
||||||
|
scope.drag = false;
|
||||||
|
});
|
||||||
|
|
||||||
|
eventService.addListener(input, 'drop', event => {
|
||||||
|
vm.readFile(event.originalEvent.dataTransfer.files);
|
||||||
|
});
|
||||||
|
|
||||||
|
eventService.addListener(input, eventNames, event => {
|
||||||
|
event.stopPropagation();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
vm.readFile = () => {
|
||||||
|
console.log(file);
|
||||||
|
};
|
||||||
|
|
||||||
|
vm.toggle = () => {
|
||||||
|
if (scope.isShown) {
|
||||||
|
scope.buttonText = 'SHOW';
|
||||||
|
} else {
|
||||||
|
scope.buttonText = 'HIDE';
|
||||||
|
}
|
||||||
|
|
||||||
|
scope.isShown = !scope.isShown;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
AtInputTextareaSecretController.$inject = ['BaseInputController'];
|
AtInputTextareaSecretController.$inject = ['BaseInputController', 'EventService'];
|
||||||
|
|
||||||
function atInputTextareaSecret (pathService) {
|
function atInputTextareaSecret (pathService) {
|
||||||
return {
|
return {
|
||||||
|
@ -2,14 +2,29 @@
|
|||||||
<div class="form-group at-u-flat">
|
<div class="form-group at-u-flat">
|
||||||
<at-input-label></at-input-label>
|
<at-input-label></at-input-label>
|
||||||
|
|
||||||
<textarea class="form-control at-Input"
|
<div class="input-group">
|
||||||
ng-model="state._value"
|
<span class="input-group-btn at-InputGroup-button">
|
||||||
ng-class="{ 'at-Input--rejected': state._rejected }"
|
<button class="btn at-ButtonHollow--white at-Input-button"
|
||||||
ng-attr-maxlength="{{ state.max_length || undefined }}"
|
ng-disabled="state._disabled || form.disabled"
|
||||||
ng-attr-tabindex="{{ tab || undefined }}"
|
ng-click="vm.toggle()">
|
||||||
ng-attr-placeholder="{{::state._placeholder || undefined }}"
|
{{ buttonText }}
|
||||||
ng-change="vm.check()"
|
</button>
|
||||||
ng-disabled="state._disabled || form.disabled" /></textarea>
|
</span>
|
||||||
|
<input ng-show="ssh"
|
||||||
|
class="at-InputFile--hidden"
|
||||||
|
ng-class="{'at-InputFile--drag': drag }"
|
||||||
|
type="file"
|
||||||
|
name="files" />
|
||||||
|
<textarea class="form-control at-Input at-Textarea"
|
||||||
|
ng-model="state._value"
|
||||||
|
ng-class="{ 'at-Input--rejected': state._rejected }"
|
||||||
|
ng-attr-maxlength="{{ state.max_length || undefined }}"
|
||||||
|
ng-attr-tabindex="{{ tab || undefined }}"
|
||||||
|
ng-attr-placeholder="{{::state._placeholder || undefined }}"
|
||||||
|
ng-change="vm.check()"
|
||||||
|
ng-disabled="state._disabled || form.disabled" />
|
||||||
|
</textarea>
|
||||||
|
</div>
|
||||||
|
|
||||||
<at-input-message></at-input-message>
|
<at-input-message></at-input-message>
|
||||||
</div>
|
</div>
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
<div class="form-group at-u-flat">
|
<div class="form-group at-u-flat">
|
||||||
<at-input-label></at-input-label>
|
<at-input-label></at-input-label>
|
||||||
|
|
||||||
<textarea class="form-control at-Input"
|
<textarea class="form-control at-Input at-Textarea"
|
||||||
ng-model="state._value"
|
ng-model="state._value"
|
||||||
ng-class="{ 'at-Input--rejected': state._rejected }"
|
ng-class="{ 'at-Input--rejected': state._rejected }"
|
||||||
ng-attr-maxlength="{{ state.max_length || undefined }}"
|
ng-attr-maxlength="{{ state.max_length || undefined }}"
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
.at-Popover {
|
.at-Popover {
|
||||||
padding: 0 0 0 @at-space-4x;
|
padding: 0 0 0 @at-space-3x;
|
||||||
line-height: @at-line-height-short;
|
}
|
||||||
|
|
||||||
|
.at-Popover--inline {
|
||||||
|
display: inline-block;
|
||||||
}
|
}
|
||||||
|
|
||||||
.at-Popover-icon {
|
.at-Popover-icon {
|
||||||
|
@ -4,7 +4,7 @@ function atPopoverLink (scope, el, attr, controllers) {
|
|||||||
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(container, icon, popover);
|
popoverController.init(scope, container, icon, popover);
|
||||||
}
|
}
|
||||||
|
|
||||||
function AtPopoverController () {
|
function AtPopoverController () {
|
||||||
@ -14,9 +14,10 @@ function AtPopoverController () {
|
|||||||
let icon;
|
let icon;
|
||||||
let popover;
|
let popover;
|
||||||
|
|
||||||
vm.init = (_container_, _icon_, _popover_) => {
|
vm.init = (scope, _container_, _icon_, _popover_) => {
|
||||||
icon = _icon_;
|
icon = _icon_;
|
||||||
popover = _popover_;
|
popover = _popover_;
|
||||||
|
scope.inline = scope.state._inline || true;
|
||||||
|
|
||||||
icon.addEventListener('click', vm.createDisplayListener());
|
icon.addEventListener('click', vm.createDisplayListener());
|
||||||
};
|
};
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
<div ng-show="state.help_text" class="at-Popover">
|
<div ng-show="state.help_text"
|
||||||
|
class="at-Popover"
|
||||||
|
ng-class="{ 'at-Popover--inline': inline }">
|
||||||
<span class="at-Popover-icon">
|
<span class="at-Popover-icon">
|
||||||
<i class="fa fa-question-circle"></i>
|
<i class="fa fa-question-circle"></i>
|
||||||
</span>
|
</span>
|
||||||
|
@ -14,14 +14,22 @@ function EventService () {
|
|||||||
el
|
el
|
||||||
};
|
};
|
||||||
|
|
||||||
listener.el.addEventListener(listener.name, listener.fn);
|
if (Array.isArray(name)) {
|
||||||
|
name.forEach(e => listener.el.addEventListener(e, listener.fn));
|
||||||
|
} else {
|
||||||
|
listener.el.addEventListener(listener.name, listener.fn);
|
||||||
|
}
|
||||||
|
|
||||||
return listener;
|
return listener;
|
||||||
};
|
};
|
||||||
|
|
||||||
this.remove = listeners => {
|
this.remove = listeners => {
|
||||||
listeners.forEach(listener => {
|
listeners.forEach(listener => {
|
||||||
listener.el.removeEventListener(listener.name, listener.fn);
|
if (Array.isArray(listener.name)) {
|
||||||
|
listener.name.forEach(name => listener.el.removeEventListener(name, listener.fn));
|
||||||
|
} else {
|
||||||
|
listener.el.removeEventListener(listener.name, listener.fn);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -76,3 +76,7 @@
|
|||||||
color: @at-gray-dark-3x;
|
color: @at-gray-dark-3x;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.at-mixin-FontFixedWidth () {
|
||||||
|
font-family: Menlo, Monaco, Consolas, "Courier New", monospace;
|
||||||
|
}
|
||||||
|
@ -4,7 +4,8 @@
|
|||||||
* 1. Colors
|
* 1. Colors
|
||||||
* 2. Typography
|
* 2. Typography
|
||||||
* 3. Layout
|
* 3. Layout
|
||||||
* 4. Misc
|
* 4. Input
|
||||||
|
* 5. Misc
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// 1. Colors --------------------------------------------------------------------------------------
|
// 1. Colors --------------------------------------------------------------------------------------
|
||||||
@ -59,7 +60,10 @@
|
|||||||
@at-space-5x: 15px;
|
@at-space-5x: 15px;
|
||||||
@at-space-6x: 20px;
|
@at-space-6x: 20px;
|
||||||
|
|
||||||
// 4. Misc --------------------------------------------------------------------------------------
|
// 4. Input ---------------------------------------------------------------------------------------
|
||||||
|
@at-input-button-width: 72px;
|
||||||
|
|
||||||
|
// 5. Misc ----------------------------------------------------------------------------------------
|
||||||
@at-border-radius: 5px;
|
@at-border-radius: 5px;
|
||||||
@at-input-height: 30px;
|
@at-input-height: 30px;
|
||||||
@at-popover-width: 320px;
|
@at-popover-width: 320px;
|
||||||
|
Loading…
Reference in New Issue
Block a user