Newer
Older
piotr.kupczyk@id.ethz.ch
committed
import _ from 'lodash'
import autoBind from 'auto-bind'
piotr.kupczyk@id.ethz.ch
committed
import FileSaver from 'file-saver'
import { stringify } from 'csv-stringify'
piotr.kupczyk@id.ethz.ch
committed
import GridFilterOptions from '@src/js/components/common/grid/GridFilterOptions.js'
piotr.kupczyk@id.ethz.ch
committed
import GridExportOptions from '@src/js/components/common/grid/GridExportOptions.js'
piotr.kupczyk@id.ethz.ch
committed
import GridPagingOptions from '@src/js/components/common/grid/GridPagingOptions.js'
import GridSortingOptions from '@src/js/components/common/grid/GridSortingOptions.js'
import compare from '@src/js/common/compare.js'
piotr.kupczyk@id.ethz.ch
committed
piotr.kupczyk@id.ethz.ch
committed
const LOCAL_GRID_RELOAD_PERIOD = 200
const REMOTE_GRID_RELOAD_PERIOD = 500
piotr.kupczyk@id.ethz.ch
committed
piotr.kupczyk@id.ethz.ch
committed
export default class GridController {
constructor() {
autoBind(this)
piotr.kupczyk@id.ethz.ch
committed
this.cache = {}
piotr.kupczyk@id.ethz.ch
committed
}
init(context) {
piotr.kupczyk@id.ethz.ch
committed
const props = context.getProps()
piotr.kupczyk@id.ethz.ch
committed
let filterMode = GridFilterOptions.GLOBAL_FILTER
piotr.kupczyk@id.ethz.ch
committed
if (props.filterModes) {
filterMode = this._getEnumValue(filterMode, props.filterModes)
if (!filterMode) {
filterMode = props.filterModes.length > 0 ? props.filterModes[0] : null
}
}
piotr.kupczyk@id.ethz.ch
committed
let sortings = []
piotr.kupczyk@id.ethz.ch
committed
if (props.sortings) {
props.sortings.forEach(sorting => {
sortings.push({
columnName: sorting.columnName,
sortDirection: sorting.sortDirection
? sorting.sortDirection
: GridSortingOptions.ASC
})
})
} else if (props.sort) {
piotr.kupczyk@id.ethz.ch
committed
sortings.push({
columnName: props.sort,
sortDirection: props.sortDirection
? props.sortDirection
: GridSortingOptions.ASC
})
}
context.initState({
loaded: false,
loading: false,
piotr.kupczyk@id.ethz.ch
committed
filterMode: filterMode,
filters: {},
piotr.kupczyk@id.ethz.ch
committed
globalFilter: {
operator: GridFilterOptions.OPERATOR_AND,
text: null
},
page: 0,
pageSize: 10,
columnsVisibility: {},
columnsSorting: [],
allColumns: [],
piotr.kupczyk@id.ethz.ch
committed
local: null,
rows: [],
filteredRows: [],
sortedRows: [],
allRows: [],
piotr.kupczyk@id.ethz.ch
committed
selectedRow: null,
piotr.kupczyk@id.ethz.ch
committed
multiselectedRows: {},
piotr.kupczyk@id.ethz.ch
committed
heights: {},
piotr.kupczyk@id.ethz.ch
committed
sortings: sortings,
piotr.kupczyk@id.ethz.ch
committed
totalCount: 0,
piotr.kupczyk@id.ethz.ch
committed
exportState: {
loading: false,
piotr.kupczyk@id.ethz.ch
committed
warnings: null,
piotr.kupczyk@id.ethz.ch
committed
error: null,
fileName: null,
filePath: null
},
piotr.kupczyk@id.ethz.ch
committed
exportOptions: {
piotr.kupczyk@id.ethz.ch
committed
columns: GridExportOptions.VISIBLE_COLUMNS,
rows: GridExportOptions.CURRENT_PAGE,
values: GridExportOptions.RICH_TEXT,
includeDependencies: true
piotr.kupczyk@id.ethz.ch
committed
}
})
this.context = context
}
piotr.kupczyk@id.ethz.ch
committed
async load() {
try {
const props = this.context.getProps()
if ((props.rows && props.loadRows) || (!props.rows && !props.loadRows)) {
throw new Error(
'Incorrect grid configuration. Please set "rows" or "loadRows" property.'
)
}
if (
(props.columns && props.loadColumns) ||
(!props.columns && !props.loadColumns)
) {
throw new Error(
'Incorrect grid configuration. Please set "columns" or "loadColumns" property.'
)
}
await this.context.setState(() => ({
loading: true
}))
const state = this.context.getState()
const newState = {
...state,
heights: {},
loading: false,
loaded: true
}
let settings = null
piotr.kupczyk@id.ethz.ch
committed
if (!state.loaded) {
settings = await this._loadSettings()
_.merge(newState, settings)
}
let result = {}
piotr.kupczyk@id.ethz.ch
committed
if (props.rows) {
result.rows = props.rows
result.totalCount = props.rows.length
result.local = true
} else if (props.loadRows) {
const columns = {}
piotr.kupczyk@id.ethz.ch
committed
newState.allColumns.forEach(column => {
columns[column.name] = column
})
piotr.kupczyk@id.ethz.ch
committed
const loadedResult = await props.loadRows({
columns: columns,
filterMode: newState.filterMode,
filters: newState.filters,
globalFilter: newState.globalFilter,
page: newState.page,
pageSize: newState.pageSize,
sortings: newState.sortings
})
if (_.isArray(loadedResult)) {
result.rows = loadedResult
result.totalCount = loadedResult.length
result.local = true
} else {
result.rows = loadedResult.rows
result.totalCount = loadedResult.totalCount
result.local = false
}
piotr.kupczyk@id.ethz.ch
committed
}
newState.local = result.local
piotr.kupczyk@id.ethz.ch
committed
if (result.local) {
const { newAllColumns, newColumnsVisibility, newColumnsSorting } =
await this._loadColumns(
result.rows,
newState.columnsVisibility,
newState.columnsSorting
)
newState.allColumns = newAllColumns
newState.columnsVisibility = newColumnsVisibility
newState.columnsSorting = newColumnsSorting
newState.allRows = result.rows
newState.filteredRows = this._filterRows(
newState.allRows,
newState.allColumns,
newState.columnsVisibility,
newState.filterMode,
newState.filters,
newState.globalFilter
)
newState.sortedRows = this._sortRows(
newState.filteredRows,
newState.allColumns,
newState.sortings
)
newState.totalCount = newState.filteredRows.length
const pageCount = Math.max(
Math.ceil(newState.totalCount / newState.pageSize),
1
)
newState.page = Math.min(newState.page, pageCount - 1)
newState.rows = this._pageRows(
newState.sortedRows,
newState.page,
newState.pageSize
)
} else {
newState.allRows = result.rows
newState.filteredRows = result.rows
newState.sortedRows = result.rows
newState.rows = result.rows
newState.totalCount = result.totalCount
const pageCount = Math.max(
Math.ceil(result.totalCount / newState.pageSize),
1
newState.page = Math.min(newState.page, pageCount - 1)
const { newAllColumns, newColumnsVisibility, newColumnsSorting } =
await this._loadColumns(
newState.rows,
newState.columnsVisibility,
newState.columnsSorting
)
newState.allColumns = newAllColumns
newState.columnsVisibility = newColumnsVisibility
newState.columnsSorting = newColumnsSorting
}
piotr.kupczyk@id.ethz.ch
committed
// do not update filters (this would override filter changes that a user could do while grid was loading)
delete newState.filters
delete newState.globalFilter
await this.context.setState(newState)
if (!state.loaded) {
this.selectRow(props.selectedRowId)
this.multiselectRows(props.multiselectedRowIds)
} else {
this.selectRow(newState.selectedRow ? newState.selectedRow.id : null)
this.multiselectRows(Object.keys(newState.multiselectedRows))
}
} catch (error) {
this._onError(error)
}
}
async _loadColumns(rows, columnsVisibility, columnsSorting) {
piotr.kupczyk@id.ethz.ch
committed
const props = this.context.getProps()
const state = this.context.getState()
let newAllColumns = []
const newColumnsVisibility = { ...columnsVisibility }
const newColumnsSorting = [...columnsSorting]
piotr.kupczyk@id.ethz.ch
committed
if (props.columns) {
newAllColumns = props.columns
} else if (props.loadColumns) {
newAllColumns = await props.loadColumns(rows)
}
piotr.kupczyk@id.ethz.ch
committed
newAllColumns = newAllColumns.map(newColumn => {
if (!newColumn.name) {
piotr.kupczyk@id.ethz.ch
committed
throw new Error('column.name cannot be empty')
}
piotr.kupczyk@id.ethz.ch
committed
if (newColumn.exportable && !newColumn.getValue) {
throw new Error(
'column.name cannot be exportable without getValue implementation'
)
}
return this._loadColumn(newColumn)
})
piotr.kupczyk@id.ethz.ch
committed
piotr.kupczyk@id.ethz.ch
committed
// If there is a filter value defined for a column and this column does not exist
// in the new columns list then take it over from the previous columns list.
// This may happen e.g. when a user is filtering by a dynamic column
// and enters a filter value that does not match any row. Without this trick the column
// would disappear and the user would not be able to clear the filter value.
Object.keys(state.filters).forEach(columnName => {
const newColumn = _.find(
newAllColumns,
newColumn => newColumn.name === columnName
)
if (!newColumn) {
const existingColumn = _.find(
state.allColumns,
column => column.name === columnName
)
newAllColumns.push(existingColumn)
}
})
newAllColumns.forEach((newColumn, newColumnIndex) => {
let newColumnVisibility = newColumnsVisibility[newColumn.name]
if (newColumnVisibility === undefined || !newColumn.configurable) {
newColumnsVisibility[newColumn.name] = newColumn.visible
}
let newColumnSorting = _.findIndex(
newColumnsSorting,
columnName => columnName === newColumn.name
)
piotr.kupczyk@id.ethz.ch
committed
if (newColumnSorting === -1) {
piotr.kupczyk@id.ethz.ch
committed
// If a column does not have a sorting value yet, then set its sorting to
// the max sorting of the columns that were before it in the columns list
newColumnSorting = newAllColumns
.slice(0, newColumnIndex)
.reduce((maxSorting, column) => {
const sorting = _.findIndex(
newColumnsSorting,
columnName => columnName === column.name
)
return Math.max(sorting, maxSorting)
}, -1)
newColumnsSorting.splice(newColumnSorting + 1, 0, newColumn.name)
}
})
piotr.kupczyk@id.ethz.ch
committed
return { newAllColumns, newColumnsVisibility, newColumnsSorting }
piotr.kupczyk@id.ethz.ch
committed
}
_loadColumn(column) {
piotr.kupczyk@id.ethz.ch
committed
const defaultMatches = function (value, filter) {
if (filter) {
return value !== null && value !== undefined
? String(value)
.trim()
.toUpperCase()
.includes(filter.trim().toUpperCase())
: false
} else {
return true
}
}
const defaultCompare = compare
return {
...column,
name: column.name,
label: column.label,
getValue: column.getValue,
piotr.kupczyk@id.ethz.ch
committed
matches: (row, filter) => {
piotr.kupczyk@id.ethz.ch
committed
const value = column.getValue({ row, column, operation: 'match' })
piotr.kupczyk@id.ethz.ch
committed
if (column.matchesValue) {
return column.matchesValue({
piotr.kupczyk@id.ethz.ch
committed
value,
row,
column,
filter,
defaultMatches
})
} else {
return defaultMatches(value, filter)
}
},
piotr.kupczyk@id.ethz.ch
committed
compare: (row1, row2, sortDirection) => {
piotr.kupczyk@id.ethz.ch
committed
const value1 = column.getValue({
row: row1,
column,
operation: 'compare'
})
const value2 = column.getValue({
row: row2,
column,
operation: 'compare'
})
piotr.kupczyk@id.ethz.ch
committed
if (column.compareValue) {
return column.compareValue({
piotr.kupczyk@id.ethz.ch
committed
value1,
value2,
row1,
row2,
column,
piotr.kupczyk@id.ethz.ch
committed
sortDirection,
piotr.kupczyk@id.ethz.ch
committed
defaultCompare
})
} else {
return defaultCompare(value1, value2)
}
},
sortable: column.sortable === undefined ? true : column.sortable,
filterable: column.filterable === undefined ? true : column.filterable,
visible: column.visible === undefined ? true : column.visible,
configurable:
piotr.kupczyk@id.ethz.ch
committed
column.configurable === undefined ? true : column.configurable,
piotr.kupczyk@id.ethz.ch
committed
exportable: column.exportable === undefined ? true : column.exportable,
piotr.kupczyk@id.ethz.ch
committed
nowrap: column.nowrap === undefined ? false : column.nowrap,
truncate: column.truncate === undefined ? false : column.truncate,
piotr.kupczyk@id.ethz.ch
committed
metadata: column.metadata === undefined ? {} : column.metadata
}
piotr.kupczyk@id.ethz.ch
committed
}
_sortColumns(columns, columnsSorting) {
columns.sort((c1, c2) => {
const c1Index = _.findIndex(
columnsSorting,
columnName => columnName === c1.name
)
const c2Index = _.findIndex(
columnsSorting,
columnName => columnName === c2.name
)
return c1Index - c2Index
})
piotr.kupczyk@id.ethz.ch
committed
}
async _loadSettings() {
piotr.kupczyk@id.ethz.ch
committed
const props = this.context.getProps()
piotr.kupczyk@id.ethz.ch
committed
piotr.kupczyk@id.ethz.ch
committed
if (!props.loadSettings) {
return {}
piotr.kupczyk@id.ethz.ch
committed
}
piotr.kupczyk@id.ethz.ch
committed
const loaded = await props.loadSettings()
piotr.kupczyk@id.ethz.ch
committed
piotr.kupczyk@id.ethz.ch
committed
if (!loaded || !_.isObject(loaded)) {
return {}
piotr.kupczyk@id.ethz.ch
committed
}
piotr.kupczyk@id.ethz.ch
committed
const settings = {}
piotr.kupczyk@id.ethz.ch
committed
piotr.kupczyk@id.ethz.ch
committed
settings.filterMode = this._getEnumValue(
loaded.filterMode,
GridFilterOptions.FILTER_MODE_OPTIONS
)
piotr.kupczyk@id.ethz.ch
committed
piotr.kupczyk@id.ethz.ch
committed
if (props.filterModes) {
settings.filterMode = this._getEnumValue(
settings.filterMode,
props.filterModes
)
}
piotr.kupczyk@id.ethz.ch
committed
piotr.kupczyk@id.ethz.ch
committed
if (_.isObject(loaded.globalFilter)) {
piotr.kupczyk@id.ethz.ch
committed
const globalFilter = {}
piotr.kupczyk@id.ethz.ch
committed
piotr.kupczyk@id.ethz.ch
committed
globalFilter.operator = this._getEnumValue(
loaded.globalFilter.operator,
GridFilterOptions.OPERATOR_OPTIONS
piotr.kupczyk@id.ethz.ch
committed
)
piotr.kupczyk@id.ethz.ch
committed
piotr.kupczyk@id.ethz.ch
committed
if (globalFilter.operator !== undefined) {
settings.globalFilter = globalFilter
}
piotr.kupczyk@id.ethz.ch
committed
}
piotr.kupczyk@id.ethz.ch
committed
piotr.kupczyk@id.ethz.ch
committed
settings.pageSize = this._getEnumValue(
loaded.pageSize,
GridPagingOptions.PAGE_SIZE_OPTIONS
)
piotr.kupczyk@id.ethz.ch
committed
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
if (_.isArray(loaded.sortings)) {
const sortings = []
loaded.sortings.forEach(loadedSorting => {
if (_.isObject(loadedSorting)) {
const sorting = {}
sorting.columnName = this._getStringValue(loadedSorting.columnName)
sorting.sortDirection = this._getEnumValue(
loadedSorting.sortDirection,
GridSortingOptions.SORTING_DIRECTION_OPTIONS
)
if (
sorting.columnName !== undefined &&
sorting.sortDirection !== undefined
) {
sortings.push(sorting)
}
}
})
if (sortings.length > 0) {
settings.sortings = sortings
}
}
if (settings.sortings === undefined) {
const sort = this._getStringValue(loaded.sort)
const sortDirection = this._getEnumValue(
loaded.sortDirection,
GridSortingOptions.SORTING_DIRECTION_OPTIONS
)
if (sort !== undefined && sortDirection !== undefined) {
settings.sortings = [
{
columnName: sort,
sortDirection: sortDirection
}
]
}
}
piotr.kupczyk@id.ethz.ch
committed
settings.columnsVisibility = this._getObjectValue(loaded.columnsVisibility)
settings.columnsSorting = this._getArrayValue(loaded.columnsSorting)
piotr.kupczyk@id.ethz.ch
committed
piotr.kupczyk@id.ethz.ch
committed
if (_.isObject(loaded.exportOptions)) {
piotr.kupczyk@id.ethz.ch
committed
const exportOptions = {}
piotr.kupczyk@id.ethz.ch
committed
piotr.kupczyk@id.ethz.ch
committed
exportOptions.columns = this._getEnumValue(
loaded.exportOptions.columns,
GridExportOptions.COLUMNS_OPTIONS
piotr.kupczyk@id.ethz.ch
committed
)
piotr.kupczyk@id.ethz.ch
committed
exportOptions.rows = this._getEnumValue(
loaded.exportOptions.rows,
GridExportOptions.ROWS_OPTIONS
)
exportOptions.values = this._getEnumValue(
loaded.exportOptions.values,
GridExportOptions.VALUES_OPTIONS
piotr.kupczyk@id.ethz.ch
committed
)
piotr.kupczyk@id.ethz.ch
committed
if (
exportOptions.columns !== undefined &&
exportOptions.rows !== undefined &&
exportOptions.values !== undefined
) {
settings.exportOptions = exportOptions
}
piotr.kupczyk@id.ethz.ch
committed
}
piotr.kupczyk@id.ethz.ch
committed
return settings
piotr.kupczyk@id.ethz.ch
committed
}
async _saveSettings() {
piotr.kupczyk@id.ethz.ch
committed
const { onSettingsChange } = this.context.getProps()
piotr.kupczyk@id.ethz.ch
committed
piotr.kupczyk@id.ethz.ch
committed
if (onSettingsChange) {
const state = this.context.getState()
piotr.kupczyk@id.ethz.ch
committed
piotr.kupczyk@id.ethz.ch
committed
let settings = {
piotr.kupczyk@id.ethz.ch
committed
filterMode: state.filterMode,
piotr.kupczyk@id.ethz.ch
committed
globalFilter: {
operator: state.globalFilter.operator
},
piotr.kupczyk@id.ethz.ch
committed
pageSize: state.pageSize,
piotr.kupczyk@id.ethz.ch
committed
sortings: state.sortings,
piotr.kupczyk@id.ethz.ch
committed
columnsVisibility: state.columnsVisibility,
piotr.kupczyk@id.ethz.ch
committed
columnsSorting: state.columnsSorting,
exportOptions: state.exportOptions
piotr.kupczyk@id.ethz.ch
committed
}
piotr.kupczyk@id.ethz.ch
committed
piotr.kupczyk@id.ethz.ch
committed
onSettingsChange(settings)
}
}
piotr.kupczyk@id.ethz.ch
committed
_filterRows(
rows,
columns,
columnsVisibility,
filterMode,
filters,
globalFilter
) {
if (filterMode === GridFilterOptions.GLOBAL_FILTER) {
piotr.kupczyk@id.ethz.ch
committed
if (this._isEmpty(globalFilter.text)) {
piotr.kupczyk@id.ethz.ch
committed
return rows
}
piotr.kupczyk@id.ethz.ch
committed
const tokens = this._split(globalFilter.text)
piotr.kupczyk@id.ethz.ch
committed
piotr.kupczyk@id.ethz.ch
committed
return _.filter([...rows], row => {
piotr.kupczyk@id.ethz.ch
committed
let rowMatches = null
piotr.kupczyk@id.ethz.ch
committed
piotr.kupczyk@id.ethz.ch
committed
if (globalFilter.operator === GridFilterOptions.OPERATOR_AND) {
rowMatches = true
} else if (globalFilter.operator === GridFilterOptions.OPERATOR_OR) {
rowMatches = false
}
tokens: for (let t = 0; t < tokens.length; t++) {
let token = tokens[t]
piotr.kupczyk@id.ethz.ch
committed
let rowMatchesToken = false
piotr.kupczyk@id.ethz.ch
committed
columns: for (let c = 0; c < columns.length; c++) {
let column = columns[c]
piotr.kupczyk@id.ethz.ch
committed
let visible = columnsVisibility[column.name]
piotr.kupczyk@id.ethz.ch
committed
piotr.kupczyk@id.ethz.ch
committed
if (visible) {
piotr.kupczyk@id.ethz.ch
committed
rowMatchesToken = column.matches(row, token)
if (rowMatchesToken) {
break columns
}
piotr.kupczyk@id.ethz.ch
committed
}
piotr.kupczyk@id.ethz.ch
committed
}
piotr.kupczyk@id.ethz.ch
committed
piotr.kupczyk@id.ethz.ch
committed
if (globalFilter.operator === GridFilterOptions.OPERATOR_AND) {
rowMatches = rowMatches && rowMatchesToken
if (!rowMatches) {
break tokens
}
} else if (globalFilter.operator === GridFilterOptions.OPERATOR_OR) {
rowMatches = rowMatches || rowMatchesToken
if (rowMatches) {
break tokens
}
}
piotr.kupczyk@id.ethz.ch
committed
}
piotr.kupczyk@id.ethz.ch
committed
return rowMatches
piotr.kupczyk@id.ethz.ch
committed
} else if (filterMode === GridFilterOptions.COLUMN_FILTERS) {
return _.filter([...rows], row => {
let matchesAll = true
columns.forEach(column => {
let visible = columnsVisibility[column.name]
if (visible) {
let filter = filters[column.name]
piotr.kupczyk@id.ethz.ch
committed
if (!this._isEmpty(filter)) {
piotr.kupczyk@id.ethz.ch
committed
matchesAll = matchesAll && column.matches(row, filter)
}
}
})
return matchesAll
})
piotr.kupczyk@id.ethz.ch
committed
} else {
return rows
piotr.kupczyk@id.ethz.ch
committed
}
}
piotr.kupczyk@id.ethz.ch
committed
_sortRows(rows, columns, sortings) {
if (sortings && sortings.length > 0) {
const columnSortings = []
sortings.forEach(sorting => {
const column = _.find(columns, ['name', sorting.columnName])
if (column) {
columnSortings.push({
column,
sorting
})
}
})
if (columnSortings.length > 0) {
return rows.sort((t1, t2) => {
piotr.kupczyk@id.ethz.ch
committed
let result = 0
let index = 0
while (index < columnSortings.length && result === 0) {
const { column, sorting } = columnSortings[index]
const sign =
sorting.sortDirection === GridSortingOptions.ASC ? 1 : -1
result = sign * column.compare(t1, t2, sorting.sortDirection)
index++
}
return result
})
}
}
return rows
}
_pageRows(rows, page, pageSize) {
return rows.slice(
page * pageSize,
Math.min(rows.length, (page + 1) * pageSize)
)
piotr.kupczyk@id.ethz.ch
committed
}
piotr.kupczyk@id.ethz.ch
committed
async selectRow(newSelectedRowId) {
piotr.kupczyk@id.ethz.ch
committed
const { selectable, onSelectedRowChange } = this.context.getProps()
piotr.kupczyk@id.ethz.ch
committed
const { allRows, rows, selectedRow } = this.context.getState()
piotr.kupczyk@id.ethz.ch
committed
if (!selectable) {
return
}
let newSelectedRow = null
if (newSelectedRowId !== null && newSelectedRowId !== undefined) {
piotr.kupczyk@id.ethz.ch
committed
const data = _.find(allRows, row => row.id === newSelectedRowId)
piotr.kupczyk@id.ethz.ch
committed
const visible =
_.findIndex(rows, row => row.id === newSelectedRowId) !== -1
piotr.kupczyk@id.ethz.ch
committed
piotr.kupczyk@id.ethz.ch
committed
newSelectedRow = {
id: newSelectedRowId,
piotr.kupczyk@id.ethz.ch
committed
data,
piotr.kupczyk@id.ethz.ch
committed
visible
piotr.kupczyk@id.ethz.ch
committed
if (!_.isEqual(selectedRow, newSelectedRow)) {
await this.context.setState(() => ({
selectedRow: newSelectedRow
}))
piotr.kupczyk@id.ethz.ch
committed
if (onSelectedRowChange) {
onSelectedRowChange(newSelectedRow)
piotr.kupczyk@id.ethz.ch
committed
}
}
piotr.kupczyk@id.ethz.ch
committed
async multiselectRows(newMultiselectedRowIds) {
piotr.kupczyk@id.ethz.ch
committed
const { multiselectable, onMultiselectedRowsChange } =
this.context.getProps()
piotr.kupczyk@id.ethz.ch
committed
const { local, allRows, rows, multiselectedRows } = this.context.getState()
piotr.kupczyk@id.ethz.ch
committed
if (!multiselectable) {
return
}
piotr.kupczyk@id.ethz.ch
committed
const newMultiselectedRows = {}
piotr.kupczyk@id.ethz.ch
committed
if (newMultiselectedRowIds && newMultiselectedRowIds.length > 0) {
const allRowsMap = {}
allRows.forEach(row => {
allRowsMap[row.id] = row
})
const rowsMap = {}
rows.forEach(row => {
rowsMap[row.id] = row
})
newMultiselectedRowIds.forEach(rowId => {
if (rowId !== null && rowId !== undefined) {
piotr.kupczyk@id.ethz.ch
committed
const visible = rowsMap[rowId] !== undefined
piotr.kupczyk@id.ethz.ch
committed
let data = allRowsMap[rowId]
piotr.kupczyk@id.ethz.ch
committed
if (data) {
newMultiselectedRows[rowId] = {
id: rowId,
data,
visible
}
} else if (!local) {
piotr.kupczyk@id.ethz.ch
committed
const multiselectedRow = multiselectedRows[rowId]
if (multiselectedRow) {
data = multiselectedRow.data
}
piotr.kupczyk@id.ethz.ch
committed
newMultiselectedRows[rowId] = {
id: rowId,
data,
visible
}
piotr.kupczyk@id.ethz.ch
committed
}
piotr.kupczyk@id.ethz.ch
committed
}
})
}
await this.context.setState(() => ({
multiselectedRows: newMultiselectedRows
}))
if (onMultiselectedRowsChange) {
onMultiselectedRowsChange(newMultiselectedRows)
}
}
piotr.kupczyk@id.ethz.ch
committed
async showRow(rowId) {
const { sortedRows, page, pageSize } = this.context.getState()
piotr.kupczyk@id.ethz.ch
committed
piotr.kupczyk@id.ethz.ch
committed
if (!rowId) {
return
}
piotr.kupczyk@id.ethz.ch
committed
piotr.kupczyk@id.ethz.ch
committed
const index = _.findIndex(sortedRows, ['id', rowId])
piotr.kupczyk@id.ethz.ch
committed
piotr.kupczyk@id.ethz.ch
committed
if (index === -1) {
return
}
piotr.kupczyk@id.ethz.ch
committed
piotr.kupczyk@id.ethz.ch
committed
const newPage = Math.floor(index / pageSize)
piotr.kupczyk@id.ethz.ch
committed
piotr.kupczyk@id.ethz.ch
committed
if (newPage !== page) {
await this.context.setState({
page: newPage
})
await this.load()
}
piotr.kupczyk@id.ethz.ch
committed
}
piotr.kupczyk@id.ethz.ch
committed
async handleFilterModeChange(filterMode) {
await this.context.setState({
filterMode
})
await this.load()
await this._saveSettings()
}
piotr.kupczyk@id.ethz.ch
committed
async handleFilterChange(column, filter) {
piotr.kupczyk@id.ethz.ch
committed
const { local } = this.context.getState()
await this.context.setState(state => {
const newFilters = {
...state.filters
}
piotr.kupczyk@id.ethz.ch
committed
if (filter && _.trim(filter).length > 0) {
newFilters[column] = filter
} else {
delete newFilters[column]
}
piotr.kupczyk@id.ethz.ch
committed
return {
page: 0,
filters: newFilters
piotr.kupczyk@id.ethz.ch
committed
}
})
if (this.loadTimerId) {
clearTimeout(this.loadTimerId)
this.loadTimerId = null
piotr.kupczyk@id.ethz.ch
committed
}
piotr.kupczyk@id.ethz.ch
committed
piotr.kupczyk@id.ethz.ch
committed
this.loadTimerId = setTimeout(
async () => {
piotr.kupczyk@id.ethz.ch
committed
await this.load()
piotr.kupczyk@id.ethz.ch
committed
},
local ? LOCAL_GRID_RELOAD_PERIOD : REMOTE_GRID_RELOAD_PERIOD
)
piotr.kupczyk@id.ethz.ch
committed
}
piotr.kupczyk@id.ethz.ch
committed
async handleGlobalFilterChange(newGlobalFilter) {
const { local, globalFilter } = this.context.getState()
piotr.kupczyk@id.ethz.ch
committed
await this.context.setState(() => ({
page: 0,
piotr.kupczyk@id.ethz.ch
committed
globalFilter: newGlobalFilter
piotr.kupczyk@id.ethz.ch
committed
}))
if (this.loadTimerId) {
clearTimeout(this.loadTimerId)
this.loadTimerId = null
}
piotr.kupczyk@id.ethz.ch
committed
this.loadTimerId = setTimeout(
async () => {
piotr.kupczyk@id.ethz.ch
committed
await this.load()
piotr.kupczyk@id.ethz.ch
committed
},
local ? LOCAL_GRID_RELOAD_PERIOD : REMOTE_GRID_RELOAD_PERIOD
)
piotr.kupczyk@id.ethz.ch
committed
if (globalFilter.operator !== newGlobalFilter.operator) {
await this._saveSettings()
}
piotr.kupczyk@id.ethz.ch
committed
}
piotr.kupczyk@id.ethz.ch
committed
async handleColumnVisibleChange(visibilityMap) {
const { allColumns } = this.context.getState()
piotr.kupczyk@id.ethz.ch
committed
allColumns.forEach(column => {
if (!column.configurable) {
delete visibilityMap[column.name]
}
})
await this.context.setState(state => {
piotr.kupczyk@id.ethz.ch
committed
const newColumnsVisibility = {
...state.columnsVisibility,
...visibilityMap
}
const newFilters = { ...state.filters }
piotr.kupczyk@id.ethz.ch
committed
piotr.kupczyk@id.ethz.ch
committed
Object.keys(visibilityMap).forEach(columnName => {
const visible = visibilityMap[columnName]
if (!visible) {
delete newFilters[columnName]
}
piotr.kupczyk@id.ethz.ch
committed
})
return {
columnsVisibility: newColumnsVisibility,
filters: newFilters
piotr.kupczyk@id.ethz.ch
committed
}
})
await this.load()
await this._saveSettings()
piotr.kupczyk@id.ethz.ch
committed
}
piotr.kupczyk@id.ethz.ch
committed
async handleColumnOrderChange(sourceIndex, destinationIndex) {
await this.context.setState(state => {
const columns = this.getAllColumns()
const sourceColumn = columns[sourceIndex]
const destinationColumn = columns[destinationIndex]
piotr.kupczyk@id.ethz.ch
committed
const sourceSorting = _.findIndex(
state.columnsSorting,
columnName => columnName === sourceColumn.name
)
const destinationSorting = _.findIndex(
state.columnsSorting,
columnName => columnName === destinationColumn.name
)
piotr.kupczyk@id.ethz.ch
committed
const newColumnsSorting = [...state.columnsSorting]
newColumnsSorting.splice(sourceSorting, 1)
newColumnsSorting.splice(destinationSorting, 0, sourceColumn.name)
piotr.kupczyk@id.ethz.ch
committed
return {
columnsSorting: newColumnsSorting
}
})
await this.load()
await this._saveSettings()
piotr.kupczyk@id.ethz.ch
committed
}
piotr.kupczyk@id.ethz.ch
committed
async handleSortChange(column, append) {
if (!column.sortable) {
piotr.kupczyk@id.ethz.ch
committed
return
}
piotr.kupczyk@id.ethz.ch
committed
function createInitialSorting(column) {
return {
columnName: column.name,
sortDirection: GridSortingOptions.ASC
}
}
function createReversedSorting(column, sorting) {
return {
columnName: column.name,
sortDirection:
sorting.sortDirection === GridSortingOptions.ASC
? GridSortingOptions.DESC
: GridSortingOptions.ASC
}
}
piotr.kupczyk@id.ethz.ch
committed
await this.context.setState(state => {
piotr.kupczyk@id.ethz.ch
committed
const newSortings = []
const index = _.findIndex(
state.sortings,
sorting => sorting.columnName === column.name
)
const sorting = state.sortings[index]
if (append) {
if (index !== -1) {
newSortings.push(...state.sortings)
newSortings.splice(index, 1)
} else {
newSortings.push(...state.sortings)
newSortings.push(createInitialSorting(column))
}
piotr.kupczyk@id.ethz.ch
committed
} else {
piotr.kupczyk@id.ethz.ch
committed
if (index !== -1) {
newSortings.push(...state.sortings)
newSortings[index] = createReversedSorting(column, sorting)
} else {
newSortings.push(createInitialSorting(column))
piotr.kupczyk@id.ethz.ch
committed
}
}
piotr.kupczyk@id.ethz.ch
committed
return {
page: 0,
sortings: newSortings
}
piotr.kupczyk@id.ethz.ch
committed
})
await this.load()
await this._saveSettings()
piotr.kupczyk@id.ethz.ch
committed
}
piotr.kupczyk@id.ethz.ch
committed
async handlePageChange(page) {
await this.context.setState(() => ({
page
}))
await this.load()
piotr.kupczyk@id.ethz.ch
committed
}
piotr.kupczyk@id.ethz.ch
committed
async handlePageSizeChange(pageSize) {
await this.context.setState(() => ({
page: 0,
pageSize
}))
await this.load()
await this._saveSettings()
piotr.kupczyk@id.ethz.ch
committed
}
piotr.kupczyk@id.ethz.ch
committed
async handleRowClick(row) {
const { onRowClick } = this.context.getProps()
if (onRowClick) {
onRowClick({
id: row.id,
data: row,
visible: true
})
}
}
async handleRowSelect(row) {
piotr.kupczyk@id.ethz.ch
committed
await this.selectRow(row ? row.id : null)
piotr.kupczyk@id.ethz.ch
committed
}
piotr.kupczyk@id.ethz.ch
committed
async handleRowMultiselect(row) {
const { multiselectedRows } = this.context.getState()
if (row) {
const newMultiselectedRows = { ...multiselectedRows }
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
}