Table widget dropdowns - dependent drop-downs and loading indicators
This commit is contained in:
parent
d880f1ffbd
commit
9377bcf7c1
|
|
@ -104,11 +104,19 @@ class Table extends WidgetBase
|
|||
traceLog($columnName);
|
||||
traceLog($rowData);
|
||||
|
||||
$options = [
|
||||
'string'=>'String',
|
||||
'checkbox'=>'Checkbox',
|
||||
'dropdown'=>'Dropdown'
|
||||
];
|
||||
if ($rowData['billable'] == 'yes' || $rowData['billable'] == 'no') {
|
||||
$options = [
|
||||
'string'=>'String - '.$rowData['billable'],
|
||||
'checkbox'=>'Checkbox - '.$rowData['billable'],
|
||||
'dropdown'=>'Dropdown - '.$rowData['billable']
|
||||
];
|
||||
} else {
|
||||
$options = [
|
||||
'who-knows'=>'Who knows?',
|
||||
'whatever'=>'Whatever',
|
||||
'sometimes'=>'Sometimes'
|
||||
];
|
||||
}
|
||||
|
||||
return [
|
||||
'options' => $options
|
||||
|
|
|
|||
|
|
@ -196,6 +196,19 @@
|
|||
.table-control td[data-column-type=dropdown] .content-container {
|
||||
outline: none;
|
||||
}
|
||||
html.cssanimations .table-control td[data-column-type=dropdown] [data-view-container].loading:after {
|
||||
background-image: url(../../../../assets/images/loading-indicator-transparent.svg);
|
||||
background-size: 15px 15px;
|
||||
background-position: 50% 50%;
|
||||
position: absolute;
|
||||
width: 15px;
|
||||
height: 15px;
|
||||
top: 6px;
|
||||
right: 5px;
|
||||
content: ' ';
|
||||
-webkit-animation: spin 1s linear infinite;
|
||||
animation: spin 1s linear infinite;
|
||||
}
|
||||
.table-control-dropdown-list {
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
|
|
|
|||
|
|
@ -519,6 +519,17 @@
|
|||
return
|
||||
}
|
||||
|
||||
Table.prototype.notifyRowProcessorsOnChange = function(cellElement) {
|
||||
var columnName = cellElement.getAttribute('data-column'),
|
||||
row = cellElement.parentNode
|
||||
|
||||
for (var i = 0, len = row.children.length; i < len; i++) {
|
||||
var column = this.options.columns[i].key
|
||||
|
||||
this.cellProcessors[column].onRowValueChanged(columnName, row.children[i])
|
||||
}
|
||||
}
|
||||
|
||||
// PUBLIC METHODS
|
||||
// ============================
|
||||
|
||||
|
|
@ -558,7 +569,6 @@
|
|||
this.activeCell = null
|
||||
}
|
||||
|
||||
|
||||
// HELPER METHODS
|
||||
// ============================
|
||||
|
||||
|
|
@ -661,6 +671,8 @@
|
|||
dataContainer.value = value
|
||||
|
||||
this.markCellRowDirty(cellElement)
|
||||
|
||||
this.notifyRowProcessorsOnChange(cellElement)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -93,6 +93,12 @@
|
|||
Base.prototype.onClick = function(ev) {
|
||||
}
|
||||
|
||||
/*
|
||||
* This method is called when a cell value in the row changes.
|
||||
*/
|
||||
Base.prototype.onRowValueChanged = function(columnName, cellElement) {
|
||||
}
|
||||
|
||||
/*
|
||||
* Determines if the keyboard navigation in the specified direction is allowed
|
||||
* by the cell processor. Some processors could reject the navigation, for example
|
||||
|
|
@ -129,6 +135,8 @@
|
|||
viewContainer.textContent = value
|
||||
|
||||
cellContentContainer.appendChild(viewContainer)
|
||||
|
||||
return viewContainer
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
|||
|
|
@ -30,8 +30,6 @@
|
|||
|
||||
this.itemListElement = null
|
||||
|
||||
this.optionsCache = {}
|
||||
|
||||
this.cachedOptionPromises = {}
|
||||
|
||||
// Event handlers
|
||||
|
|
@ -51,10 +49,9 @@
|
|||
DropdownProcessor.prototype.dispose = function() {
|
||||
this.unregisterListHandlers()
|
||||
this.itemClickHandler = null
|
||||
this.itemKeyDownHandler = null
|
||||
this.itemListElement = null
|
||||
this.optionsCache = null
|
||||
this.cachedOptionPromises = null
|
||||
|
||||
BaseProto.dispose.call(this)
|
||||
}
|
||||
|
||||
|
|
@ -74,15 +71,14 @@
|
|||
* Renders the cell in the normal (no edit) mode
|
||||
*/
|
||||
DropdownProcessor.prototype.renderCell = function(value, cellContentContainer) {
|
||||
var self = this
|
||||
var viewContainer = this.createViewContainer(cellContentContainer, '...')
|
||||
|
||||
this.fetchOptions(cellContentContainer.parentNode, function renderCellFetchOptions(options) {
|
||||
if ( options[value] !== undefined )
|
||||
if (options[value] !== undefined)
|
||||
value = options[value]
|
||||
|
||||
self.createViewContainer(cellContentContainer, value)
|
||||
viewContainer.textContent = value
|
||||
cellContentContainer.setAttribute('tabindex', 0)
|
||||
self = null
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -199,19 +195,15 @@
|
|||
// the caching key contains the master column values.
|
||||
|
||||
var row = cellElement.parentNode,
|
||||
cachingKey = this.createOptionsCachingKey(row)
|
||||
|
||||
if (this.optionsCache[cachingKey] !== undefined) {
|
||||
onSuccess(this.optionsCache[cachingKey])
|
||||
return
|
||||
}
|
||||
cachingKey = this.createOptionsCachingKey(row),
|
||||
viewContainer = this.getViewContainer(cellElement)
|
||||
|
||||
// Request options from the server. When the table widget builds,
|
||||
// multiple cells in the column could require loading the options.
|
||||
// The AJAX promises are cached here so that we have a single
|
||||
// request per caching key.
|
||||
|
||||
// TODO: Loading indicator
|
||||
viewContainer.setAttribute('class', 'loading')
|
||||
|
||||
if (!this.cachedOptionPromises[cachingKey]) {
|
||||
var requestData = {
|
||||
|
|
@ -225,8 +217,8 @@
|
|||
|
||||
this.cachedOptionPromises[cachingKey].done(function onDropDownLoadOptionsSuccess(data){
|
||||
onSuccess(data.options)
|
||||
|
||||
row = null
|
||||
}).always(function onDropDownLoadOptionsAlways(){
|
||||
viewContainer.setAttribute('class', '')
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
@ -236,7 +228,7 @@
|
|||
dependsOn = this.columnConfiguration.depends_on
|
||||
|
||||
if (dependsOn) {
|
||||
if (typeof this.columnConfiguration.depends_on == 'object') {
|
||||
if (typeof dependsOn == 'object') {
|
||||
for (var i = 0, len = dependsOn.length; i < len; i++ )
|
||||
cachingKey += dependsOn[i] + this.tableObj.getRowCellValueByColumnName(row, dependsOn[i])
|
||||
} else
|
||||
|
|
@ -344,5 +336,44 @@
|
|||
this.showDropdown()
|
||||
}
|
||||
|
||||
/*
|
||||
* This method is called when a cell value in the row changes.
|
||||
*/
|
||||
Base.prototype.onRowValueChanged = function(columnName, cellElement) {
|
||||
// Determine if this drop-down depends on the changed column
|
||||
// and update the option list if necessary
|
||||
|
||||
if (!this.columnConfiguration.depends_on)
|
||||
return
|
||||
|
||||
var dependsOnColumn = false,
|
||||
dependsOn = this.columnConfiguration.depends_on
|
||||
|
||||
if (typeof dependsOn == 'object') {
|
||||
for (var i = 0, len = dependsOn.length; i < len; i++ ) {
|
||||
if (dependsOn[i] == columnName) {
|
||||
dependsOnColumn = true
|
||||
break
|
||||
}
|
||||
}
|
||||
} else
|
||||
dependsOnColumn = dependsOn == columnName
|
||||
|
||||
if (!dependsOnColumn)
|
||||
return
|
||||
|
||||
var currentValue = this.tableObj.getCellValue(cellElement),
|
||||
viewContainer = this.getViewContainer(cellElement)
|
||||
|
||||
this.fetchOptions(cellElement, function rowValueChangedFetchOptions(options) {
|
||||
var value = options[currentValue] !== undefined ?
|
||||
options[currentValue] :
|
||||
'...'
|
||||
|
||||
viewContainer.textContent = value
|
||||
viewContainer = null
|
||||
})
|
||||
}
|
||||
|
||||
$.oc.table.processor.dropdown = DropdownProcessor;
|
||||
}(window.jQuery);
|
||||
|
|
@ -230,6 +230,23 @@
|
|||
}
|
||||
}
|
||||
|
||||
html.cssanimations {
|
||||
.table-control td[data-column-type=dropdown] {
|
||||
[data-view-container].loading:after {
|
||||
background-image:url(../../../../assets/images/loading-indicator-transparent.svg);
|
||||
background-size: 15px 15px;
|
||||
background-position: 50% 50%;
|
||||
position: absolute;
|
||||
width: 15px;
|
||||
height: 15px;
|
||||
top: 6px;
|
||||
right: 5px;
|
||||
content: ' ';
|
||||
.animation(spin 1s linear infinite);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.table-control-dropdown-list {
|
||||
.user-select(none);
|
||||
position: absolute;
|
||||
|
|
|
|||
Loading…
Reference in New Issue