mirror of
https://github.com/valitydev/redash.git
synced 2024-11-06 17:15:17 +00:00
Merge pull request #2311 from kravets-levko/feature/add-widget-dialog-update
Add Widget Dialog UX Update
This commit is contained in:
commit
d6cc7489b1
@ -1,65 +1,65 @@
|
||||
.modal-header {
|
||||
padding: 23px 26px;
|
||||
}
|
||||
|
||||
.modal-body {
|
||||
padding: 0 26px 10px;
|
||||
}
|
||||
|
||||
.modal-content {
|
||||
box-shadow: 0 5px 20px rgba(0, 0, 0, 0.31);
|
||||
}
|
||||
|
||||
.modal-footer {
|
||||
padding: 20px 22px;
|
||||
}
|
||||
|
||||
.modal-xl {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
overflow: hidden;
|
||||
.modal-dialog {
|
||||
position: fixed;
|
||||
margin: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
padding: 0;
|
||||
}
|
||||
.modal-content {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
border: 2px solid #3c7dcf;
|
||||
border-radius: 0;
|
||||
box-shadow: none;
|
||||
}
|
||||
.modal-header {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
left: 0;
|
||||
height: 50px;
|
||||
padding: 10px;
|
||||
border: 0;
|
||||
}
|
||||
.modal-body {
|
||||
position: absolute;
|
||||
top: 50px;
|
||||
bottom: 60px;
|
||||
width: 100%;
|
||||
overflow: auto;
|
||||
}
|
||||
.modal-footer {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
height: 60px;
|
||||
padding: 10px;
|
||||
}
|
||||
}
|
||||
.modal-header {
|
||||
padding: 23px 26px;
|
||||
}
|
||||
|
||||
.modal-body {
|
||||
padding: 0 26px 10px;
|
||||
}
|
||||
|
||||
.modal-content {
|
||||
box-shadow: 0 5px 20px rgba(0, 0, 0, 0.31);
|
||||
}
|
||||
|
||||
.modal-footer {
|
||||
padding: 20px 26px;
|
||||
}
|
||||
|
||||
.modal-xl {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
overflow: hidden;
|
||||
.modal-dialog {
|
||||
position: fixed;
|
||||
margin: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
padding: 0;
|
||||
}
|
||||
.modal-content {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
border: 2px solid #3c7dcf;
|
||||
border-radius: 0;
|
||||
box-shadow: none;
|
||||
}
|
||||
.modal-header {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
left: 0;
|
||||
height: 50px;
|
||||
padding: 10px;
|
||||
border: 0;
|
||||
}
|
||||
.modal-body {
|
||||
position: absolute;
|
||||
top: 50px;
|
||||
bottom: 60px;
|
||||
width: 100%;
|
||||
overflow: auto;
|
||||
}
|
||||
.modal-footer {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
height: 60px;
|
||||
padding: 10px;
|
||||
}
|
||||
}
|
||||
|
@ -4,46 +4,70 @@
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<p class="btn-group">
|
||||
<button type="button" class="btn btn-default" ng-class="{active: $ctrl.isVisualization()}" ng-click="$ctrl.setType('visualization')">Visualization</button>
|
||||
<button type="button" class="btn btn-default" ng-class="{active: $ctrl.isTextBox()}" ng-click="$ctrl.setType('textbox')">Text Box</button>
|
||||
<button type="button" class="btn btn-default" ng-class="{active: $ctrl.isVisualization}" ng-click="$ctrl.setType('visualization')">Visualization</button>
|
||||
<button type="button" class="btn btn-default" ng-class="{active: $ctrl.isTextBox}" ng-click="$ctrl.setType('textbox')">Text Box</button>
|
||||
</p>
|
||||
|
||||
<div ng-show="$ctrl.isTextBox()">
|
||||
<div class="form-group">
|
||||
<textarea class="form-control" ng-model="$ctrl.text" rows="3"></textarea>
|
||||
</div>
|
||||
<div ng-show="$ctrl.text">
|
||||
<strong>Preview:</strong>
|
||||
<p ng-bind-html="$ctrl.text | markdown"></p>
|
||||
</div>
|
||||
<div ng-show="$ctrl.isTextBox">
|
||||
<div class="form-group">
|
||||
<textarea class="form-control" ng-model="$ctrl.text" ng-model-options="{ debounce: 200 }" rows="3" autofocus></textarea>
|
||||
</div>
|
||||
<div ng-show="$ctrl.text">
|
||||
<strong>Preview:</strong>
|
||||
<p ng-bind-html="$ctrl.text | markdown"></p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div ng-show="$ctrl.isVisualization()">
|
||||
<div class="form-group">
|
||||
<ui-select ng-model="$ctrl.query.selected" theme="bootstrap" reset-search-input="false" on-select="$ctrl.onQuerySelect($item, $model)">
|
||||
<ui-select-match placeholder="Search a query by name">{{$select.selected.name}}</ui-select-match>
|
||||
<ui-select-choices repeat="q in $ctrl.queries"
|
||||
refresh="$ctrl.searchQueries($select.search)"
|
||||
refresh-delay="0">
|
||||
<div ng-bind-html="$ctrl.trustAsHtml(q.name | highlight: $select.search)"></div>
|
||||
</ui-select-choices>
|
||||
</ui-select>
|
||||
<div ng-show="$ctrl.isVisualization">
|
||||
<div class="form-group">
|
||||
<input type="text" placeholder="Search a query by name" class="form-control" autofocus
|
||||
ng-if="!$ctrl.selectedQuery" ng-model="$ctrl.searchTerm" ng-change="$ctrl.searchQueries($ctrl.searchTerm)">
|
||||
<div ng-if="$ctrl.selectedQuery" class="p-relative">
|
||||
<input type="text" class="form-control bg-white"
|
||||
ng-value="$ctrl.selectedQuery.name" readonly="readonly">
|
||||
<a href="javascript:void(0)" ng-click="$ctrl.selectQuery(null)"
|
||||
class="d-flex align-items-center justify-content-center"
|
||||
style="position: absolute; right: 1px; top: 1px; bottom: 1px; width: 30px; background: #fff; border-radius: 3px;"
|
||||
><i class="text-muted fa fa-times"></i></a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div ng-if="!$ctrl.selectedQuery" class="scrollbox" style="max-height: 50vh">
|
||||
<div ng-if="$ctrl.searchTerm == ''">
|
||||
<div class="list-group" ng-if="$ctrl.recentQueries.length > 0">
|
||||
<a class="list-group-item" ng-repeat="query in $ctrl.recentQueries"
|
||||
ng-click="$ctrl.selectQuery(query.id)">{{query.name}}</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div ng-show="$ctrl.selected_query">
|
||||
<div class="form-group">
|
||||
<label for="">Choose Visualization</label>
|
||||
<select ng-model="$ctrl.selectedVis" ng-options="vis as vis.name group by vis.type for vis in $ctrl.selected_query.visualizations" class="form-control"></select>
|
||||
</div>
|
||||
<div ng-if="$ctrl.searchTerm != ''">
|
||||
<div ng-if="$ctrl.searchedQueries.length == 0" class="text-muted">
|
||||
No results matching search term.
|
||||
</div>
|
||||
<div class="list-group" ng-if="$ctrl.searchedQueries.length > 0">
|
||||
<a class="list-group-item"
|
||||
ng-repeat="query in $ctrl.searchedQueries" ng-click="$ctrl.selectQuery(query.id)"
|
||||
ng-bind-html="$ctrl.trustAsHtml(query.name | highlight: $ctrl.searchTerm)"
|
||||
></a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div ng-show="$ctrl.selectedQuery">
|
||||
<div class="form-group">
|
||||
<label>Choose Visualization</label>
|
||||
<select ng-model="$ctrl.selectedVis" class="form-control"
|
||||
ng-options="vis as vis.name group by vis.type for vis in $ctrl.selectedQuery.visualizations"></select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group" ng-if="$ctrl.isTextBox()">
|
||||
<label><input type="checkbox" ng-model="$ctrl.isHidden"> Hidden</label>
|
||||
<div class="form-group" ng-if="$ctrl.isTextBox">
|
||||
<label><input type="checkbox" ng-model="$ctrl.isHidden"> Hidden</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-default" ng-disabled="$ctrl.saveInProgress" ng-click="$ctrl.dismiss()">Close</button>
|
||||
<button type="button" class="btn btn-primary" ng-disabled="$ctrl.saveInProgress || !($ctrl.selectedVis || $ctrl.isTextBox())" ng-click="$ctrl.saveWidget()">Add to Dashboard</button>
|
||||
<button type="button" class="btn btn-primary" ng-disabled="$ctrl.saveInProgress || !($ctrl.selectedVis || $ctrl.isTextBox)" ng-click="$ctrl.saveWidget()">Add to Dashboard</button>
|
||||
</div>
|
||||
|
@ -1,3 +1,4 @@
|
||||
import { debounce } from 'underscore';
|
||||
import template from './add-widget-dialog.html';
|
||||
|
||||
const AddWidgetDialog = {
|
||||
@ -12,61 +13,82 @@ const AddWidgetDialog = {
|
||||
|
||||
this.dashboard = this.resolve.dashboard;
|
||||
this.saveInProgress = false;
|
||||
this.selectedVis = null;
|
||||
this.query = {};
|
||||
this.selected_query = undefined;
|
||||
|
||||
// Textbox
|
||||
this.text = '';
|
||||
this.existing_text = '';
|
||||
this.new_text = '';
|
||||
this.isHidden = false;
|
||||
this.type = 'visualization';
|
||||
|
||||
// Visualization
|
||||
this.selectedQuery = null;
|
||||
this.searchTerm = '';
|
||||
this.recentQueries = [];
|
||||
|
||||
// Don't show draft (unpublished) queries
|
||||
Query.recent().$promise.then((items) => {
|
||||
this.recentQueries = items.filter(item => !item.is_draft);
|
||||
});
|
||||
|
||||
this.searchedQueries = [];
|
||||
this.selectedVis = null;
|
||||
|
||||
this.trustAsHtml = html => $sce.trustAsHtml(html);
|
||||
this.isVisualization = () => this.type === 'visualization';
|
||||
this.isTextBox = () => this.type === 'textbox';
|
||||
|
||||
this.setType = (type) => {
|
||||
this.type = type;
|
||||
this.isVisualization = this.type === 'visualization';
|
||||
this.isTextBox = this.type === 'textbox';
|
||||
};
|
||||
this.setType('visualization');
|
||||
|
||||
this.onQuerySelect = () => {
|
||||
if (!this.query.selected) {
|
||||
return;
|
||||
}
|
||||
this.selectQuery = (queryId) => {
|
||||
// Clear previously selected query (if any)
|
||||
this.selectedQuery = null;
|
||||
this.selectedVis = null;
|
||||
|
||||
Query.get({ id: this.query.selected.id }, (query) => {
|
||||
if (query) {
|
||||
this.selected_query = query;
|
||||
if (query.visualizations.length) {
|
||||
this.selectedVis = query.visualizations[0];
|
||||
if (queryId) {
|
||||
Query.get({ id: queryId }, (query) => {
|
||||
if (query) {
|
||||
this.selectedQuery = query;
|
||||
if (query.visualizations.length) {
|
||||
this.selectedVis = query.visualizations[0];
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
this.searchQueries = (term) => {
|
||||
// `ng-model-options` does not work with `ng-change`, so do debounce here
|
||||
this.searchQueries = debounce((term) => {
|
||||
if (!term || term.length === 0) {
|
||||
this.queries = [];
|
||||
this.searchedQueries = [];
|
||||
return;
|
||||
}
|
||||
|
||||
Query.search({ q: term }, (results) => {
|
||||
this.queries = results;
|
||||
// If user will type too quick - it's possible that there will be
|
||||
// several requests running simultaneously. So we need to check
|
||||
// which results are matching current search term and ignore
|
||||
// outdated results.
|
||||
if (this.searchTerm === term) {
|
||||
this.searchedQueries = results;
|
||||
}
|
||||
});
|
||||
};
|
||||
}, 200);
|
||||
|
||||
this.saveWidget = () => {
|
||||
this.saveInProgress = true;
|
||||
|
||||
const selectedVis = this.isVisualization ? this.selectedVis : null;
|
||||
|
||||
const widget = new Widget({
|
||||
visualization_id: this.selectedVis && this.selectedVis.id,
|
||||
visualization_id: selectedVis && selectedVis.id,
|
||||
dashboard_id: this.dashboard.id,
|
||||
options: {
|
||||
isHidden: this.isTextBox() && this.isHidden,
|
||||
isHidden: this.isTextBox && this.isHidden,
|
||||
position: {},
|
||||
},
|
||||
visualization: this.selectedVis,
|
||||
text: this.text,
|
||||
visualization: selectedVis,
|
||||
text: this.isTextBox ? this.text : '',
|
||||
});
|
||||
|
||||
const position = this.dashboard.calculateNewWidgetPosition(widget);
|
||||
|
Loading…
Reference in New Issue
Block a user