Newer
Older
piotr.kupczyk@id.ethz.ch
committed
if (newMultiselectedRows[row.id]) {
delete newMultiselectedRows[row.id]
} else {
newMultiselectedRows[row.id] = true
}
await this.multiselectRows(Object.keys(newMultiselectedRows))
}
}
piotr.kupczyk@id.ethz.ch
committed
async handleMultiselectAllRowsChange() {
piotr.kupczyk@id.ethz.ch
committed
const { rows, multiselectedRows } = this.context.getState()
piotr.kupczyk@id.ethz.ch
committed
const rowIds = rows.map(row => String(row.id))
piotr.kupczyk@id.ethz.ch
committed
const multiselectedRowIds = Object.keys(multiselectedRows)
let newMultiselectedRowIds = null
if (_.difference(rowIds, multiselectedRowIds).length === 0) {
newMultiselectedRowIds = _.difference(multiselectedRowIds, rowIds)
} else {
newMultiselectedRowIds = _.union(multiselectedRowIds, rowIds)
}
this.multiselectRows(newMultiselectedRowIds)
}
piotr.kupczyk@id.ethz.ch
committed
async handleMultiselectionClear() {
this.multiselectRows([])
}
piotr.kupczyk@id.ethz.ch
committed
async handleExecuteAction(action) {
if (action && action.execute) {
const { multiselectedRows } = this.context.getState()
action.execute({ multiselectedRows })
}
}
piotr.kupczyk@id.ethz.ch
committed
async handleExport() {
piotr.kupczyk@id.ethz.ch
committed
const exportable = this.getExportable()
piotr.kupczyk@id.ethz.ch
committed
if (!exportable) {
return
} else if (exportable.fileFormat === GridExportOptions.TSV_FILE_FORMAT) {
await this.handleExportTSV(exportable)
} else if (exportable.fileFormat === GridExportOptions.XLS_FILE_FORMAT) {
await this.handleExportXLS(exportable)
piotr.kupczyk@id.ethz.ch
committed
}
}
piotr.kupczyk@id.ethz.ch
committed
async handleExportTSV(exportable) {
piotr.kupczyk@id.ethz.ch
committed
const _this = this
const state = this.context.getState()
const props = this.context.getProps()
piotr.kupczyk@id.ethz.ch
committed
function _stringToUtf16ByteArray(str) {
var bytes = []
bytes.push(255, 254)
for (var i = 0; i < str.length; ++i) {
var charCode = str.charCodeAt(i)
bytes.push(charCode & 0xff) //low byte
bytes.push((charCode & 0xff00) >>> 8) //high byte (might be 0)
}
return bytes
}
piotr.kupczyk@id.ethz.ch
committed
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
function _getFileName(prefix) {
const now = new Date()
const year = String(now.getFullYear()).padStart(4, '0')
const month = String(now.getMonth() + 1).padStart(2, '0')
const day = String(now.getDate()).padStart(2, '0')
const hours = String(now.getHours()).padStart(2, '0')
const minutes = String(now.getMinutes()).padStart(2, '0')
const seconds = String(now.getSeconds()).padStart(2, '0')
const millis = String(now.getMilliseconds()).padStart(3, '0')
return (
prefix +
'.' +
year +
'-' +
month +
'-' +
day +
'-' +
hours +
'-' +
minutes +
'-' +
seconds +
'-' +
millis +
'.tsv'
)
}
piotr.kupczyk@id.ethz.ch
committed
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
async function _getExportedRows() {
const { exportOptions } = state
var exportedRows = []
if (exportOptions.rows === GridExportOptions.ALL_PAGES) {
if (state.local) {
exportedRows = state.sortedRows
} else if (props.loadRows) {
const loadedResult = await props.loadRows({
filters: state.filters,
globalFilter: state.globalFilter,
page: 0,
pageSize: 1000000,
sortings: state.sortings
})
exportedRows = loadedResult.rows
}
} else if (exportOptions.rows === GridExportOptions.CURRENT_PAGE) {
exportedRows = state.rows
} else if (exportOptions.rows === GridExportOptions.SELECTED_ROWS) {
exportedRows = Object.values(state.multiselectedRows).map(
selectedRow => selectedRow.data
)
} else {
throw Error('Unsupported rows option: ' + exportOptions.rows)
}
return exportedRows
}
async function _getExportedColumns(exportedRows) {
const { exportOptions } = state
var exportedColumns = []
if (exportOptions.columns === GridExportOptions.ALL_COLUMNS) {
exportedColumns = _this.getAllColumns()
} else if (exportOptions.columns === GridExportOptions.VISIBLE_COLUMNS) {
piotr.kupczyk@id.ethz.ch
committed
const { newAllColumns, newColumnsVisibility, newColumnsSorting } =
await _this._loadColumns(
exportedRows,
state.columnsVisibility,
state.columnsSorting
)
piotr.kupczyk@id.ethz.ch
committed
_this._sortColumns(newAllColumns, newColumnsSorting)
piotr.kupczyk@id.ethz.ch
committed
exportedColumns = newAllColumns.filter(
column => newColumnsVisibility[column.name]
)
piotr.kupczyk@id.ethz.ch
committed
} else {
throw Error('Unsupported columns option: ' + exportOptions.columns)
}
return exportedColumns.filter(column => column.exportable)
}
function _exportTSV(rows, columns) {
const { exportOptions } = state
piotr.kupczyk@id.ethz.ch
committed
const headers = columns.map(column => column.name)
piotr.kupczyk@id.ethz.ch
committed
const arrayOfRowArrays = []
piotr.kupczyk@id.ethz.ch
committed
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
arrayOfRowArrays.push(headers)
rows.forEach(row => {
var rowAsArray = []
columns.forEach(column => {
var rowValue = column.getValue({
row,
column,
operation: 'export',
exportOptions
})
if (!rowValue) {
rowValue = ''
} else {
var specialCharsRemover = document.createElement('textarea')
specialCharsRemover.innerHTML = rowValue
rowValue = specialCharsRemover.value //Removes special HTML Chars
rowValue = String(rowValue).replace(/\r?\n|\r|\t/g, ' ') //Remove carriage returns and tabs
if (exportOptions.values === GridExportOptions.RICH_TEXT) {
// do nothing with the value
} else if (exportOptions.values === GridExportOptions.PLAIN_TEXT) {
rowValue = String(rowValue).replace(/<(?:.|\n)*?>/gm, '')
} else {
throw Error('Unsupported values option: ' + exportOptions.values)
}
}
rowAsArray.push(rowValue)
})
arrayOfRowArrays.push(rowAsArray)
})
piotr.kupczyk@id.ethz.ch
committed
{
header: false,
delimiter: '\t',
quoted: false
},
arrayOfRowArrays,
function (err, tsv) {
var utf16bytes = _stringToUtf16ByteArray(tsv)
var utf16bytesArray = new Uint8Array(utf16bytes.length)
utf16bytesArray.set(utf16bytes, 0)
var blob = new Blob([utf16bytesArray], {
type: 'text/tsv;charset=UTF-16LE;'
})
piotr.kupczyk@id.ethz.ch
committed
FileSaver.saveAs(blob, _getFileName(exportable.filePrefix))
piotr.kupczyk@id.ethz.ch
committed
}
)
}
piotr.kupczyk@id.ethz.ch
committed
try {
this.context.setState({
exportState: {
loading: true
}
})
piotr.kupczyk@id.ethz.ch
committed
const exportedRows = await _getExportedRows()
const exportedColumns = await _getExportedColumns(exportedRows)
_exportTSV(exportedRows, exportedColumns)
piotr.kupczyk@id.ethz.ch
committed
this.context.setState({
exportState: null
})
} catch (e) {
this.context.setState({
exportState: {
piotr.kupczyk@id.ethz.ch
committed
error: e
piotr.kupczyk@id.ethz.ch
committed
}
})
}
piotr.kupczyk@id.ethz.ch
committed
}
piotr.kupczyk@id.ethz.ch
committed
async handleExportXLS(exportable) {
piotr.kupczyk@id.ethz.ch
committed
const _this = this
piotr.kupczyk@id.ethz.ch
committed
const state = this.context.getState()
const props = this.context.getProps()
piotr.kupczyk@id.ethz.ch
committed
if (!props.exportXLS) {
piotr.kupczyk@id.ethz.ch
committed
console.error(
piotr.kupczyk@id.ethz.ch
committed
'Missing exportXLS callback function for grid with id: ' + props.id
piotr.kupczyk@id.ethz.ch
committed
)
return
}
piotr.kupczyk@id.ethz.ch
committed
async function _getExportedRows() {
const { exportOptions } = state
piotr.kupczyk@id.ethz.ch
committed
piotr.kupczyk@id.ethz.ch
committed
let exportedRows = []
piotr.kupczyk@id.ethz.ch
committed
piotr.kupczyk@id.ethz.ch
committed
if (exportOptions.rows === GridExportOptions.ALL_PAGES) {
piotr.kupczyk@id.ethz.ch
committed
if (state.local) {
exportedRows = state.sortedRows
} else if (props.loadRows) {
const columns = {}
piotr.kupczyk@id.ethz.ch
committed
piotr.kupczyk@id.ethz.ch
committed
state.allColumns.forEach(column => {
columns[column.name] = column
})
const loadedResult = await props.loadRows({
columns: columns,
filterMode: state.filterMode,
filters: state.filters,
globalFilter: state.globalFilter,
page: 0,
pageSize: 1000000,
sortings: state.sortings
})
exportedRows = loadedResult.rows
}
piotr.kupczyk@id.ethz.ch
committed
} else if (exportOptions.rows === GridExportOptions.CURRENT_PAGE) {
piotr.kupczyk@id.ethz.ch
committed
exportedRows = state.rows
piotr.kupczyk@id.ethz.ch
committed
} else if (exportOptions.rows === GridExportOptions.SELECTED_ROWS) {
piotr.kupczyk@id.ethz.ch
committed
exportedRows = Object.values(state.multiselectedRows).map(
selectedRow => selectedRow.data
)
} else {
piotr.kupczyk@id.ethz.ch
committed
throw Error('Unsupported rows option: ' + exportOptions.columns)
piotr.kupczyk@id.ethz.ch
committed
}
piotr.kupczyk@id.ethz.ch
committed
piotr.kupczyk@id.ethz.ch
committed
if (exportedRows.some(row => _.isEmpty(row.exportableId))) {
throw Error(
"Some of the rows to be exported do not have 'exportableId' set."
)
}
piotr.kupczyk@id.ethz.ch
committed
piotr.kupczyk@id.ethz.ch
committed
return exportedRows
}
async function _getExportedFields(exportedRows) {
piotr.kupczyk@id.ethz.ch
committed
const { exportOptions } = state
let exportedFieldsMap = {}
piotr.kupczyk@id.ethz.ch
committed
piotr.kupczyk@id.ethz.ch
committed
if (exportOptions.columns === GridExportOptions.ALL_COLUMNS) {
exportedFieldsMap = {}
piotr.kupczyk@id.ethz.ch
committed
} else if (exportOptions.columns === GridExportOptions.VISIBLE_COLUMNS) {
const exportableFields = []
piotr.kupczyk@id.ethz.ch
committed
// find visible exportable columns for the exported rows
piotr.kupczyk@id.ethz.ch
committed
const { newAllColumns, newColumnsVisibility, newColumnsSorting } =
piotr.kupczyk@id.ethz.ch
committed
await _this._loadColumns(
piotr.kupczyk@id.ethz.ch
committed
exportedRows,
state.columnsVisibility,
state.columnsSorting
)
piotr.kupczyk@id.ethz.ch
committed
piotr.kupczyk@id.ethz.ch
committed
_this._sortColumns(newAllColumns, newColumnsSorting)
piotr.kupczyk@id.ethz.ch
committed
piotr.kupczyk@id.ethz.ch
committed
newAllColumns.forEach(column => {
if (column.exportableField && newColumnsVisibility[column.name]) {
exportableFields.push(column.exportableField)
piotr.kupczyk@id.ethz.ch
committed
}
})
piotr.kupczyk@id.ethz.ch
committed
// build exported fields map: { kind: { type: [{ type: "PROPERTY/ATTRIBUTE", id: "propertyCode/attributeCode"}, ...], ... }, ... }
piotr.kupczyk@id.ethz.ch
committed
exportedRows.forEach(exportedRow => {
const { exportable_kind, type_perm_id } = exportedRow.exportableId
piotr.kupczyk@id.ethz.ch
committed
piotr.kupczyk@id.ethz.ch
committed
if (exportable_kind && type_perm_id) {
let exportedFieldsForKind = exportedFieldsMap[exportable_kind]
piotr.kupczyk@id.ethz.ch
committed
if (!exportedFieldsForKind) {
exportedFieldsMap[exportable_kind] = exportedFieldsForKind = {}
piotr.kupczyk@id.ethz.ch
committed
}
piotr.kupczyk@id.ethz.ch
committed
exportedFieldsForKind[type_perm_id] = exportableFields
piotr.kupczyk@id.ethz.ch
committed
}
})
} else {
piotr.kupczyk@id.ethz.ch
committed
throw Error('Unsupported columns option: ' + exportOptions.columns)
piotr.kupczyk@id.ethz.ch
committed
}
return exportedFieldsMap
piotr.kupczyk@id.ethz.ch
committed
}
try {
this.context.setState({
exportState: {
loading: true
}
})
const exportedRows = await _getExportedRows()
const exportedFields = await _getExportedFields(exportedRows)
piotr.kupczyk@id.ethz.ch
committed
const exportedIds = exportedRows.map(row => row.exportableId)
const { sessionToken, exportResult } = await props.exportXLS({
exportedFilePrefix: exportable.filePrefix,
exportedFileContent: exportable.fileContent,
piotr.kupczyk@id.ethz.ch
committed
exportedIds: exportedIds,
exportedFields: exportedFields,
exportedValues: state.exportOptions.values,
exportedReferredMasterData:
piotr.kupczyk@id.ethz.ch
committed
exportable.fileContent === GridExportOptions.TYPES_CONTENT &&
state.exportOptions.includeDependencies
piotr.kupczyk@id.ethz.ch
committed
})
if (exportResult.status === 'OK') {
piotr.kupczyk@id.ethz.ch
committed
const filePath = exportResult.result.file_name
piotr.kupczyk@id.ethz.ch
committed
const fileName = filePath.substring(filePath.lastIndexOf('/') + 1)
const fileUrl =
piotr.kupczyk@id.ethz.ch
committed
'/openbis/openbis/download/?sessionID=' +
piotr.kupczyk@id.ethz.ch
committed
encodeURIComponent(sessionToken) +
'&filePath=' +
encodeURIComponent(filePath)
piotr.kupczyk@id.ethz.ch
committed
if (!_.isEmpty(exportResult.result.warnings)) {
piotr.kupczyk@id.ethz.ch
committed
this.context.setState({
exportState: {
piotr.kupczyk@id.ethz.ch
committed
warnings: exportResult.result.warnings,
piotr.kupczyk@id.ethz.ch
committed
fileName,
fileUrl
}
piotr.kupczyk@id.ethz.ch
committed
})
piotr.kupczyk@id.ethz.ch
committed
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
} else {
this.context.setState({
exportState: null
})
this.handleExportDownload(fileName, fileUrl)
}
} else if (exportResult.status === 'error') {
this.context.setState({
exportState: {
error: exportResult.message
}
})
} else {
this.context.setState({
exportState: {
error: JSON.stringify(exportResult)
}
})
}
} catch (e) {
this.context.setState({
exportState: {
piotr.kupczyk@id.ethz.ch
committed
error: e
piotr.kupczyk@id.ethz.ch
committed
}
})
}
piotr.kupczyk@id.ethz.ch
committed
}
piotr.kupczyk@id.ethz.ch
committed
piotr.kupczyk@id.ethz.ch
committed
handleExportDownload(fileName, fileUrl) {
this.context.setState({
exportState: null
})
const link = document.createElement('a')
link.href = fileUrl
link.download = fileName
link.click()
}
piotr.kupczyk@id.ethz.ch
committed
piotr.kupczyk@id.ethz.ch
committed
handleExportCancel() {
this.context.setState({
exportState: null
piotr.kupczyk@id.ethz.ch
committed
})
piotr.kupczyk@id.ethz.ch
committed
}
piotr.kupczyk@id.ethz.ch
committed
async handleExportOptionsChange(exportOptions) {
await this.context.setState(() => ({
exportOptions
}))
await this._saveSettings()
}
piotr.kupczyk@id.ethz.ch
committed
async handleMeasured(cellRef, column, row) {
piotr.kupczyk@id.ethz.ch
committed
if (!this.measureQueue) {
this.measureQueue = []
piotr.kupczyk@id.ethz.ch
committed
}
piotr.kupczyk@id.ethz.ch
committed
this.measureQueue.push({
cellRef,
column,
row
})
piotr.kupczyk@id.ethz.ch
committed
piotr.kupczyk@id.ethz.ch
committed
if (this.measureTimeoutId) {
clearTimeout(this.measureTimeoutId)
piotr.kupczyk@id.ethz.ch
committed
}
piotr.kupczyk@id.ethz.ch
committed
this.measureTimeoutId = setTimeout(() => {
piotr.kupczyk@id.ethz.ch
committed
this.context.setState(state => {
piotr.kupczyk@id.ethz.ch
committed
const heights = state.heights
let newHeights = heights
piotr.kupczyk@id.ethz.ch
committed
piotr.kupczyk@id.ethz.ch
committed
this.measureQueue.forEach(measureItem => {
const rowHeights = heights[measureItem.row.id]
let newRowHeights = newHeights[measureItem.row.id] || rowHeights
piotr.kupczyk@id.ethz.ch
committed
piotr.kupczyk@id.ethz.ch
committed
if (measureItem.cellRef.current) {
const height = rowHeights
? rowHeights[measureItem.column.name]
: null
const newHeight = measureItem.cellRef.current.scrollHeight
piotr.kupczyk@id.ethz.ch
committed
piotr.kupczyk@id.ethz.ch
committed
if (newHeight !== height) {
if (newHeights === heights) {
newHeights = {
...heights
piotr.kupczyk@id.ethz.ch
committed
}
piotr.kupczyk@id.ethz.ch
committed
}
if (newRowHeights === rowHeights) {
newRowHeights = {
...rowHeights
piotr.kupczyk@id.ethz.ch
committed
}
piotr.kupczyk@id.ethz.ch
committed
newHeights[measureItem.row.id] = newRowHeights
piotr.kupczyk@id.ethz.ch
committed
}
piotr.kupczyk@id.ethz.ch
committed
newRowHeights[measureItem.column.name] = newHeight
piotr.kupczyk@id.ethz.ch
committed
}
piotr.kupczyk@id.ethz.ch
committed
}
piotr.kupczyk@id.ethz.ch
committed
})
return {
heights: newHeights
piotr.kupczyk@id.ethz.ch
committed
}
piotr.kupczyk@id.ethz.ch
committed
})
piotr.kupczyk@id.ethz.ch
committed
this.measureQueue = []
piotr.kupczyk@id.ethz.ch
committed
}, 500)
piotr.kupczyk@id.ethz.ch
committed
}
getAllColumns() {
const { allColumns, columnsSorting } = this.context.getState()
let columns = [...allColumns]
this._sortColumns(columns, columnsSorting)
piotr.kupczyk@id.ethz.ch
committed
return this._getCachedValue('allColumns', columns)
}
getVisibleColumns() {
const { allColumns, columnsSorting, columnsVisibility } =
this.context.getState()
let columns = [...allColumns]
columns = columns.filter(column => columnsVisibility[column.name])
this._sortColumns(columns, columnsSorting)
piotr.kupczyk@id.ethz.ch
committed
return this._getCachedValue('visibleColumns', columns)
}
piotr.kupczyk@id.ethz.ch
committed
getPage() {
const { page } = this.context.getState()
return page
}
getPageSize() {
const { pageSize } = this.context.getState()
return pageSize
}
piotr.kupczyk@id.ethz.ch
committed
getSortings() {
const { sortings } = this.context.getState()
return sortings
piotr.kupczyk@id.ethz.ch
committed
}
getFilters() {
const { filters } = this.context.getState()
return filters
}
piotr.kupczyk@id.ethz.ch
committed
getGlobalFilter() {
const { globalFilter } = this.context.getState()
return globalFilter
}
getRows() {
const { rows } = this.context.getState()
return rows
piotr.kupczyk@id.ethz.ch
committed
}
piotr.kupczyk@id.ethz.ch
committed
getSelectedRow() {
const { selectedRow } = this.context.getState()
return selectedRow
piotr.kupczyk@id.ethz.ch
committed
}
piotr.kupczyk@id.ethz.ch
committed
piotr.kupczyk@id.ethz.ch
committed
getMultiselectedRows() {
const { multiselectedRows } = this.context.getState()
return multiselectedRows
}
piotr.kupczyk@id.ethz.ch
committed
getTotalCount() {
const { totalCount } = this.context.getState()
return totalCount
}
piotr.kupczyk@id.ethz.ch
committed
piotr.kupczyk@id.ethz.ch
committed
getExportable() {
const { exportable } = this.context.getProps()
piotr.kupczyk@id.ethz.ch
committed
if (exportable !== undefined) {
return exportable
} else {
return null
}
piotr.kupczyk@id.ethz.ch
committed
}
piotr.kupczyk@id.ethz.ch
committed
_getCachedValue(key, newValue) {
if (_.isEqual(this.cache[key], newValue)) {
return this.cache[key]
} else {
this.cache[key] = newValue
return newValue
}
}
piotr.kupczyk@id.ethz.ch
committed
_getObjectValue(value) {
return _.isObject(value) ? value : undefined
}
_getArrayValue(value) {
return _.isArray(value) ? value : undefined
}
_getStringValue(value) {
return _.isString(value) ? value : undefined
}
_getEnumValue(value, allowedValues) {
return _.includes(allowedValues, value) ? value : undefined
}
piotr.kupczyk@id.ethz.ch
committed
_isEmpty(value) {
return (
value === null ||
value === undefined ||
(_.isString(value) && value.trim().length === 0)
)
piotr.kupczyk@id.ethz.ch
committed
}
_split(str) {
return str.split(' ').filter(token => !this._isEmpty(token))
}
_onError(error) {
const { onError } = this.context.getProps()
if (onError) {
onError(error)
}
throw error
}